Compare commits

..

55 Commits

Author SHA1 Message Date
Guido D'Orsi
7f07f7e049 Merge pull request #1294 from boorad/authv2-web-example-refactoring
feat: update web examples to new auth system
2025-01-29 10:31:25 +01:00
Guido D'Orsi
b22589bbad test: add integration tests for the websocket peer 2025-01-29 10:26:09 +01:00
Guido D'Orsi
be144adeee chore: rename onSuccessCalles to isFirstMessage and add a test 2025-01-29 10:17:59 +01:00
Brad Anderson
513619b04e feat: update web examples to new auth system 2025-01-28 23:13:44 -05:00
Guido D'Orsi
81f6a75aac chore: add comment 2025-01-28 20:11:52 +01:00
Guido D'Orsi
913d03de75 feat: update the RN example apps with the new Auth system 2025-01-28 19:34:56 +01:00
Guido D'Orsi
2d0172d839 feat: update the React Native packages with the new auth system 2025-01-28 19:34:04 +01:00
Guido D'Orsi
27d46641e3 feat: unify React Native and Jazz Tools DemoAuth users management 2025-01-28 19:32:55 +01:00
Guido D'Orsi
1014816d79 feat: move WebSocketPeerWithReconnection to jazz-tools 2025-01-28 19:09:34 +01:00
Guido D'Orsi
441194acb7 feat: add onSuccess hook and close the WebSocket on error 2025-01-28 19:06:35 +01:00
Guido D'Orsi
c9bea7200f feat: rename jazz-browser-clerk-auth in jazz-clerk-auth and update jazz-react-native-auth-clerk 2025-01-28 19:05:43 +01:00
Guido D'Orsi
6ada48d57a chore: remove unused imports 2025-01-28 18:05:55 +01:00
Guido D'Orsi
8fa978df21 fix: initialize the anonymous storage only if newAccountProps is not provided 2025-01-28 16:27:25 +01:00
Guido D'Orsi
df7356fd24 chore: cleanup and small fix on websocket enable/disable 2025-01-28 14:45:58 +01:00
Guido D'Orsi
153fa4ce44 fix: invalidate context when localOnly is updated 2025-01-28 12:19:46 +01:00
Guido D'Orsi
6105bd2c88 test: cover createContext and ContextManager 2025-01-28 12:09:48 +01:00
Guido D'Orsi
c06d63eec1 fix: align passphrase auth with the current auth flow 2025-01-28 10:54:47 +01:00
Guido D'Orsi
4ee7359370 test: PassphraseAuth 2025-01-27 22:25:22 +01:00
Guido D'Orsi
7b5c47b377 test: cover DemoAuth 2025-01-27 22:12:42 +01:00
Guido D'Orsi
7ad57e88f3 test: add tests on the react-core auth hooks 2025-01-27 21:59:06 +01:00
Guido D'Orsi
c181e55557 feat: make auth state platform agnostic 2025-01-27 19:27:21 +01:00
Guido D'Orsi
7e0cd721e6 Merge remote-tracking branch 'origin/main' into authv2 2025-01-27 15:51:33 +01:00
Guido D'Orsi
59ddff40ab Merge pull request #1270 from garden-co/authv2-clerk
feat: improve the clerk integration
2025-01-27 11:01:13 +01:00
Guido D'Orsi
cb75cc62b2 chore: exclude storage from the propsChanged check 2025-01-25 00:38:09 +01:00
Guido D'Orsi
b141f6a62f fix: double context creation on React 2025-01-25 00:36:58 +01:00
Guido D'Orsi
4a251a528c feat: improve the clerk integration 2025-01-24 19:37:59 +01:00
Guido D'Orsi
f8b6119996 feat: translate isAnonymous checks into isAuthenticated and add tests 2025-01-24 16:15:18 +01:00
Guido D'Orsi
a7c90f749e feat: move the context manager into jazz-tools 2025-01-24 15:30:43 +01:00
Guido D'Orsi
3a6e34589f feat: defaultName and better props tracking 2025-01-23 18:34:37 +01:00
Guido D'Orsi
a1d512d9d7 Merge remote-tracking branch 'origin/main' into authv2 2025-01-23 17:28:14 +01:00
Guido D'Orsi
51bf6b7fdc Merge pull request #1223 from garden-co/auth-flow-rewrite
Auth flow rewrite
2025-01-23 17:26:53 +01:00
Guido D'Orsi
b5826fc0a5 Merge pull request #1176 from garden-co/user-onboarding-upgrade
feat: zero-setup auth, anonymous user upgrade and localOnly sync
2025-01-23 17:22:50 +01:00
Guido D'Orsi
becdd2f451 test(music-player): fix e2e tests 2025-01-23 17:12:47 +01:00
Guido D'Orsi
3b20bf354f feat: migrate svelte 2025-01-23 16:58:44 +01:00
Guido D'Orsi
fff7fe934b feat: update Vue 2025-01-23 12:55:12 +01:00
Guido D'Orsi
4c8d128eee feat: auth flow rewrite 2025-01-22 18:04:45 +01:00
Guido D'Orsi
df106ca680 test: fix create image test 2025-01-21 11:15:18 +01:00
Guido D'Orsi
c58f93b597 Merge remote-tracking branch 'origin/main' into user-onboarding-upgrade 2025-01-21 10:49:01 +01:00
Guido D'Orsi
3f2a0ead1b fix: self-review fixes 2025-01-21 10:34:42 +01:00
Guido D'Orsi
c9e6d2998e feat: improve the jazz-react provider 2025-01-20 20:10:18 +01:00
Guido D'Orsi
40634c6ec1 chore: update lockfile 2025-01-20 12:41:08 +01:00
Guido D'Orsi
8bc758ce95 Merge remote-tracking branch 'origin/main' into user-onboarding-upgrade 2025-01-20 12:32:25 +01:00
Guido D'Orsi
73079ca1b7 test: update e2e tests 2025-01-17 22:57:45 +01:00
Guido D'Orsi
c36f19a97f fix: fix imports & and tests on createJazzBrowserContext 2025-01-17 19:28:38 +01:00
Guido D'Orsi
109338923a chore: update lockfile 2025-01-17 18:18:57 +01:00
Guido D'Orsi
f4c2501b06 chore: move auth hook in useInJazzAuth 2025-01-17 18:18:24 +01:00
Guido D'Orsi
9161229474 chore: revert the changes on the todo example 2025-01-17 18:17:46 +01:00
Guido D'Orsi
265f2f40bf fix: fix passkey auth cancelation and add tests 2025-01-17 17:50:35 +01:00
Guido D'Orsi
d2935ac2ae feat: make clerk auth compatible with passkeys 2025-01-17 14:49:25 +01:00
Guido D'Orsi
aaf00b8a20 feat: update starter example with anonymous auth 2025-01-17 14:49:25 +01:00
Guido D'Orsi
f9c6a49d2a feat: onboarding auth on todo example 2025-01-17 14:49:25 +01:00
Guido D'Orsi
2f54a48a3d feat: make auth code disappear from the React setup 2025-01-17 14:49:25 +01:00
Guido D'Orsi
be4dd0a787 feat: centralize auth secret management 2025-01-17 14:49:25 +01:00
Guido D'Orsi
f3f072e7cf feat: improve login/signup code and better UX on the music player auth 2025-01-17 14:49:24 +01:00
Guido D'Orsi
a6270bf552 feat: add onboarding user upgrade to Passkey and Clerk auth 2025-01-17 14:49:24 +01:00
294 changed files with 10757 additions and 7248 deletions

View File

@@ -27,13 +27,8 @@ jobs:
with:
submodules: true
- name: Enable latestcorepack
run: |
echo "Before: corepack version => $(corepack --version || echo 'not installed')"
npm install -g corepack@latest
echo "After : corepack version => $(corepack --version)"
corepack enable
pnpm --version
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3

View File

@@ -18,13 +18,8 @@ jobs:
with:
submodules: true
- name: Enable latestcorepack
run: |
echo "Before: corepack version => $(corepack --version || echo 'not installed')"
npm install -g corepack@latest
echo "After : corepack version => $(corepack --version)"
corepack enable
pnpm --version
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3

View File

@@ -16,13 +16,8 @@ jobs:
with:
submodules: true
- name: Enable latestcorepack
run: |
echo "Before: corepack version => $(corepack --version || echo 'not installed')"
npm install -g corepack@latest
echo "After : corepack version => $(corepack --version)"
corepack enable
pnpm --version
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3

View File

@@ -20,13 +20,8 @@ jobs:
with:
submodules: true
- name: Enable latestcorepack
run: |
echo "Before: corepack version => $(corepack --version || echo 'not installed')"
npm install -g corepack@latest
echo "After : corepack version => $(corepack --version)"
corepack enable
pnpm --version
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3

View File

@@ -22,13 +22,8 @@ jobs:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Enable latestcorepack
run: |
echo "Before: corepack version => $(corepack --version || echo 'not installed')"
npm install -g corepack@latest
echo "After : corepack version => $(corepack --version)"
corepack enable
pnpm --version
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3

View File

@@ -15,13 +15,8 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Enable latestcorepack
run: |
echo "Before: corepack version => $(corepack --version || echo 'not installed')"
npm install -g corepack@latest
echo "After : corepack version => $(corepack --version)"
corepack enable
pnpm --version
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v4

View File

@@ -1,4 +1,4 @@
Copyright 2025, Garden Computing, Inc.
Copyright 2024, Garden Computing, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@@ -17,4 +17,4 @@ For community and support, please join our [Discord](https://discord.gg/utDMjHYg
- Community & support: [Discord](https://discord.gg/utDMjHYg42)
- Updates: [X](https://x.com/jazz_tools) & [Email](https://garden.co/news)
Copyright 2025 — Garden Computing, Inc.
Copyright 2024 — Garden Computing, Inc.

View File

@@ -42,15 +42,6 @@
}
},
"overrides": [
{
"include": ["**/package.json"],
"linter": {
"enabled": false
},
"formatter": {
"enabled": false
}
},
{
"include": ["packages/**/src/**"],
"linter": {

View File

@@ -1,31 +1,5 @@
# chat-rn-clerk
## 1.0.64
### Patch Changes
- jazz-react-native@0.9.23
- jazz-react-native-auth-clerk@0.9.23
- jazz-tools@0.9.23
- jazz-react-native-media-images@0.9.23
## 1.0.63
### Patch Changes
- jazz-react-native@0.9.22
- jazz-react-native-auth-clerk@0.9.22
## 1.0.62
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react-native@0.9.21
- jazz-react-native-auth-clerk@0.9.21
- jazz-react-native-media-images@0.9.21
## 1.0.61
### Patch Changes

View File

@@ -1,9 +1,9 @@
import { Redirect, Stack } from "expo-router";
import { useIsAuthenticated } from "jazz-react-native";
import React from "react";
import { useAuth } from "../../src/auth-context";
export default function HomeLayout() {
const { isAuthenticated } = useAuth();
const isAuthenticated = useIsAuthenticated();
if (isAuthenticated) {
return <Redirect href={"/chat"} />;

View File

@@ -1,8 +1,8 @@
import { Redirect, Stack } from "expo-router";
import { useAuth } from "../../src/auth-context";
import { useIsAuthenticated } from "jazz-react-native";
export default function UnAuthenticatedLayout() {
const { isAuthenticated } = useAuth();
const isAuthenticated = useIsAuthenticated();
if (isAuthenticated) {
return <Redirect href={"/chat"} />;

View File

@@ -20,10 +20,15 @@ export default function ChatScreen() {
const navigation = useNavigation();
const { user } = useUser();
function handleLogOut() {
logOut();
router.navigate("/");
}
useLayoutEffect(() => {
navigation.setOptions({
headerTitle: "Chat",
headerRight: () => <Button onPress={logOut} title="Logout" />,
headerRight: () => <Button onPress={handleLogOut} title="Logout" />,
});
}, [navigation]);

View File

@@ -1,7 +1,7 @@
{
"name": "chat-rn-clerk",
"main": "index.js",
"version": "1.0.64",
"version": "1.0.61",
"scripts": {
"build": "expo export -p ios",
"start": "expo start",

View File

@@ -1,49 +1,17 @@
import { useClerk, useUser } from "@clerk/clerk-expo";
import { JazzProvider, setupKvStore } from "jazz-react-native";
import { useJazzClerkAuth } from "jazz-react-native-auth-clerk";
import React, { createContext, PropsWithChildren, useContext } from "react";
import { Text, View } from "react-native";
const AuthContext = createContext<{
isAuthenticated: boolean;
isLoading: boolean;
}>({
isAuthenticated: false,
isLoading: true,
});
export function useAuth() {
return useContext(AuthContext);
}
const kvStore = setupKvStore();
import { useClerk } from "@clerk/clerk-expo";
import { JazzProviderWithClerk } from "jazz-react-native-auth-clerk";
import React, { PropsWithChildren } from "react";
export function JazzAndAuth({ children }: PropsWithChildren) {
const { isSignedIn, isLoaded: isClerkLoaded } = useUser();
const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk, kvStore);
const isAuthenticated = Boolean(isSignedIn && isClerkLoaded && auth);
return (
<AuthContext.Provider
value={{ isAuthenticated, isLoading: !isClerkLoaded || !auth }}
<JazzProviderWithClerk
clerk={clerk}
storage="sqlite"
peer="wss://cloud.jazz.tools/?key=chat-rn-clerk-example-jazz@garden.co"
>
{state?.errors?.length > 0 &&
state.errors.map((error) => (
<View key={error}>
<Text style={{ color: "red" }}>{error}</Text>
</View>
))}
{auth && clerk.user ? (
<JazzProvider
auth={auth}
storage="sqlite"
peer="wss://cloud.jazz.tools/?key=chat-rn-clerk-example-jazz@garden.co"
>
{children}
</JazzProvider>
) : (
children
)}
</AuthContext.Provider>
{children}
</JazzProviderWithClerk>
);
}

View File

@@ -1,26 +1,5 @@
# chat-rn
## 1.0.61
### Patch Changes
- jazz-react-native@0.9.23
- jazz-tools@0.9.23
## 1.0.60
### Patch Changes
- jazz-react-native@0.9.22
## 1.0.59
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react-native@0.9.21
## 1.0.58
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"version": "1.0.61",
"version": "1.0.58",
"main": "index.js",
"scripts": {
"build": "expo export -p ios",

View File

@@ -9,7 +9,7 @@ import * as Linking from "expo-linking";
import React, { StrictMode, useEffect, useState } from "react";
import HandleInviteScreen from "./invite";
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react-native";
import { JazzProvider } from "jazz-react-native";
import ChatScreen from "./chat";
const Stack = createNativeStackNavigator();
@@ -28,7 +28,6 @@ const linking = {
};
function App() {
const [auth, state] = useDemoAuth();
const [initialRoute, setInitialRoute] = useState<
"ChatScreen" | "HandleInviteScreen"
>("ChatScreen");
@@ -43,14 +42,9 @@ function App() {
});
}, []);
if (!auth) {
return null;
}
return (
<StrictMode>
<JazzProvider
auth={auth}
storage="sqlite"
peer="wss://cloud.jazz.tools/?key=chat-rn-example-jazz@garden.co"
>
@@ -69,9 +63,6 @@ function App() {
</Stack.Navigator>
</NavigationContainer>
</JazzProvider>
{state.state !== "signedIn" ? (
<DemoAuthBasicUI appName="Jazz Chat" state={state} />
) : null}
</StrictMode>
);
}

View File

@@ -1,6 +1,6 @@
import clsx from "clsx";
import * as Clipboard from "expo-clipboard";
import { Group, ID } from "jazz-tools";
import { Group, ID, Profile } from "jazz-tools";
import { useEffect, useState } from "react";
import React, {
Button,
@@ -22,10 +22,16 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
const [chatId, setChatId] = useState<ID<Chat>>();
const loadedChat = useCoState(Chat, chatId, [{}]);
const [message, setMessage] = useState("");
const profile = useCoState(Profile, me._refs.profile?.id, {});
function handleLogOut() {
setChatId(undefined);
logOut();
}
useEffect(() => {
navigation.setOptions({
headerRight: () => <Button onPress={logOut} title="Logout" />,
headerRight: () => <Button onPress={handleLogOut} title="Logout" />,
headerLeft: () =>
loadedChat ? (
<Button
@@ -131,6 +137,18 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
<View className="flex flex-col h-full">
{!loadedChat ? (
<View className="flex flex-col h-full items-center justify-center">
<Text className="text-m font-bold mb-6">Username</Text>
<TextInput
className="rounded h-12 p-2 mb-12 w-40 border border-gray-200 block"
value={profile?.name ?? ""}
onChangeText={(value) => {
if (profile) {
profile.name = value;
}
}}
textAlignVertical="center"
onSubmitEditing={sendMessage}
/>
<TouchableOpacity
onPress={createChat}
className="bg-blue-500 p-4 rounded-md"

View File

@@ -1,29 +1,5 @@
# chat-vue
## 0.0.48
### Patch Changes
- jazz-browser@0.9.23
- jazz-tools@0.9.23
- jazz-vue@0.9.23
## 0.0.47
### Patch Changes
- jazz-browser@0.9.22
- jazz-vue@0.9.22
## 0.0.46
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser@0.9.21
- jazz-vue@0.9.21
## 0.0.45
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-vue",
"version": "0.0.48",
"version": "0.0.45",
"private": true,
"type": "module",
"scripts": {

View File

@@ -4,29 +4,35 @@ import App from "./App.vue";
import "./index.css";
import router from "./router";
const AuthScreen = defineComponent({
name: "AuthScreen",
setup() {
const auth = useDemoAuth();
return () => [
auth.value.state === "anonymous"
? h(DemoAuthBasicUI, {
appName: "Jazz Vue Chat",
auth,
})
: h(App),
];
},
});
const RootComponent = defineComponent({
name: "RootComponent",
setup() {
const { authMethod, state } = useDemoAuth();
return () => [
return () =>
h(
JazzProvider,
{
auth: authMethod.value,
peer: "wss://cloud.jazz.tools/?key=chat-example-jazz@garden.co",
peer: "wss://cloud.jazz.tools/?key=vue-chat-example-jazz@garden.co",
},
{
default: () => h(App),
default: () => h(AuthScreen),
},
),
state.state !== "signedIn" &&
h(DemoAuthBasicUI, {
appName: "Jazz Chat",
state,
}),
];
);
},
});

View File

@@ -1,29 +1,5 @@
# jazz-example-chat
## 0.0.144
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.143
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.142
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.141
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.144",
"version": "0.0.141",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,19 +1,11 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
export function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = useDemoAuth();
return (
<>
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=chat-example-jazz@garden.co"
>
{children}
</JazzProvider>
{state.state !== "signedIn" && (
<DemoAuthBasicUI appName="Jazz Chat" state={state} />
)}
</>
<JazzProvider
localOnly="anonymous"
peer="wss://cloud.jazz.tools/?key=chat-example-jazz@garden.co"
>
{children}
</JazzProvider>
);
}

View File

@@ -1,29 +1,5 @@
# minimal-auth-clerk
## 0.0.43
### Patch Changes
- jazz-react@0.9.23
- jazz-react-auth-clerk@0.9.23
- jazz-tools@0.9.23
## 0.0.42
### Patch Changes
- jazz-react@0.9.22
- jazz-react-auth-clerk@0.9.22
## 0.0.41
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react@0.9.21
- jazz-react-auth-clerk@0.9.21
## 0.0.40
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "clerk",
"private": true,
"version": "0.0.43",
"version": "0.0.40",
"type": "module",
"scripts": {
"dev": "vite",
@@ -13,7 +13,7 @@
"dependencies": {
"@clerk/clerk-react": "^5.4.1",
"jazz-react": "workspace:*",
"jazz-react-auth-clerk": "workspace:0.9.23",
"jazz-react-auth-clerk": "workspace:0.9.20",
"jazz-tools": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1"

View File

@@ -1,13 +1,25 @@
import { useAccount } from "jazz-react";
import { SignInButton } from "@clerk/clerk-react";
import { useAccount, useIsAuthenticated } from "jazz-react";
function App() {
const { me, logOut } = useAccount();
const isAuthenticated = useIsAuthenticated();
if (isAuthenticated) {
return (
<div className="container">
<h1>You're logged in</h1>
<p>Welcome back, {me?.profile?.name}</p>
<button onClick={() => logOut()}>Logout</button>
</div>
);
}
return (
<div className="container">
<h1>You're logged in</h1>
<p>Welcome back, {me?.profile?.name}</p>
<button onClick={() => logOut()}>Logout</button>
<h1>You're not logged in</h1>
<SignInButton />
</div>
);
}

View File

@@ -62,7 +62,7 @@ button {
}
.container {
max-width: 200px;
max-width: 400px;
margin: 0 auto;
padding: 0 1rem;
display: flex;

View File

@@ -1,10 +1,9 @@
import { ClerkProvider, SignInButton, useClerk } from "@clerk/clerk-react";
import { useJazzClerkAuth } from "jazz-react-auth-clerk";
import { ClerkProvider, useClerk } from "@clerk/clerk-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { JazzProvider } from "jazz-react";
import { JazzProviderWithClerk } from "jazz-react-auth-clerk";
// Import your publishable key
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY;
@@ -13,35 +12,26 @@ if (!PUBLISHABLE_KEY) {
throw new Error("Add your Clerk publishable key to the .env.local file");
}
function JazzAndAuth({ children }: { children: React.ReactNode }) {
function JazzProvider({ children }: { children: React.ReactNode }) {
const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk);
return (
<main className="container">
{state?.errors?.map((error) => (
<div key={error}>{error}</div>
))}
{clerk.user && auth ? (
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=minimal-auth-clerk-example@garden.co"
>
{children}
</JazzProvider>
) : (
<SignInButton />
)}
</main>
<JazzProviderWithClerk
clerk={clerk}
localOnly="anonymous"
peer="wss://cloud.jazz.tools/?key=minimal-auth-clerk-example@garden.co"
>
{children}
</JazzProviderWithClerk>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY} afterSignOutUrl="/">
<JazzAndAuth>
<JazzProvider>
<App />
</JazzAndAuth>
</JazzProvider>
</ClerkProvider>
</StrictMode>,
);

View File

@@ -1,26 +1,5 @@
# file-share-svelte
## 0.0.28
### Patch Changes
- jazz-svelte@0.9.23
- jazz-tools@0.9.23
## 0.0.27
### Patch Changes
- jazz-svelte@0.9.22
## 0.0.26
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-svelte@0.9.21
## 0.0.25
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "file-share-svelte",
"version": "0.0.28",
"version": "0.0.25",
"private": true,
"type": "module",
"scripts": {

View File

@@ -17,6 +17,6 @@ export function formatFileSize(bytes: number): string {
* @param createdAt The creation date
* @returns A unique file ID string
*/
export function generateTempFileId(fileName: string, createdAt: Date): string {
return `file-${fileName}-${createdAt.getTime()}`;
export function generateTempFileId(fileName: string | undefined, createdAt: Date | undefined): string {
return `file-${fileName ?? 'unknown'}-${createdAt?.getTime() ?? 0}`;
}

View File

@@ -14,9 +14,6 @@
import { FileShareAccount } from '$lib/schema';
let { children } = $props();
const auth = usePasskeyAuth({
appName: 'File Share'
});
</script>
<svelte:head>
@@ -25,21 +22,13 @@
<Toaster richColors />
{#if auth.state.state === 'ready'}
<div class="fixed inset-0 flex items-center justify-center bg-gray-50/80">
<div class="rounded-lg bg-white p-8 shadow-lg">
<PasskeyAuthBasicUI state={auth.state} />
</div>
</div>
{/if}
{#if auth.current}
<JazzProvider
AccountSchema={FileShareAccount}
auth={auth.current}
peer="wss://cloud.jazz.tools/?key=file-share-svelte@garden.co"
>
<JazzProvider
AccountSchema={FileShareAccount}
peer="wss://cloud.jazz.tools/?key=minimal-svelte-auth-passkey@garden.co"
>
<PasskeyAuthBasicUI auth={usePasskeyAuth({ appName: 'File Share' })}>
<div class="min-h-screen bg-gray-100">
{@render children()}
</div>
</JazzProvider>
{/if}
</PasskeyAuthBasicUI>
</JazzProvider>

View File

@@ -28,7 +28,7 @@
const input = event.target as HTMLInputElement;
const files = input.files;
if (!files || !files.length || !me?.root?.sharedFiles || !me.root.publicGroup) return;
if (!files || !files.length || !me.root?.sharedFiles || !me.root.publicGroup) return;
const file = files[0];
const fileName = file.name;
@@ -129,12 +129,14 @@
{#if sharedFiles.current}
{#if !(sharedFiles.current.length === 0 && uploadingFiles.size === 0)}
{#each [...sharedFiles.current, ...uploadingFiles.values()] as file (generateTempFileId(file?.name, file?.createdAt))}
<FileItem
{file}
loading={uploadingFiles.has(generateTempFileId(file?.name, file?.createdAt))}
onShare={shareFile}
onDelete={deleteFile}
/>
{#if file}
<FileItem
{file}
loading={uploadingFiles.has(generateTempFileId(file?.name, file?.createdAt))}
onShare={shareFile}
onDelete={deleteFile}
/>
{/if}
{/each}
{:else}
<p class="text-center text-gray-500">No files yet</p>

View File

@@ -1,29 +1,5 @@
# form
## 0.0.39
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.38
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.37
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.36
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "form",
"private": true,
"version": "0.0.39",
"version": "0.0.36",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,4 +1,4 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
@@ -6,22 +6,14 @@ import "./index.css";
import { JazzAccount } from "./schema.ts";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=form-example@garden.co"
AccountSchema={JazzAccount}
>
{children}
</JazzProvider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Form" state={authState} />
)}
</>
<JazzProvider
peer="wss://cloud.jazz.tools/?key=form-example@garden.co"
localOnly="anonymous"
AccountSchema={JazzAccount}
>
{children}
</JazzProvider>
);
}

View File

@@ -1,29 +1,5 @@
# image-upload
## 0.0.41
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.40
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.39
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.38
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "image-upload",
"private": true,
"version": "0.0.41",
"version": "0.0.38",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,4 +1,4 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
@@ -6,21 +6,15 @@ import "./index.css";
import { JazzAccount } from "./schema.ts";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=image-upload-example@garden.co"
localOnly="anonymous"
AccountSchema={JazzAccount}
>
{children}
</JazzProvider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Image upload" state={authState} />
)}
</>
);
}

View File

@@ -1,20 +1,5 @@
# jazz-example-inspector
## 0.0.102
### Patch Changes
- Updated dependencies [70c9a5d]
- cojson@0.9.23
- cojson-transport-ws@0.9.23
## 0.0.101
### Patch Changes
- Updated dependencies [14b6149]
- cojson-transport-ws@0.9.22
## 0.0.100
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector-app",
"private": true,
"version": "0.0.102",
"version": "0.0.100",
"type": "module",
"scripts": {
"dev": "vite",
@@ -16,8 +16,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"cojson": "workspace:0.9.23",
"cojson-transport-ws": "workspace:0.9.23",
"cojson": "workspace:0.9.19",
"cojson-transport-ws": "workspace:0.9.19",
"hash-slash": "workspace:0.2.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",

View File

@@ -1,28 +1,5 @@
# jazz-example-musicplayer
## 0.0.65
### Patch Changes
- jazz-inspector@0.9.23
- jazz-react@0.9.23
- jazz-tools@0.9.23
## 0.0.64
### Patch Changes
- jazz-react@0.9.22
## 0.0.63
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-inspector@0.9.22
- jazz-react@0.9.21
## 0.0.62
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.65",
"version": "0.0.62",
"type": "module",
"scripts": {
"dev": "vite",
@@ -13,13 +13,16 @@
"test:ui": "playwright test --ui"
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-slot": "^1.1.1",
"@radix-ui/react-toast": "^1.1.4",
"@radix-ui/react-tooltip": "^1.1.6",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.9.23",
"jazz-tools": "workspace:0.9.23",
"jazz-react": "workspace:0.9.20",
"jazz-tools": "workspace:0.9.20",
"jazz-inspector": "workspace:*",
"lucide-react": "^0.274.0",
"react": "^18.3.1",

View File

@@ -86,7 +86,7 @@ export class MusicaAccount extends Account {
* You can use it to set up the account root and any other initial CoValues you need.
*/
migrate() {
if (!this._refs.root) {
if (this.root === undefined) {
const ownership = { owner: this };
const tracks = ListOfTracks.create([], ownership);

View File

@@ -11,7 +11,7 @@ import { PlayerControls } from "./components/PlayerControls";
import "./index.css";
import { MusicaAccount } from "@/1_schema";
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
import { useUploadExampleData } from "./lib/useUploadExampleData";
/**
@@ -54,30 +54,11 @@ function Main() {
);
}
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = useDemoAuth();
const peer =
(new URL(window.location.href).searchParams.get(
"peer",
) as `ws://${string}`) ??
"wss://cloud.jazz.tools/?key=music-player-example-jazz@garden.co";
return (
<>
<JazzProvider
storage="indexedDB"
auth={auth}
peer={peer}
AccountSchema={MusicaAccount}
>
{children}
<JazzInspector />
</JazzProvider>
<DemoAuthBasicUI appName="Jazz Music Player" state={state} />
</>
);
}
const peer =
(new URL(window.location.href).searchParams.get(
"peer",
) as `ws://${string}`) ??
"wss://cloud.jazz.tools/?key=music-player-example-jazz@garden.co";
declare module "jazz-react" {
interface Register {
@@ -87,8 +68,15 @@ declare module "jazz-react" {
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<JazzAndAuth>
<JazzProvider
peer={peer}
storage="indexedDB"
localOnly="anonymous" // This makes the app work in local mode when the user is anonymous
AccountSchema={MusicaAccount}
defaultProfileName="Anonymous unicorn"
>
<Main />
</JazzAndAuth>
<JazzInspector />
</JazzProvider>
</React.StrictMode>,
);

View File

@@ -1,12 +1,17 @@
import { useToast } from "@/hooks/use-toast";
import { createInviteLink, useAccount, useCoState } from "jazz-react";
import {
createInviteLink,
useAccount,
useCoState,
useIsAuthenticated,
} from "jazz-react";
import { ID } from "jazz-tools";
import { useNavigate, useParams } from "react-router";
import { Playlist } from "./1_schema";
import { createNewPlaylist, uploadMusicTracks } from "./4_actions";
import { MediaPlayer } from "./5_useMediaPlayer";
import { AuthButton } from "./components/AuthButton";
import { FileUploadButton } from "./components/FileUploadButton";
import { LogoutButton } from "./components/LogoutButton";
import { MusicTrackRow } from "./components/MusicTrackRow";
import { PlaylistTitleInput } from "./components/PlaylistTitleInput";
import { SidePanel } from "./components/SidePanel";
@@ -66,6 +71,8 @@ export function HomePage({ mediaPlayer }: { mediaPlayer: MediaPlayer }) {
});
};
const isAuthenticated = useIsAuthenticated();
return (
<div className="flex flex-col h-screen text-gray-800 bg-blue-50">
<div className="flex flex-1 overflow-hidden">
@@ -86,12 +93,12 @@ export function HomePage({ mediaPlayer }: { mediaPlayer: MediaPlayer }) {
<Button onClick={handleCreatePlaylist}>New playlist</Button>
</>
)}
{!isRootPlaylist && (
{!isRootPlaylist && isAuthenticated && (
<Button onClick={handlePlaylistShareClick}>
Share playlist
</Button>
)}
<LogoutButton />
<AuthButton />
</div>
</div>
<ul className="flex flex-col">

View File

@@ -0,0 +1,40 @@
"use client";
import { Button } from "@/components/ui/button";
import { useAccount, useIsAuthenticated } from "jazz-react";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { AuthModal } from "./AuthModal";
export function AuthButton() {
const [open, setOpen] = useState(false);
const { logOut } = useAccount();
const navigate = useNavigate();
const isAuthenticated = useIsAuthenticated();
function handleSignOut() {
logOut();
navigate("/");
}
if (isAuthenticated) {
return (
<Button variant="outline" onClick={handleSignOut}>
Sign out
</Button>
);
}
return (
<>
<Button
onClick={() => setOpen(true)}
className="bg-white text-black hover:bg-gray-100"
>
Sign up
</Button>
<AuthModal open={open} onOpenChange={setOpen} />
</>
);
}

View File

@@ -0,0 +1,97 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { usePasskeyAuth } from "jazz-react";
import { useState } from "react";
interface AuthModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
}
export function AuthModal({ open, onOpenChange }: AuthModalProps) {
const [username, setUsername] = useState("");
const [isSignUp, setIsSignUp] = useState(true);
const [error, setError] = useState<string | null>(null);
const auth = usePasskeyAuth({
appName: "Jazz Music Player",
});
const handleViewChange = () => {
setIsSignUp(!isSignUp);
setError(null);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
if (isSignUp) {
await auth.signUp(username);
} else {
await auth.logIn();
}
onOpenChange(false);
} catch (error) {
setError(error instanceof Error ? error.message : "Unknown error");
}
};
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle className="text-2xl font-bold">
{isSignUp ? "Create account" : "Welcome back"}
</DialogTitle>
<DialogDescription>
{isSignUp
? "Sign up to enable network sync and share your playlists with others"
: "Changes done before logging in will be lost"}
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
{isSignUp && (
<div className="space-y-2">
<Label htmlFor="username">Username</Label>
<Input
id="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Enter your username"
required
/>
</div>
)}
{error && <div className="text-sm text-red-500">{error}</div>}
<div className="space-y-4">
<Button
type="submit"
className="w-full bg-blue-600 hover:bg-blue-700"
>
{isSignUp ? "Sign up with passkey" : "Login with passkey"}
</Button>
<div className="text-center text-sm">
{isSignUp ? "Already have an account?" : "Don't have an account?"}{" "}
<button
type="button"
onClick={handleViewChange}
className="text-blue-600 hover:underline"
>
{isSignUp ? "Login" : "Sign up"}
</button>
</div>
</div>
</form>
</DialogContent>
</Dialog>
);
}

View File

@@ -0,0 +1,37 @@
import { Badge } from "@/components/ui/badge";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { useIsAuthenticated } from "jazz-react";
import { Info } from "lucide-react";
export function LocalOnlyTag() {
const isAuthenticated = useIsAuthenticated();
if (isAuthenticated) {
return null;
}
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="inline-flex items-center gap-1.5 cursor-help">
<Badge variant="default" className="h-5 text-xs font-normal">
Local only
</Badge>
<Info className="h-3.5 w-3.5 text-muted-foreground" />
</div>
</TooltipTrigger>
<TooltipContent className="max-w-[250px]">
<p>
Sign up to enable network sync and share your playlists with others
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}

View File

@@ -1,5 +1,6 @@
import { useAccount } from "jazz-react";
import { useNavigate, useParams } from "react-router";
import { LocalOnlyTag } from "./LocalOnlyTag";
export function SidePanel() {
const { playlistId } = useParams();
@@ -25,7 +26,7 @@ export function SidePanel() {
return (
<aside className="w-64 p-6 bg-white overflow-y-auto">
<div className="flex items-center mb-6">
<div className="flex items-center mb-1">
<svg
className="w-8 h-8 mr-2"
viewBox="0 0 24 24"
@@ -46,6 +47,9 @@ export function SidePanel() {
</svg>
<span className="text-xl font-bold text-blue-600">Music Player</span>
</div>
<div className="mb-6">
<LocalOnlyTag />
</div>
<nav>
<h2 className="mb-2 text-sm font-semibold text-gray-600">Playlists</h2>
<ul className="space-y-1">

View File

@@ -0,0 +1,36 @@
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
},
);
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
);
}
export { Badge, badgeVariants };

View File

@@ -0,0 +1,120 @@
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import * as React from "react";
import { cn } from "@/lib/utils";
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className,
)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className,
)}
{...props}
/>
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className,
)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
};

View File

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

View File

@@ -0,0 +1,24 @@
import * as LabelPrimitive from "@radix-ui/react-label";
import { type VariantProps, cva } from "class-variance-authority";
import * as React from "react";
import { cn } from "@/lib/utils";
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
);
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
));
Label.displayName = LabelPrimitive.Root.displayName;
export { Label };

View File

@@ -0,0 +1,30 @@
"use client";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import * as React from "react";
import { cn } from "@/lib/utils";
const TooltipProvider = TooltipPrimitive.Provider;
const Tooltip = TooltipPrimitive.Root;
const TooltipTrigger = TooltipPrimitive.Trigger;
const TooltipContent = React.forwardRef<
React.ElementRef<typeof TooltipPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<TooltipPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className,
)}
{...props}
/>
));
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };

View File

@@ -1,11 +1,14 @@
import { MusicaAccount } from "@/1_schema";
import { useAccount } from "jazz-react";
import { useEffect } from "react";
import { uploadMusicTracks } from "../4_actions";
export function useUploadExampleData() {
const { me } = useAccount();
useEffect(() => {
uploadOnboardingData();
}, []);
}, [me.id]);
}
async function uploadOnboardingData() {

View File

@@ -1,44 +1,84 @@
import { test } from "@playwright/test";
import { BrowserContext, test } from "@playwright/test";
import { HomePage } from "./pages/HomePage";
import { LoginPage } from "./pages/LoginPage";
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
test("create a new playlist and share", async ({ page }) => {
const loginPage = new LoginPage(page);
async function mockAuthenticator(context: BrowserContext) {
await context.addInitScript(() => {
Object.defineProperty(window.navigator, "credentials", {
value: {
...window.navigator.credentials,
create: async () => ({
type: "public-key",
id: new Uint8Array([1, 2, 3, 4]),
rawId: new Uint8Array([1, 2, 3, 4]),
response: {
clientDataJSON: new Uint8Array([1]),
attestationObject: new Uint8Array([2]),
},
}),
get: async () => ({
type: "public-key",
id: new Uint8Array([1, 2, 3, 4]),
rawId: new Uint8Array([1, 2, 3, 4]),
response: {
authenticatorData: new Uint8Array([1]),
clientDataJSON: new Uint8Array([2]),
signature: new Uint8Array([3]),
},
}),
},
configurable: true,
});
});
}
await loginPage.goto();
await loginPage.fillUsername("S. Mario");
await loginPage.signup();
// Configure the authenticator
test.beforeEach(async ({ context }) => {
// Enable virtual authenticator environment
await mockAuthenticator(context);
});
const homePage = new HomePage(page);
test("create a new playlist and share", async ({
page: marioPage,
browser,
}) => {
await marioPage.goto("/");
const marioHome = new HomePage(marioPage);
// The example song should be loaded
await homePage.expectMusicTrack("Example song");
await homePage.editTrackTitle("Example song", "Super Mario World");
await marioHome.expectMusicTrack("Example song");
await marioHome.editTrackTitle("Example song", "Super Mario World");
await homePage.createPlaylist();
await homePage.editPlaylistTitle("Save the princess");
await marioHome.createPlaylist();
await marioHome.editPlaylistTitle("Save the princess");
await homePage.navigateToPlaylist("All tracks");
await homePage.addTrackToPlaylist("Super Mario World", "Save the princess");
await marioHome.navigateToPlaylist("All tracks");
await marioHome.addTrackToPlaylist("Super Mario World", "Save the princess");
await homePage.navigateToPlaylist("Save the princess");
await homePage.expectMusicTrack("Super Mario World");
await marioHome.navigateToPlaylist("Save the princess");
await marioHome.expectMusicTrack("Super Mario World");
const url = await homePage.getShareLink();
await marioHome.signUp("Mario");
const url = await marioHome.getShareLink();
await sleep(4000); // Wait for the sync to complete
await homePage.logout();
const luigiContext = await browser.newContext();
await mockAuthenticator(luigiContext);
await loginPage.goto();
await loginPage.fillUsername("Luigi");
await loginPage.signup();
const luigiPage = await luigiContext.newPage();
await luigiPage.goto("/");
await page.goto(url);
const luigiHome = new HomePage(luigiPage);
await homePage.expectMusicTrack("Super Mario World");
await homePage.playMusicTrack("Super Mario World");
await homePage.expectActiveTrackPlaying();
await luigiHome.signUp("Luigi");
await luigiPage.goto(url);
await luigiHome.expectMusicTrack("Super Mario World");
await luigiHome.playMusicTrack("Super Mario World");
await luigiHome.expectActiveTrackPlaying();
});

View File

@@ -95,6 +95,17 @@ export class HomePage {
.click();
}
async signUp(name: string) {
await this.page.getByRole("button", { name: "Sign up" }).click();
await this.page.getByRole("textbox", { name: "Username" }).fill(name);
await this.page
.getByRole("button", { name: "Sign up with passkey" })
.click();
await expect(
this.page.getByRole("button", { name: "Sign out" }),
).toBeVisible();
}
async logout() {
await this.logoutButton.click();
}

View File

@@ -1,40 +0,0 @@
import { Locator, Page, expect } from "@playwright/test";
export class LoginPage {
readonly page: Page;
readonly usernameInput: Locator;
readonly signupButton: Locator;
constructor(page: Page) {
this.page = page;
this.usernameInput = page.getByRole("textbox");
this.signupButton = page.getByRole("button", {
name: "Sign up",
});
}
async goto() {
this.page.goto("/");
}
async fillUsername(value: string) {
await this.usernameInput.clear();
await this.usernameInput.fill(value);
}
async loginAs(value: string) {
await this.page
.getByRole("button", {
name: value,
})
.click();
}
async signup() {
await this.signupButton.click();
}
async expectLoaded() {
await expect(this.signupButton).toBeVisible();
}
}

View File

@@ -1,29 +1,5 @@
# jazz-example-onboarding
## 0.0.45
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.44
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.43
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.42
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-onboarding",
"private": true,
"version": "0.0.45",
"version": "0.0.42",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,7 +1,7 @@
import App from "@/App.tsx";
import "@/index.css";
import { HRAccount } from "@/schema.ts";
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
import React from "react";
import ReactDOM from "react-dom/client";
@@ -12,16 +12,10 @@ const peer =
"wss://cloud.jazz.tools/?key=onboarding-example-jazz@garden.co";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider AccountSchema={HRAccount} auth={auth} peer={peer}>
{children}
</JazzProvider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Jazz Onboarding" state={authState} />
)}
</>
<JazzProvider AccountSchema={HRAccount} peer={peer}>
{children}
</JazzProvider>
);
}

View File

@@ -0,0 +1,20 @@
// vite.config.ts
import path from "path";
import react from "file:///Users/brad/dev/jazz/node_modules/@vitejs/plugin-react-swc/index.mjs";
import topLevelAwait from "file:///Users/brad/dev/jazz/node_modules/vite-plugin-top-level-await/exports/import.mjs";
import { defineConfig } from "file:///Users/brad/dev/jazz/node_modules/vite/dist/node/index.js";
var __vite_injected_original_dirname =
"/Users/brad/dev/jazz/examples/onboarding";
var vite_config_default = defineConfig({
plugins: [react(), topLevelAwait()],
resolve: {
alias: {
"@": path.resolve(__vite_injected_original_dirname, "./src"),
},
},
build: {
minify: false,
},
});
export { vite_config_default as default };
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcudHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbImNvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9kaXJuYW1lID0gXCIvVXNlcnMvYnJhZC9kZXYvamF6ei9leGFtcGxlcy9vbmJvYXJkaW5nXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCIvVXNlcnMvYnJhZC9kZXYvamF6ei9leGFtcGxlcy9vbmJvYXJkaW5nL3ZpdGUuY29uZmlnLnRzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9Vc2Vycy9icmFkL2Rldi9qYXp6L2V4YW1wbGVzL29uYm9hcmRpbmcvdml0ZS5jb25maWcudHNcIjtpbXBvcnQgcGF0aCBmcm9tIFwicGF0aFwiO1xuaW1wb3J0IHJlYWN0IGZyb20gXCJAdml0ZWpzL3BsdWdpbi1yZWFjdC1zd2NcIjtcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ2aXRlXCI7XG5pbXBvcnQgdG9wTGV2ZWxBd2FpdCBmcm9tIFwidml0ZS1wbHVnaW4tdG9wLWxldmVsLWF3YWl0XCI7XG5cbi8vIGh0dHBzOi8vdml0ZWpzLmRldi9jb25maWcvXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuICBwbHVnaW5zOiBbcmVhY3QoKSwgdG9wTGV2ZWxBd2FpdCgpXSxcbiAgcmVzb2x2ZToge1xuICAgIGFsaWFzOiB7XG4gICAgICBcIkBcIjogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgXCIuL3NyY1wiKSxcbiAgICB9LFxuICB9LFxuICBidWlsZDoge1xuICAgIG1pbmlmeTogZmFsc2UsXG4gIH0sXG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7QUFBMFMsT0FBTyxVQUFVO0FBQzNULE9BQU8sV0FBVztBQUNsQixTQUFTLG9CQUFvQjtBQUM3QixPQUFPLG1CQUFtQjtBQUgxQixJQUFNLG1DQUFtQztBQU16QyxJQUFPLHNCQUFRLGFBQWE7QUFBQSxFQUMxQixTQUFTLENBQUMsTUFBTSxHQUFHLGNBQWMsQ0FBQztBQUFBLEVBQ2xDLFNBQVM7QUFBQSxJQUNQLE9BQU87QUFBQSxNQUNMLEtBQUssS0FBSyxRQUFRLGtDQUFXLE9BQU87QUFBQSxJQUN0QztBQUFBLEVBQ0Y7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNMLFFBQVE7QUFBQSxFQUNWO0FBQ0YsQ0FBQzsiLAogICJuYW1lcyI6IFtdCn0K

View File

@@ -1,26 +1,5 @@
# organization
## 0.0.37
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
## 0.0.36
### Patch Changes
- jazz-react@0.9.22
## 0.0.35
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react@0.9.21
## 0.0.34
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "organization",
"private": true,
"version": "0.0.37",
"version": "0.0.34",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,4 +1,4 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
@@ -28,22 +28,13 @@ function Router() {
}
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
AccountSchema={JazzAccount}
auth={auth}
peer="wss://cloud.jazz.tools/?key=organization-example@garden.co"
>
{children}
</JazzProvider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Organization" state={authState} />
)}
</>
<JazzProvider
AccountSchema={JazzAccount}
peer="wss://cloud.jazz.tools/?key=organization-example@garden.co"
>
{children}
</JazzProvider>
);
}

View File

@@ -1,23 +1,5 @@
# passkey-svelte
## 0.0.32
### Patch Changes
- jazz-svelte@0.9.23
## 0.0.31
### Patch Changes
- jazz-svelte@0.9.22
## 0.0.30
### Patch Changes
- jazz-svelte@0.9.21
## 0.0.29
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "passkey-svelte",
"version": "0.0.32",
"version": "0.0.29",
"type": "module",
"private": true,
"scripts": {

View File

@@ -2,26 +2,13 @@
import { JazzProvider, PasskeyAuthBasicUI, usePasskeyAuth } from 'jazz-svelte';
let { children } = $props();
let auth = usePasskeyAuth({ appName: 'minimal-svelte-auth-passkey' });
$inspect(auth.state);
</script>
<div
style="width: 100vw; height: 100vh; display: flex; align-items: center; justify-content: center;"
>
<PasskeyAuthBasicUI state={auth.state} />
{#if auth.current}
<JazzProvider
auth={auth.current}
peer="wss://cloud.jazz.tools/?key=minimal-svelte-auth-passkey@garden.co"
>
{@render children?.()}
</JazzProvider>
{/if}
</div>
<JazzProvider peer="wss://cloud.jazz.tools/?key=minimal-svelte-auth-passkey@garden.co">
<PasskeyAuthBasicUI auth={usePasskeyAuth({ appName: 'minimal-svelte-auth-passkey' })}>
{@render children?.()}
</PasskeyAuthBasicUI>
</JazzProvider>
<style>
:global(html, body) {

View File

@@ -1,26 +1,5 @@
# minimal-auth-passkey
## 0.0.42
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
## 0.0.41
### Patch Changes
- jazz-react@0.9.22
## 0.0.40
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react@0.9.21
## 0.0.39
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "passkey",
"private": true,
"version": "0.0.42",
"version": "0.0.39",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -5,20 +5,15 @@ import App from "./App.tsx";
import "./index.css";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = usePasskeyAuth({
const auth = usePasskeyAuth({
appName: "Jazz Minimal Auth Passkey Example",
});
return (
<>
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=minimal-auth-passkey-example@garden.co"
>
{children}
</JazzProvider>
<PasskeyAuthBasicUI state={state} />
</>
<JazzProvider peer="wss://cloud.jazz.tools/?key=minimal-auth-passkey-example@garden.co">
<PasskeyAuthBasicUI {...auth} />
{children}
</JazzProvider>
);
}

View File

@@ -1,26 +1,5 @@
# jazz-password-manager
## 0.0.63
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
## 0.0.62
### Patch Changes
- jazz-react@0.9.22
## 0.0.61
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react@0.9.21
## 0.0.60
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.63",
"version": "0.0.60",
"type": "module",
"scripts": {
"dev": "vite",
@@ -12,8 +12,8 @@
"clean-install": "rm -rf node_modules pnpm-lock.yaml && pnpm install"
},
"dependencies": {
"jazz-react": "workspace:0.9.23",
"jazz-tools": "workspace:0.9.23",
"jazz-react": "workspace:0.9.20",
"jazz-tools": "workspace:0.9.20",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.41.5",

View File

@@ -6,21 +6,18 @@ import App from "./5_App.tsx";
import "./index.css";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = usePasskeyAuth({
const auth = usePasskeyAuth({
appName: "Jazz Password Manager",
});
return (
<>
<JazzProvider
AccountSchema={PasswordManagerAccount}
auth={auth}
peer="wss://cloud.jazz.tools/?key=password-manager-example-jazz@garden.co"
>
{children}
</JazzProvider>
<PasskeyAuthBasicUI state={state} />
</>
<JazzProvider
AccountSchema={PasswordManagerAccount}
peer="wss://cloud.jazz.tools/?key=password-manager-example-jazz@garden.co"
>
<PasskeyAuthBasicUI {...auth} />
{children}
</JazzProvider>
);
}

View File

@@ -1,29 +1,5 @@
# jazz-example-pets
## 0.0.161
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.160
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.159
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.158
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.161",
"version": "0.0.158",
"type": "module",
"scripts": {
"dev": "vite",
@@ -19,9 +19,9 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-browser-media-images": "workspace:0.9.23",
"jazz-react": "workspace:0.9.23",
"jazz-tools": "workspace:0.9.23",
"jazz-browser-media-images": "workspace:0.9.20",
"jazz-react": "workspace:0.9.20",
"jazz-tools": "workspace:0.9.20",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.3.1",
@@ -41,7 +41,7 @@
"@vitejs/plugin-react-swc": "^3.3.2",
"autoprefixer": "^10.4.20",
"is-ci": "^3.0.1",
"jazz-run": "workspace:0.9.23",
"jazz-run": "workspace:0.9.20",
"postcss": "^8.4.27",
"tailwindcss": "^3.4.15",
"typescript": "~5.6.2",

View File

@@ -8,13 +8,7 @@ import {
} from "react-router-dom";
import "./index.css";
import {
DemoAuthBasicUI,
JazzProvider,
useAcceptInvite,
useAccount,
useDemoAuth,
} from "jazz-react";
import { JazzProvider, useAcceptInvite, useAccount } from "jazz-react";
import { PetAccount, PetPost } from "./1_schema.ts";
import { NewPetPostForm } from "./3_NewPetPostForm.tsx";
@@ -41,14 +35,15 @@ const peer =
const appName = "Jazz Rate My Pet Example";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider auth={auth} peer={peer} AccountSchema={PetAccount}>
<JazzProvider
peer={peer}
AccountSchema={PetAccount}
localOnly="anonymous"
>
{children}
</JazzProvider>
<DemoAuthBasicUI appName={appName} state={authState} />
</>
);
}

View File

@@ -1,29 +1,5 @@
# reactions
## 0.0.41
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
- jazz-browser-media-images@0.9.23
## 0.0.40
### Patch Changes
- jazz-browser-media-images@0.9.22
- jazz-react@0.9.22
## 0.0.39
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser-media-images@0.9.21
- jazz-react@0.9.21
## 0.0.38
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "reactions",
"private": true,
"version": "0.0.41",
"version": "0.0.38",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,24 +1,18 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
auth={auth}
localOnly="anonymous"
peer="wss://cloud.jazz.tools/?key=reactions-example@garden.co"
>
{children}
</JazzProvider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="Reactions" state={authState} />
)}
</>
);
}

View File

@@ -1,29 +1,5 @@
# todo-vue
## 0.0.46
### Patch Changes
- jazz-browser@0.9.23
- jazz-tools@0.9.23
- jazz-vue@0.9.23
## 0.0.45
### Patch Changes
- jazz-browser@0.9.22
- jazz-vue@0.9.22
## 0.0.44
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-browser@0.9.21
- jazz-vue@0.9.21
## 0.0.43
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "todo-vue",
"version": "0.0.46",
"version": "0.0.43",
"private": true,
"type": "module",
"scripts": {

View File

@@ -11,30 +11,36 @@ declare module "jazz-vue" {
}
}
const AuthScreen = defineComponent({
name: "AuthScreen",
setup() {
const auth = useDemoAuth();
return () => [
auth.value.state === "anonymous"
? h(DemoAuthBasicUI, {
appName: "Jazz Vue Todo",
auth,
})
: h(App),
];
},
});
const RootComponent = defineComponent({
name: "RootComponent",
setup() {
const { authMethod, state } = useDemoAuth();
return () => [
return () =>
h(
JazzProvider,
{
AccountSchema: ToDoAccount,
auth: authMethod.value,
peer: "wss://cloud.jazz.tools/?key=vue-todo-example-jazz@garden.co",
},
{
default: () => h(App),
default: () => h(AuthScreen),
},
),
state.state !== "signedIn" &&
h(DemoAuthBasicUI, {
appName: "Jazz Vue Todo",
state,
}),
];
);
},
});

View File

@@ -1,26 +1,5 @@
# jazz-example-todo
## 0.0.160
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
## 0.0.159
### Patch Changes
- jazz-react@0.9.22
## 0.0.158
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react@0.9.21
## 0.0.157
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.160",
"version": "0.0.157",
"type": "module",
"scripts": {
"dev": "vite",
@@ -16,8 +16,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.9.23",
"jazz-tools": "workspace:0.9.23",
"jazz-react": "workspace:0.9.20",
"jazz-tools": "workspace:0.9.20",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.3.1",

View File

@@ -38,18 +38,17 @@ import {
const appName = "Jazz Todo List Example";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName });
const auth = usePasskeyAuth({ appName });
return (
<>
<JazzProvider
AccountSchema={TodoAccount}
auth={passkeyAuth}
peer="wss://cloud.jazz.tools/?key=todo-example-jazz@garden.co"
>
<PasskeyAuthBasicUI {...auth} />
{children}
</JazzProvider>
<PasskeyAuthBasicUI state={passKeyState} />
</>
);
}

View File

@@ -1,26 +1,5 @@
# version-history
## 0.0.38
### Patch Changes
- jazz-react@0.9.23
- jazz-tools@0.9.23
## 0.0.37
### Patch Changes
- jazz-react@0.9.22
## 0.0.36
### Patch Changes
- Updated dependencies [1be017d]
- jazz-tools@0.9.21
- jazz-react@0.9.21
## 0.0.35
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "version-history",
"private": true,
"version": "0.0.38",
"version": "0.0.35",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,25 +1,17 @@
import { DemoAuthBasicUI, JazzProvider, useDemoAuth } from "jazz-react";
import { JazzProvider } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<JazzProvider
auth={auth}
peer="wss://cloud.jazz.tools/?key=version-history@garden.co"
>
{children}
</JazzProvider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="React + Demo Auth" state={authState} />
)}
</>
<JazzProvider
localOnly="anonymous"
peer="wss://cloud.jazz.tools/?key=version-history@garden.co"
>
{children}
</JazzProvider>
);
}

12
flake.lock generated
View File

@@ -5,11 +5,11 @@
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"lastModified": 1726560853,
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
"type": "github"
},
"original": {
@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1737885589,
"narHash": "sha256-Zf0hSrtzaM1DEz8//+Xs51k/wdSajticVrATqDrfQjg=",
"lastModified": 1730785428,
"narHash": "sha256-Zwl8YgTVJTEum+L+0zVAWvXAGbWAuXHax3KzuejaDyo=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "852ff1d9e153d8875a83602e03fdef8a63f0ecf8",
"rev": "4aa36568d413aca0ea84a1684d2d46f55dbabad7",
"type": "github"
},
"original": {

View File

@@ -18,15 +18,12 @@
buildInputs = with pkgs; [
nodejs_22
nodePackages.pnpm
git
];
shellHook = ''
echo ""
echo "Welcome to the Jazz development environment!"
echo "Run 'pnpm install' to install the dependencies."
echo ""
'';
};
});
}
}

View File

@@ -1,11 +1,11 @@
import { clsx } from "clsx";
import Link from "next/link";
import { forwardRef } from "react";
import { Icon } from "./Icon";
import { Icon } from "../atoms/Icon";
import { Spinner } from "./Spinner";
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "tertiary" | "destructive" | "plain";
variant?: "primary" | "secondary" | "tertiary";
size?: "sm" | "md" | "lg";
href?: string;
newTab?: boolean;
@@ -42,7 +42,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
loading,
loadingText,
icon,
type = "button",
...buttonProps
},
ref,
@@ -59,21 +58,16 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
secondary:
"text-stone-900 border font-medium hover:border-stone-300 hover:dark:border-stone-700 dark:text-white",
tertiary: "text-blue underline underline-offset-4",
destructive:
"bg-red-600 border-red-600 text-white font-medium hover:bg-red-700 hover:border-red-700",
};
const classNames =
variant === "plain"
? className
: clsx(
className,
"inline-flex items-center justify-center gap-2 rounded-lg text-center transition-colors",
"disabled:pointer-events-none disabled:opacity-70",
sizeClasses[size],
variantClasses[variant],
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
);
const classNames = clsx(
className,
"inline-flex items-center justify-center gap-2 rounded-lg text-center transition-colors",
"disabled:pointer-events-none disabled:opacity-70",
sizeClasses[size],
variantClasses[variant],
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
);
if (href) {
return (
@@ -101,7 +95,6 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
{...buttonProps}
disabled={disabled || loading}
className={classNames}
type={type}
>
<ButtonIcon icon={icon} loading={loading} />

View File

@@ -1,13 +0,0 @@
export function Copyright({
className,
companyName = "Garden Computing, Inc.",
}: {
companyName?: string;
className?: string;
}) {
return (
<p className={className}>
© {new Date().getFullYear()} {companyName}
</p>
);
}

View File

@@ -1,48 +0,0 @@
import clsx from "clsx";
type HeadingProps = {
level?: 1 | 2 | 3 | 4 | 5 | 6;
size?: 1 | 2 | 3 | 4 | 5 | 6;
} & React.ComponentPropsWithoutRef<"h1" | "h2" | "h3" | "h4" | "h5" | "h6">;
const classes = {
1: [
"font-display",
"text-stone-950 dark:text-white",
"text-5xl lg:text-6xl",
"mb-3",
"font-medium",
"tracking-tighter",
],
2: [
"font-display",
"text-stone-950 dark:text-white",
"text-2xl md:text-4xl",
"mb-2",
"font-semibold",
"tracking-tight",
],
3: [
"font-display",
"text-stone-950 dark:text-white",
"text-xl md:text-2xl",
"mb-2",
"font-semibold",
"tracking-tight",
],
4: ["text-bold"],
5: [],
6: [],
};
export function Heading({
className,
level = 1,
size: customSize,
...props
}: HeadingProps) {
let Element: `h${typeof level}` = `h${level}`;
const size = customSize || level;
return <Element {...props} className={clsx(classes[size])} />;
}

View File

@@ -1,38 +1,85 @@
import clsx from "clsx";
import { Heading } from "./Heading";
export function H1(
props: React.ComponentPropsWithoutRef<"h1"> & React.PropsWithChildren,
) {
return <Heading level={1} {...props} />;
interface HeadingProps {
children: React.ReactNode;
className?: string;
id?: string;
}
export function H2(
props: React.ComponentPropsWithoutRef<"h2"> & React.PropsWithChildren,
) {
return <Heading level={2} {...props} />;
export function H1({ children, className, id }: HeadingProps) {
return (
<h1
id={id}
className={clsx(
className,
"font-display",
"text-stone-950 dark:text-white",
"text-5xl lg:text-6xl",
"mb-3",
"font-medium",
"tracking-tighter",
)}
>
{children}
</h1>
);
}
export function H3(
props: React.ComponentPropsWithoutRef<"h3"> & React.PropsWithChildren,
) {
return <Heading level={3} {...props} />;
export function H2({ children, className, id }: HeadingProps) {
return (
<h2
id={id}
className={clsx(
className,
"font-display",
"text-stone-950 dark:text-white",
"text-2xl md:text-4xl",
"mb-2",
"font-semibold",
"tracking-tight",
)}
>
{children}
</h2>
);
}
export function H4(
props: React.ComponentPropsWithoutRef<"h4"> & React.PropsWithChildren,
) {
return <Heading level={4} {...props} />;
export function H3({ children, className, id }: HeadingProps) {
return (
<h3
id={id}
className={clsx(
className,
"font-display",
"text-stone-950 dark:text-white",
"text-xl md:text-2xl",
"mb-2",
"font-semibold",
"tracking-tight",
)}
>
{children}
</h3>
);
}
export function H5(
props: React.ComponentPropsWithoutRef<"h5"> & React.PropsWithChildren,
) {
return <Heading level={5} {...props} />;
export function H4({ children, className, id }: HeadingProps) {
return (
<h4 id={id} className={clsx(className, "text-bold")}>
{children}
</h4>
);
}
export function H6(
props: React.ComponentPropsWithoutRef<"h6"> & React.PropsWithChildren,
) {
return <Heading level={6} {...props} />;
export function Kicker({ children, className }: HeadingProps) {
return (
<p
className={clsx(
className,
"uppercase text-blue tracking-widest text-sm font-medium dark:text-stone-400",
)}
>
{children}
</p>
);
}

View File

@@ -1,21 +0,0 @@
import clsx from "clsx";
export function Kicker({
children,
className,
as,
}: React.ComponentPropsWithoutRef<"p"> & {
as?: React.ElementType;
}) {
const Element = as ?? "p";
return (
<Element
className={clsx(
className,
"uppercase text-blue tracking-widest text-sm font-medium dark:text-stone-400",
)}
>
{children}
</Element>
);
}

View File

@@ -1,33 +1,32 @@
import { clsx } from "clsx";
import { forwardRef, useId } from "react";
import { useId } from "react";
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
// label can be hidden with a "label:sr-only" className
interface Props extends React.InputHTMLAttributes<HTMLInputElement> {
// label is required for a11y, but you can hide it with a "label:sr-only" className
label: string;
className?: string;
id?: string;
type?: "text" | "email" | "number";
}
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ label, className, id: customId, ...inputProps }, ref) => {
const generatedId = useId();
const id = customId || generatedId;
const inputClassName = clsx(
"w-full rounded-md border px-3.5 py-2 shadow-sm",
"font-medium text-stone-900",
"dark:text-white dark:bg-stone-925",
);
export function Input(props: Props) {
const { label, id: customId, className, type = "text" } = props;
const generatedId = useId();
const id = customId || generatedId;
const containerClassName = clsx("grid gap-1", className);
const inputClassName = clsx(
"w-full rounded-md border px-3.5 py-2 shadow-sm",
"font-medium text-stone-900",
"dark:text-white",
);
return (
<div className={containerClassName}>
<label htmlFor={id} className="text-stone-600 dark:text-stone-300">
{label}
</label>
const containerClassName = clsx("grid gap-1", className);
<input ref={ref} {...inputProps} id={id} className={inputClassName} />
</div>
);
},
);
return (
<div className={containerClassName}>
<label htmlFor={id} className="text-stone-600 dark:text-stone-300">
{label}
</label>
<input {...props} type={type} id={id} className={inputClassName} />
</div>
);
}

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