Compare commits

...

78 Commits

Author SHA1 Message Date
Guido D'Orsi
b75914add6 Merge remote-tracking branch 'origin/main' into jmsv/581/magic-link-auth 2025-05-23 23:13:52 +02:00
Guido D'Orsi
2f4eada56c Merge pull request #2356 from garden-co/changeset-release/main
Version Packages
2025-05-23 21:37:52 +02:00
github-actions[bot]
709b3432d7 Version Packages 2025-05-23 19:33:52 +00:00
Guido D'Orsi
78a7672401 fix: update lockfile 2025-05-23 21:31:34 +02:00
Guido D'Orsi
bb214f0e8b fix: remove react-dom from the peerDependencies 2025-05-23 21:22:22 +02:00
Guido D'Orsi
8706398f74 Merge pull request #2355 from garden-co/changeset-release/main
Version Packages
2025-05-23 20:31:09 +02:00
github-actions[bot]
1502a6b421 Version Packages 2025-05-23 18:15:41 +00:00
Guido D'Orsi
f167112d99 fix: remove react and react-dom dev dependencies due to npm install issues 2025-05-23 20:12:58 +02:00
Guido D'Orsi
d4d6cb3307 Merge pull request #2354 from garden-co/changeset-release/main
Version Packages
2025-05-23 20:01:42 +02:00
github-actions[bot]
9565cdf323 Version Packages 2025-05-23 18:01:27 +00:00
Guido D'Orsi
dfb21ff0f2 fix: add missing deps for sqlite and secure storage 2025-05-23 19:59:08 +02:00
Guido D'Orsi
98d697fafb fix: remove react and react-native dev dependencies 2025-05-23 19:50:54 +02:00
Guido D'Orsi
9150d53f90 Merge pull request #2353 from garden-co/changeset-release/main
Version Packages
2025-05-23 19:46:39 +02:00
github-actions[bot]
ac7ecc278c Version Packages 2025-05-23 17:46:11 +00:00
Guido D'Orsi
99595bc7e3 fix: remove React Native specific steps only for chat-rn-expo 2025-05-23 19:42:45 +02:00
Guido D'Orsi
f5bae09679 fix: remove React Native specific steps as not required anymore 2025-05-23 19:40:51 +02:00
Guido D'Orsi
7ad7e6b1f1 chore: remove unused var 2025-05-12 12:16:17 +02:00
Guido D'Orsi
1c63bdc674 chore: post-merge fixes 2025-05-10 09:59:59 +02:00
Guido D'Orsi
c535b5d5f3 Merge remote-tracking branch 'origin/main' into jmsv/581/magic-link-auth 2025-05-10 09:58:27 +02:00
Guido D'Orsi
85e927547c Merge pull request #2106 from garden-co/cross-device-account-transfer-simplify
feat: unify handlerPath prop and rename hooks
2025-05-10 09:56:51 +02:00
Guido D'Orsi
a111ebd96b feat: unify handlerPath prop and rename hooks 2025-05-02 17:50:30 +02:00
Guido D'Orsi
238ba97ef6 fix: keep the onLogged reference always fresh 2025-05-02 17:10:03 +02:00
James Vickery
35ab08447f Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-30 10:18:12 +01:00
James Vickery
d614da38db fix example vercel.json 2025-04-30 10:16:13 +01:00
James Vickery
22103a371b fix example title 2025-04-30 10:14:36 +01:00
James Vickery
2a83de5787 changeset 2025-04-29 16:09:12 +01:00
James Vickery
c353ae7199 fix pnpm lock 2025-04-29 12:24:59 +01:00
James Vickery
8cf0180bdc Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-29 12:14:53 +01:00
James Vickery
3d65dd0d2f rename magic link auth to cross-device account transfer 2025-04-29 12:14:41 +01:00
James Vickery
566eecb005 examples 2025-04-25 12:36:47 +01:00
James Vickery
502ddb44a6 simplify docs with example links 2025-04-25 12:16:03 +01:00
James Vickery
16d04cd053 switch if returns on magic link examples to switch/case 2025-04-25 11:28:32 +01:00
James Vickery
b1d102ae77 Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-24 15:20:15 +01:00
James Vickery
be28248bd9 switch todo example to passkey + magic link auth 2025-04-24 14:26:12 +01:00
James Vickery
454a3e5458 add warning if magic link doesn't include hash 2025-04-23 16:59:11 +01:00
James Vickery
54870d089d Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-23 11:32:39 +01:00
James Vickery
1c453726cf docs docs docs 2025-04-23 10:15:59 +01:00
James Vickery
030ac42d57 example tweaks 2025-04-22 17:15:03 +01:00
James Vickery
3f500fcb43 format 2025-04-22 10:33:56 +01:00
James Vickery
8aab2c5ad1 rename provider to source and consumer to target 2025-04-22 10:33:09 +01:00
James Vickery
8096f11f17 docs 2025-04-17 14:54:36 +01:00
James Vickery
b5acd2ab88 example tweaks 2025-04-17 14:54:27 +01:00
James Vickery
892ca12b53 options docstrings 2025-04-17 14:52:26 +01:00
James Vickery
b131d04325 use new jazz colour 2025-04-17 10:13:57 +01:00
James Vickery
55e3277b00 format 2025-04-16 21:12:14 +01:00
James Vickery
03bb9df861 Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-16 17:26:11 +01:00
James Vickery
e0ce0dbe10 lil test tweak: "invalid-link-gets-ignored" 2025-04-16 17:25:04 +01:00
James Vickery
9576efe324 jazz-react-core hooks tests 2025-04-16 17:19:03 +01:00
James Vickery
0082417f76 checkValidUrl for handlers 2025-04-16 17:18:39 +01:00
James Vickery
135d239aa7 better url ref handling 2025-04-16 17:16:36 +01:00
James Vickery
6a80bea57f tidy up imports 2025-04-16 10:36:51 +01:00
Guido D'Orsi
f439b3afc2 fix: use a unique identifier for the magicLink peer 2025-04-15 19:33:10 +02:00
James Vickery
399590598c Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-15 16:13:00 +01:00
James Vickery
c9155b3134 test jazz-tools flows 2025-04-15 16:12:39 +01:00
James Vickery
0499f81d35 Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-14 15:03:15 +01:00
James Vickery
60ccdd0497 url as param on handleFlow 2025-04-14 15:00:56 +01:00
James Vickery
a09ef59fd8 add magic-link to docNavigationItems 2025-04-14 14:59:53 +01:00
James Vickery
9a08df4481 magic link auth cancellable 2025-04-14 10:20:28 +01:00
James Vickery
99ad494585 fix lock 2025-04-10 11:04:51 +01:00
James Vickery
133696f3d4 Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-10 11:02:29 +01:00
James Vickery
6ba97e0427 unified hooks for creating and handling magic link auth 2025-04-10 10:59:44 +01:00
James Vickery
e0c65e240b move flow logic to jazz-tools 2025-04-09 12:18:59 +01:00
James Vickery
a6941ba257 Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-04-07 00:47:21 +01:00
James Vickery
4d30d6dc00 switch from confirm button to confirmation code 2025-04-07 00:46:58 +01:00
James Vickery
4601438890 more example ui improvements 2025-04-01 14:13:04 +01:00
James Vickery
e606bd4c35 init magic link docs 2025-04-01 12:11:47 +01:00
James Vickery
8311901047 bunch of example app niceness 2025-04-01 11:11:40 +01:00
James Vickery
15ab4e9140 fix 0.12 merge on waitForCoValueCondition 2025-04-01 10:38:04 +01:00
James Vickery
0b73ece3d6 Merge branch 'main' of github.com:garden-co/jazz into jmsv/581/magic-link-auth 2025-03-31 17:13:56 +01:00
James Vickery
d2008bf9db example app 2025-03-31 17:06:04 +01:00
James Vickery
54ce017008 confirm step for consumer link and provider handler flow 2025-03-31 17:05:47 +01:00
James Vickery
851b1f9679 fix consumer link and provider handler flow 2025-03-31 16:21:48 +01:00
James Vickery
454a973ab3 nicer api for confirm login 2025-03-31 10:44:43 +01:00
James Vickery
317f143929 temporary agent is working! 2025-03-31 10:43:37 +01:00
James Vickery
fb34f50242 cojson changeset 2025-03-28 17:26:33 +00:00
James Vickery
2fd3373fe7 pass deletePeerStateOnClose through connectedPeers 2025-03-28 17:25:32 +00:00
James Vickery
ba2f833c93 fix lint 2025-03-27 15:51:03 +00:00
James Vickery
6450cf69ba squash 2025-03-27 15:16:00 +00:00
134 changed files with 4326 additions and 2132 deletions

View File

@@ -0,0 +1,5 @@
---
"cojson": patch
---
Pass deletePeerStateOnClose through connectedPeers

View File

@@ -0,0 +1,7 @@
---
"jazz-react-core": minor
"jazz-react": minor
"jazz-tools": minor
---
Cross-Device Account Transfer

View File

@@ -1,5 +1,21 @@
# betterauth
## 0.1.16
### Patch Changes
- jazz-inspector@0.14.13
- jazz-react@0.14.13
- jazz-react-auth-betterauth@0.14.13
## 0.1.15
### Patch Changes
- jazz-inspector@0.14.12
- jazz-react@0.14.12
- jazz-react-auth-betterauth@0.14.12
## 0.1.14
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "betterauth",
"version": "0.1.14",
"version": "0.1.16",
"private": true,
"type": "module",
"scripts": {

View File

@@ -1,5 +1,24 @@
# chat-rn-expo-clerk
## 1.0.136
### Patch Changes
- jazz-expo@0.14.13
## 1.0.135
### Patch Changes
- jazz-expo@0.14.12
## 1.0.134
### Patch Changes
- Updated dependencies [98d697f]
- jazz-expo@0.14.11
## 1.0.133
### Patch Changes

View File

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

View File

@@ -1,5 +1,24 @@
# chat-rn-expo
## 1.0.4
### Patch Changes
- jazz-expo@0.14.13
## 1.0.3
### Patch Changes
- jazz-expo@0.14.12
## 1.0.2
### Patch Changes
- Updated dependencies [98d697f]
- jazz-expo@0.14.11
## 1.0.1
### Patch Changes

View File

@@ -26,6 +26,7 @@
},
"web": {
"favicon": "./assets/favicon.png"
}
},
"plugins": ["expo-secure-store", "expo-sqlite"]
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn-expo",
"version": "1.0.1",
"version": "1.0.4",
"main": "index.ts",
"scripts": {
"build": "expo prebuild",
@@ -16,6 +16,8 @@
"@react-native-community/netinfo": "11.4.1",
"expo": "~53.0.9",
"expo-clipboard": "^7.1.4",
"expo-secure-store": "~14.2.3",
"expo-sqlite": "~15.2.10",
"expo-status-bar": "~2.2.3",
"jazz-expo": "workspace:*",
"jazz-tools": "workspace:*",

View File

@@ -1,5 +1,24 @@
# chat-rn
## 1.0.131
### Patch Changes
- jazz-react-native@0.14.13
## 1.0.130
### Patch Changes
- jazz-react-native@0.14.12
## 1.0.129
### Patch Changes
- Updated dependencies [98d697f]
- jazz-react-native@0.14.11
## 1.0.128
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-rn",
"version": "1.0.128",
"version": "1.0.131",
"main": "index.js",
"scripts": {
"android": "react-native run-android",

View File

@@ -1,5 +1,19 @@
# jazz-example-chat
## 0.0.211
### Patch Changes
- jazz-inspector@0.14.13
- jazz-react@0.14.13
## 0.0.210
### Patch Changes
- jazz-inspector@0.14.12
- jazz-react@0.14.12
## 0.0.209
### Patch Changes

View File

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

View File

@@ -1,5 +1,19 @@
# minimal-auth-clerk
## 0.0.110
### Patch Changes
- jazz-react@0.14.13
- jazz-react-auth-clerk@0.14.13
## 0.0.109
### Patch Changes
- jazz-react@0.14.12
- jazz-react-auth-clerk@0.14.12
## 0.0.108
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "clerk",
"private": true,
"version": "0.0.108",
"version": "0.0.110",
"type": "module",
"scripts": {
"dev": "vite",

View File

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

View File

@@ -0,0 +1,319 @@
# jazz-tailwind-demo-auth-starter
## 0.0.58
### Patch Changes
- jazz-react@0.11.5
- jazz-tools@0.11.5
## 0.0.57
### Patch Changes
- Updated dependencies [57a3dbe]
- Updated dependencies [a717754]
- Updated dependencies [a91f343]
- jazz-tools@0.11.4
- jazz-react@0.11.4
## 0.0.56
### Patch Changes
- jazz-react@0.11.3
- jazz-tools@0.11.3
## 0.0.55
### Patch Changes
- Updated dependencies [6892dc6]
- jazz-tools@0.11.2
- jazz-react@0.11.2
## 0.0.54
### Patch Changes
- jazz-react@0.11.1
## 0.0.53
### Patch Changes
- Updated dependencies [6a96d8b]
- Updated dependencies [a35249a]
- Updated dependencies [b9d194a]
- Updated dependencies [a4713df]
- Updated dependencies [34cbdc3]
- Updated dependencies [f039e8f]
- Updated dependencies [e22de9f]
- jazz-tools@0.11.0
- jazz-react@0.11.0
## 0.0.52
### Patch Changes
- Updated dependencies [2f99de0]
- jazz-tools@0.10.15
- jazz-react@0.10.15
## 0.0.51
### Patch Changes
- Updated dependencies [75211e3]
- jazz-tools@0.10.14
- jazz-react@0.10.14
## 0.0.50
### Patch Changes
- Updated dependencies [07feedd]
- jazz-tools@0.10.13
- jazz-react@0.10.13
## 0.0.49
### Patch Changes
- Updated dependencies [4612e05]
- jazz-tools@0.10.12
- jazz-react@0.10.12
## 0.0.48
### Patch Changes
- jazz-react@0.10.9
## 0.0.47
### Patch Changes
- Updated dependencies [2fb6428]
- jazz-tools@0.10.8
- jazz-react@0.10.8
## 0.0.46
### Patch Changes
- Updated dependencies [1136d9b]
- Updated dependencies [0eed228]
- jazz-react@0.10.7
- jazz-tools@0.10.7
## 0.0.45
### Patch Changes
- Updated dependencies [1d71ca1]
- Updated dependencies [ada802b]
- jazz-react@0.10.6
- jazz-tools@0.10.6
## 0.0.44
### Patch Changes
- Updated dependencies [59ff77e]
- jazz-tools@0.10.5
- jazz-react@0.10.5
## 0.0.43
### Patch Changes
- jazz-react@0.10.4
- jazz-tools@0.10.4
## 0.0.42
### Patch Changes
- Updated dependencies [d8582fc]
- jazz-tools@0.10.3
- jazz-react@0.10.3
## 0.0.41
### Patch Changes
- jazz-react@0.10.2
- jazz-tools@0.10.2
## 0.0.40
### Patch Changes
- Updated dependencies [5a63cba]
- jazz-tools@0.10.1
- jazz-react@0.10.1
## 0.0.39
### Patch Changes
- Updated dependencies [498954f]
- Updated dependencies [d42c2aa]
- Updated dependencies [dd03464]
- Updated dependencies [b426342]
- jazz-react@0.10.0
- jazz-tools@0.10.0
## 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
- Updated dependencies [b01cc1f]
- jazz-tools@0.9.20
- jazz-react@0.9.20
## 0.0.34
### Patch Changes
- jazz-react@0.9.19
- jazz-tools@0.9.19
## 0.0.33
### Patch Changes
- jazz-react@0.9.18
- jazz-tools@0.9.18
## 0.0.32
### Patch Changes
- Updated dependencies [c2ca1fe]
- Updated dependencies [1227047]
- jazz-tools@0.9.17
- jazz-react@0.9.17
## 0.0.31
### Patch Changes
- Updated dependencies [24b3b6a]
- jazz-tools@0.9.16
- jazz-react@0.9.16
## 0.0.30
### Patch Changes
- Updated dependencies [7491711]
- jazz-tools@0.9.15
- jazz-react@0.9.15
## 0.0.29
### Patch Changes
- Updated dependencies [3df93cc]
- jazz-tools@0.9.14
- jazz-react@0.9.14
## 0.0.28
### Patch Changes
- jazz-react@0.9.13
- jazz-tools@0.9.13
## 0.0.27
### Patch Changes
- jazz-react@0.9.12
- jazz-tools@0.9.12
## 0.0.26
### Patch Changes
- jazz-react@0.9.11
- jazz-tools@0.9.11
## 0.0.25
### Patch Changes
- Updated dependencies [5e83864]
- jazz-react@0.9.10
- jazz-tools@0.9.10
## 0.0.24
### Patch Changes
- Updated dependencies [8eb9247]
- jazz-tools@0.9.9
- jazz-react@0.9.9
## 0.0.23
### Patch Changes
- Updated dependencies [d1d773b]
- jazz-tools@0.9.8
- jazz-react@0.9.8
## 0.0.22
### Patch Changes
- jazz-react@0.9.4
## 0.0.21
### Patch Changes
- Updated dependencies [1b71969]
- jazz-react@0.9.1
- jazz-tools@0.9.1
## 0.0.20
### Patch Changes
- Updated dependencies [956a4d1]
- Updated dependencies [8eda792]
- jazz-react@0.9.0
- jazz-tools@0.9.0
## 0.0.19
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react@0.8.51

View File

@@ -0,0 +1,50 @@
# Jazz React starter with Tailwind and Demo Auth
A minimal starter template for building apps with **[Jazz](https://jazz.tools)**, React, TailwindCSS, and Demo Auth.
## Creating an app
Create a new Jazz app.
```bash
npx create-jazz-app@latest
```
Then select "React + Jazz + Demo Auth + Tailwind".
## Running locally
Install dependencies:
```bash
npm i
# or
yarn
```
Then, run the development server:
```bash
npm run dev
# or
yarn dev
```
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
## Learning Jazz
You can start by playing with the form, adding a new field in [./src/schema.ts](./src/schema.ts),
and seeing how easy it is to structure your data, and perform basic operations.
To learn more, check out the [full tutorial](https://jazz.tools/docs/react/guide).
## 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 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 adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/main.tsx](./src/main.tsx).

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en" class="h-full">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jazz | React + Cross-Device Account Transfer + Tailwind</title>
</head>
<body class="h-full flex flex-col">
<div id="root" class="align-self-center flex-1"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,38 @@
{
"name": "cross-device-account-transfer",
"private": true,
"version": "0.0.58",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
},
"dependencies": {
"input-otp": "^1.4.2",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*",
"qrcode": "^1.5.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router": "^6.16.0"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/qrcode": "^1.5.1",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"autoprefixer": "^10.4.20",
"globals": "^15.11.0",
"is-ci": "^3.0.1",
"postcss": "^8.4.27",
"tailwindcss": "^3.4.17",
"typescript": "~5.6.2",
"vite": "^6.0.11"
}
}

View File

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

View File

@@ -0,0 +1,23 @@
import { Route, Routes } from "react-router";
import CrossDeviceAccountTransferHandlerSourcePage from "./components/pages/CrossDeviceAccountTransferHandlerSource.tsx";
import CrossDeviceAccountTransferHandlerTargetPage from "./components/pages/CrossDeviceAccountTransferHandlerTarget.tsx";
import HomePage from "./components/pages/HomePage.tsx";
function App() {
return (
<Routes>
<Route path="/" element={<HomePage />} />
<Route
path="/accept-account-transfer/:transferId/:inviteSecret"
element={<CrossDeviceAccountTransferHandlerTargetPage />}
/>
<Route
path="/share-current-account/:transferId/:inviteSecret"
element={<CrossDeviceAccountTransferHandlerSourcePage />}
/>
</Routes>
);
}
export default App;

View File

@@ -0,0 +1 @@
export const apiKey = "react-passkey-auth@garden.co";

View File

@@ -0,0 +1,77 @@
"use client";
import { useAccount, usePasskeyAuth } from "jazz-react";
import { useState } from "react";
import { APPLICATION_NAME } from "../main";
import { Button } from "./Button";
import { Card } from "./Card";
import { CreateCrossDeviceAccountTransferAsSource } from "./CreateCrossDeviceAccountTransferAsSource";
import { CreateCrossDeviceAccountTransferAsTarget } from "./CreateCrossDeviceAccountTransferAsTarget";
export function AuthButtons() {
const { logOut } = useAccount();
const auth = usePasskeyAuth({ appName: APPLICATION_NAME });
const [accountTransferFlow, setAccountTransferFlow] = useState(false);
function handleLogOut() {
logOut();
window.history.pushState({}, "", "/");
}
if (auth.state === "signedIn") {
return (
<div className="flex flex-col items-center gap-8">
<div className="flex gap-2 justify-center flex-wrap">
<Button color="destructive" onClick={handleLogOut}>
Log out
</Button>
<Button color="primary" onClick={() => setAccountTransferFlow(true)}>
Get your mobile device logged in
</Button>
</div>
{accountTransferFlow ? (
<Card className="w-full flex flex-col gap-4 items-center text-center">
<CreateCrossDeviceAccountTransferAsSource />
</Card>
) : null}
</div>
);
}
return (
<div className="flex flex-col items-center gap-8">
<div className="flex gap-2 justify-center flex-wrap">
<Button onClick={() => auth.signUp("supercoolusername")}>
Sign up
</Button>
<Button onClick={() => auth.logIn()}>Log in with passkey</Button>
<Button color="primary" onClick={() => setAccountTransferFlow(true)}>
Use mobile device to log in
</Button>
</div>
{accountTransferFlow ? (
<Card className="w-full flex flex-col gap-4 items-center text-center">
<CreateCrossDeviceAccountTransferAsTarget
onLoggedIn={() => {
console.log("logged in!");
setAccountTransferFlow(false);
}}
/>
</Card>
) : (
<p className="text-balance">
You can also use the{" "}
<span className="font-bold">Get your mobile device logged in</span>{" "}
button on an already logged-in device
</p>
)}
</div>
);
}

View File

@@ -0,0 +1,17 @@
import { Button } from "./Button";
export function BackToHomepageContainer({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex flex-col items-center gap-4">
<p>{children}</p>
<a href="/">
<Button color="primary">Back to homepage</Button>
</a>
</div>
);
}

View File

@@ -0,0 +1,18 @@
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
color?: "default" | "primary" | "destructive";
}
const colors = {
default: "bg-zinc-100 hover:bg-zinc-50 text-black border-zinc-200",
primary: "bg-blue-600 hover:bg-blue-500 text-white border-blue-600",
destructive: "bg-red-600 hover:bg-red-500 text-white border-red-600",
};
export function Button({ color = "default", ...props }: ButtonProps) {
return (
<button
{...props}
className={`${colors[color]} transition-colors duration-75 ease-out border rounded-md py-1.5 px-3 text-sm`}
/>
);
}

View File

@@ -0,0 +1,12 @@
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {}
export function Card({ children, ...props }: CardProps) {
return (
<div
{...props}
className={`bg-white rounded-lg border border-zinc-200 p-4 shadow ${props.className}`}
>
{children}
</div>
);
}

View File

@@ -0,0 +1,34 @@
import { OTPInput, REGEXP_ONLY_DIGITS } from "input-otp";
import { useState } from "react";
interface ConfirmationCodeInputProps {
onSubmit: (code: string) => void;
}
export function ConfirmationCodeForm({ onSubmit }: ConfirmationCodeInputProps) {
const [value, setValue] = useState("");
return (
<OTPInput
autoFocus
value={value}
onChange={setValue}
onComplete={() => onSubmit(value)}
pattern={REGEXP_ONLY_DIGITS}
maxLength={6}
containerClassName="group flex items-center has-[:disabled]:opacity-30"
render={({ slots }) => (
<div className="flex">
{slots.map((slot, i) => (
<div
key={i}
className={`${slot.isActive ? "outline-4 outline-blue-500" : "outline-0 outline-blue-500/20"} relative w-10 h-14 text-[2rem] flex items-center justify-center transition-all duration-100 border-zinc-200 border-y border-r first:border-l first:rounded-l-md last:rounded-r-md group-hover:border-blue-500/20 group-focus-within:border-blue-500/20 outline`}
>
{slot.char !== null ? <div>{slot.char}</div> : null}
</div>
))}
</div>
)}
/>
);
}

View File

@@ -0,0 +1,87 @@
import { useCreateAccountTransfer } from "jazz-react";
import { useState } from "react";
import { Button } from "./Button";
import { QRCode } from "./QRCode";
export function CreateCrossDeviceAccountTransferAsSource() {
const [link, setLink] = useState<string | undefined>();
const { status, createLink, confirmationCode } = useCreateAccountTransfer({
as: "source",
handlerPath: "/#/accept-account-transfer",
});
const onCreateLink = () => createLink().then(setLink);
switch (status) {
case "idle":
return (
<Button color="primary" onClick={onCreateLink}>
Create QR code
</Button>
);
case "waitingForHandler":
return (
<>
<p>Scan QR code to get your mobile device logged in</p>
{link ? <QRCode url={link} /> : null}
</>
);
case "confirmationCodeGenerated":
return (
<>
<p>Confirmation code:</p>
<p className="font-medium text-3xl tracking-widest">
{confirmationCode ?? "empty"}
</p>
</>
);
case "confirmationCodeCorrect":
return <p>Confirmed! Logging in...</p>;
case "authorized":
return <p>Your device has been logged in!</p>;
case "confirmationCodeIncorrect":
return (
<>
<p>Incorrect confirmation code</p>
<Button color="primary" onClick={onCreateLink}>
Try again
</Button>
</>
);
case "error":
return (
<>
<p>Something went wrong</p>
<Button onClick={onCreateLink}>Try again</Button>
</>
);
case "cancelled":
return (
<>
<p>Login cancelled</p>
<Button color="primary" onClick={onCreateLink}>
Try again
</Button>
</>
);
default:
const check: never = status;
if (check) throw new Error(`Unhandled status: ${check}`);
}
return null;
}

View File

@@ -0,0 +1,95 @@
import { useCreateAccountTransfer } from "jazz-react";
import { useState } from "react";
import { Button } from "./Button";
import { ConfirmationCodeForm } from "./ConfirmationCodeForm";
import { QRCode } from "./QRCode";
interface CreateCrossDeviceAccountTransferAsTargetProps {
onLoggedIn: () => void;
}
export function CreateCrossDeviceAccountTransferAsTarget({
onLoggedIn,
}: CreateCrossDeviceAccountTransferAsTargetProps) {
const [link, setLink] = useState<string | undefined>();
const { status, createLink, sendConfirmationCode } = useCreateAccountTransfer(
{
as: "target",
handlerPath: "/#/share-current-account",
onLoggedIn,
},
);
const onCreateLink = () => createLink().then(setLink);
switch (status) {
case "idle":
return (
<Button color="primary" onClick={onCreateLink}>
Create QR code
</Button>
);
case "waitingForHandler":
return (
<>
<p>Scan QR code to log in</p>
{link ? <QRCode url={link} /> : null}
</>
);
case "confirmationCodeRequired":
return (
<>
<p>Enter the confirmation code displayed on your other device</p>
{sendConfirmationCode ? (
<ConfirmationCodeForm onSubmit={sendConfirmationCode} />
) : null}
</>
);
case "confirmationCodePending":
return <p>Confirming...</p>;
case "authorized":
return <p>Logged in!</p>;
case "confirmationCodeIncorrect":
return (
<>
<p>Incorrect confirmation code!</p>
<Button color="primary" onClick={onCreateLink}>
Try again
</Button>
</>
);
case "error":
return (
<>
<p>Something went wrong</p>
<Button onClick={onCreateLink}>Try again</Button>
</>
);
case "cancelled":
return (
<>
<p>Cancelled</p>
<Button onClick={onCreateLink}>Try again</Button>
</>
);
default:
const check: never = status;
if (check) throw new Error(`Unhandled status: ${check}`);
}
return null;
}

View File

@@ -0,0 +1,21 @@
export function Logo() {
return (
<svg
viewBox="0 0 386 146"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="text-black w-48 mx-auto"
>
<path
d="M176.725 33.865H188.275V22.7H176.725V33.865ZM164.9 129.4H172.875C182.72 129.4 188.275 123.9 188.275 114.22V43.6H176.725V109.545C176.725 115.65 173.975 118.51 167.925 118.51H164.9V129.4ZM245.298 53.28C241.613 45.47 233.363 41.95 222.748 41.95C208.998 41.95 200.748 48.44 197.888 58.615L208.613 61.915C210.648 55.315 216.368 52.565 222.638 52.565C231.933 52.565 235.673 56.415 236.058 64.61C226.433 65.93 216.643 67.195 209.768 69.23C200.583 72.145 195.743 77.865 195.743 86.83C195.743 96.51 202.673 104.65 215.818 104.65C225.443 104.65 232.318 101.35 237.213 94.365V103H247.388V66.425C247.388 61.475 247.168 57.185 245.298 53.28ZM217.853 95.245C210.483 95.245 207.128 91.34 207.128 86.72C207.128 82.045 210.593 79.515 215.323 77.92C220.328 76.435 226.983 75.5 235.948 74.18C235.893 76.93 235.673 80.725 234.738 83.475C233.418 89.25 227.643 95.245 217.853 95.245ZM251.22 103H301.545V92.715H269.535L303.195 45.47V43.6H254.3V53.885H284.935L251.22 101.185V103ZM304.815 103H355.14V92.715H323.13L356.79 45.47V43.6H307.895V53.885H338.53L304.815 101.185V103Z"
fill="currentColor"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M136.179 44.8277C136.179 44.8277 136.179 44.8277 136.179 44.8276V21.168C117.931 28.5527 97.9854 32.6192 77.0897 32.6192C65.1466 32.6192 53.5138 31.2908 42.331 28.7737V51.4076C42.331 51.4076 42.331 51.4076 42.331 51.4076V81.1508C41.2955 80.4385 40.1568 79.8458 38.9405 79.3915C36.1732 78.358 33.128 78.0876 30.1902 78.6145C27.2524 79.1414 24.5539 80.4419 22.4358 82.3516C20.3178 84.2613 18.8754 86.6944 18.291 89.3433C17.7066 91.9921 18.0066 94.7377 19.1528 97.2329C20.2991 99.728 22.2403 101.861 24.7308 103.361C27.2214 104.862 30.1495 105.662 33.1448 105.662H33.1455C33.6061 105.662 33.8365 105.662 34.0314 105.659C44.5583 105.449 53.042 96.9656 53.2513 86.4386C53.2534 86.3306 53.2544 86.2116 53.2548 86.0486H53.2552V85.7149L53.2552 85.5521V82.0762L53.2552 53.1993C61.0533 54.2324 69.0092 54.7656 77.0897 54.7656C77.6696 54.7656 78.2489 54.7629 78.8276 54.7574V110.696C77.792 109.983 76.6533 109.391 75.437 108.936C72.6697 107.903 69.6246 107.632 66.6867 108.159C63.7489 108.686 61.0504 109.987 58.9323 111.896C56.8143 113.806 55.3719 116.239 54.7875 118.888C54.2032 121.537 54.5031 124.283 55.6494 126.778C56.7956 129.273 58.7368 131.405 61.2273 132.906C63.7179 134.406 66.646 135.207 69.6414 135.207C70.1024 135.207 70.3329 135.207 70.5279 135.203C81.0548 134.994 89.5385 126.51 89.7478 115.983C89.7517 115.788 89.7517 115.558 89.7517 115.097V111.621L89.7517 54.3266C101.962 53.4768 113.837 51.4075 125.255 48.2397V80.9017C124.219 80.1894 123.081 79.5966 121.864 79.1424C119.097 78.1089 116.052 77.8384 113.114 78.3653C110.176 78.8922 107.478 80.1927 105.36 82.1025C103.242 84.0122 101.799 86.4453 101.215 89.0941C100.631 91.743 100.931 94.4886 102.077 96.9837C103.223 99.4789 105.164 101.612 107.655 103.112C110.145 104.612 113.073 105.413 116.069 105.413C116.53 105.413 116.76 105.413 116.955 105.409C127.482 105.2 135.966 96.7164 136.175 86.1895C136.179 85.9945 136.179 85.764 136.179 85.3029V81.8271L136.179 44.8277Z"
fill="#146AFF"
/>
</svg>
);
}

View File

@@ -0,0 +1,45 @@
"use client";
import QRCodeGenerator from "qrcode";
import { useEffect, useState } from "react";
import { Button } from "./Button";
interface QRCodeProps {
url: string;
}
export function QRCode({ url }: QRCodeProps) {
const [qr, setQr] = useState<string>();
const [copied, setCopied] = useState(false);
useEffect(() => {
QRCodeGenerator.toDataURL(url)
.then(setQr)
.catch((error) => console.error(error));
}, [url]);
return (
<div className="flex flex-col items-center gap-4">
{qr ? (
<img
src={qr}
alt="QR Code"
className="w-72 h-72 rounded-xl border-2 border-blue-600"
/>
) : (
<div className="w-96 h-96 bg-white rounded-lg flex items-center justify-center">
Loading...
</div>
)}
<Button
onClick={() => {
navigator.clipboard.writeText(url);
setCopied(true);
}}
>
{copied ? "Copied to clipboard!" : "Copy link to clipboard"}
</Button>
</div>
);
}

View File

@@ -0,0 +1,61 @@
import { useAcceptAccountTransfer } from "jazz-react";
import { BackToHomepageContainer } from "../BackToHomepageContainer";
export default function CrossDeviceAccountTransferHandlerSource() {
return (
<main className="container flex flex-col items-center gap-4 px-4 py-8 text-center">
<h1 className="text-xl">Account Transfer Source Handler</h1>
<HandleAccountTransferAsSource />
</main>
);
}
function HandleAccountTransferAsSource() {
const { status, confirmationCode } = useAcceptAccountTransfer({
as: "source",
handlerPath: "/#/share-current-account",
});
switch (status) {
case "idle":
return <p>Loading...</p>;
case "confirmationCodeGenerated":
return (
<>
<p>Confirmation code:</p>
<p className="font-medium text-3xl tracking-widest">
{confirmationCode ?? "empty"}
</p>
<p className="text-red-600">Never share this code with anyone!</p>
</>
);
case "authorized":
return (
<BackToHomepageContainer>
Your device has been logged in!
</BackToHomepageContainer>
);
case "confirmationCodeIncorrect":
return (
<>
<p>Incorrect confirmation code</p>
<p>Please try again</p>
</>
);
case "error":
return (
<BackToHomepageContainer>Something went wrong</BackToHomepageContainer>
);
case "cancelled":
return <BackToHomepageContainer>Login cancelled</BackToHomepageContainer>;
default:
const check: never = status;
if (check) throw new Error(`Unhandled status: ${check}`);
}
}

View File

@@ -0,0 +1,64 @@
import { useAcceptAccountTransfer } from "jazz-react";
import { BackToHomepageContainer } from "../BackToHomepageContainer";
import { ConfirmationCodeForm } from "../ConfirmationCodeForm";
export default function CrossDeviceAccountTransferHandlerTarget() {
return (
<main className="container flex flex-col items-center gap-4 px-4 py-8 text-center">
<h1 className="text-xl">Account Transfer Target Handler</h1>
<HandleAccountTransferAsTarget />
</main>
);
}
function HandleAccountTransferAsTarget() {
const { status, sendConfirmationCode } = useAcceptAccountTransfer({
as: "target",
handlerPath: "/#/accept-account-transfer",
onLoggedIn: () => {
console.log("logged in!");
},
});
switch (status) {
case "idle":
return <p>Loading...</p>;
case "confirmationCodeRequired":
return (
<>
<p>Enter the confirmation code displayed on your other device</p>
{sendConfirmationCode ? (
<ConfirmationCodeForm onSubmit={sendConfirmationCode} />
) : null}
</>
);
case "confirmationCodePending":
return <p>Confirming...</p>;
case "authorized":
return <BackToHomepageContainer>Logged in!</BackToHomepageContainer>;
case "confirmationCodeIncorrect":
return (
<>
<p>Incorrect confirmation code!</p>
<p>Please try again</p>
</>
);
case "error":
return (
<BackToHomepageContainer>Something went wrong</BackToHomepageContainer>
);
case "cancelled":
return <BackToHomepageContainer>Login cancelled</BackToHomepageContainer>;
default:
const check: never = status;
if (check) throw new Error(`Unhandled status: ${check}`);
}
}

View File

@@ -0,0 +1,25 @@
import { useIsAuthenticated } from "jazz-react";
import { AuthButtons } from "../AuthButtons.tsx";
import { Logo } from "../Logo.tsx";
export default function HomePage() {
const isAuthenticated = useIsAuthenticated();
return (
<>
<header>
<nav className="container flex flex-col items-center gap-8 py-8 px-4 text-center">
<p className="text-lg">
{isAuthenticated ? "You're logged in!" : "Sign up or log in"}
</p>
<AuthButtons />
</nav>
</header>
<main className="container flex flex-col gap-8">
<Logo />
</main>
</>
);
}

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -0,0 +1,30 @@
import { JazzProvider } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { HashRouter } from "react-router-dom";
import App from "./App.tsx";
import "./index.css";
import { apiKey } from "./apiKey.ts";
import { JazzAccount } from "./schema.ts";
// We use this to identify the app in the passkey auth
export const APPLICATION_NAME = "Cross-Device Account Transfer Example";
declare module "jazz-react" {
export interface Register {
Account: JazzAccount;
}
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<JazzProvider
sync={{ peer: `wss://cloud.jazz.tools/?key=${apiKey}` }}
AccountSchema={JazzAccount}
>
<HashRouter>
<App />
</HashRouter>
</JazzProvider>
</StrictMode>,
);

View File

@@ -0,0 +1,20 @@
/**
* Learn about schemas here:
* https://jazz.tools/docs/react/schemas/covalues
*/
import { Account, Group, Profile } from "jazz-tools";
export class JazzAccount extends Account {
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
migrate(this: JazzAccount) {
if (this.profile === undefined) {
const group = Group.create();
group.addMember("everyone", "reader"); // The profile info is visible to everyone
this.profile = Profile.create({ name: "supercoolusername" }, group);
}
}
}

View File

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

View File

@@ -0,0 +1,23 @@
import type { Config } from "tailwindcss";
const config: Config = {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
extend: {
container: {
center: true,
padding: {
DEFAULT: "0.75rem",
sm: "1rem",
},
screens: {
lg: "600px",
xl: "600px",
},
},
},
},
plugins: [],
} as const;
export default config;

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
{
"build": {
"env": {
"APP_NAME": "cross-device-account-transfer"
}
},
"ignoreCommand": "node ../../ignore-vercel-build.js"
}

View File

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

View File

@@ -1,5 +1,17 @@
# file-share-svelte
## 0.0.95
### Patch Changes
- jazz-inspector-element@0.14.13
## 0.0.94
### Patch Changes
- jazz-inspector-element@0.14.12
## 0.0.93
### Patch Changes

View File

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

View File

@@ -1,5 +1,19 @@
# jazz-tailwind-demo-auth-starter
## 0.0.50
### Patch Changes
- jazz-inspector@0.14.13
- jazz-react@0.14.13
## 0.0.49
### Patch Changes
- jazz-inspector@0.14.12
- jazz-react@0.14.12
## 0.0.48
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "filestream",
"private": true,
"version": "0.0.48",
"version": "0.0.50",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# form
## 0.1.51
### Patch Changes
- jazz-react@0.14.13
## 0.1.50
### Patch Changes
- jazz-react@0.14.12
## 0.1.49
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# image-upload
## 0.0.107
### Patch Changes
- jazz-react@0.14.13
## 0.0.106
### Patch Changes
- jazz-react@0.14.12
## 0.0.105
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# jazz-example-inspector
## 0.0.160
### Patch Changes
- jazz-inspector@0.14.13
## 0.0.159
### Patch Changes
- jazz-inspector@0.14.12
## 0.0.158
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-inspector-app",
"private": true,
"version": "0.0.158",
"version": "0.0.160",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# multi-cursors
## 0.0.103
### Patch Changes
- jazz-react@0.14.13
## 0.0.102
### Patch Changes
- jazz-react@0.14.12
## 0.0.101
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "multi-cursors",
"private": true,
"version": "0.0.101",
"version": "0.0.103",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,19 @@
# multiauth
## 0.0.51
### Patch Changes
- jazz-react@0.14.13
- jazz-react-auth-clerk@0.14.13
## 0.0.50
### Patch Changes
- jazz-react@0.14.12
- jazz-react-auth-clerk@0.14.12
## 0.0.49
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "multiauth",
"private": true,
"version": "0.0.49",
"version": "0.0.51",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,19 @@
# jazz-example-musicplayer
## 0.0.132
### Patch Changes
- jazz-inspector@0.14.13
- jazz-react@0.14.13
## 0.0.131
### Patch Changes
- jazz-inspector@0.14.12
- jazz-react@0.14.12
## 0.0.130
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.130",
"version": "0.0.132",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# organization
## 0.0.103
### Patch Changes
- jazz-react@0.14.13
## 0.0.102
### Patch Changes
- jazz-react@0.14.12
## 0.0.101
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# minimal-auth-passkey
## 0.0.108
### Patch Changes
- jazz-react@0.14.13
## 0.0.107
### Patch Changes
- jazz-react@0.14.12
## 0.0.106
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# passphrase
## 0.0.105
### Patch Changes
- jazz-react@0.14.13
## 0.0.104
### Patch Changes
- jazz-react@0.14.12
## 0.0.103
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "passphrase",
"private": true,
"version": "0.0.103",
"version": "0.0.105",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# jazz-password-manager
## 0.0.129
### Patch Changes
- jazz-react@0.14.13
## 0.0.128
### Patch Changes
- jazz-react@0.14.12
## 0.0.127
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.127",
"version": "0.0.129",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# jazz-example-pets
## 0.0.227
### Patch Changes
- jazz-react@0.14.13
## 0.0.226
### Patch Changes
- jazz-react@0.14.12
## 0.0.225
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.225",
"version": "0.0.227",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# reactions
## 0.0.107
### Patch Changes
- jazz-react@0.14.13
## 0.0.106
### Patch Changes
- jazz-react@0.14.12
## 0.0.105
### Patch Changes

View File

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

View File

@@ -1,5 +1,17 @@
# richtext-tiptap
## 0.1.20
### Patch Changes
- jazz-react@0.14.13
## 0.1.19
### Patch Changes
- jazz-react@0.14.12
## 0.1.18
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "richtext-tiptap",
"private": true,
"version": "0.1.18",
"version": "0.1.20",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# richtext
## 0.0.97
### Patch Changes
- jazz-react@0.14.13
## 0.0.96
### Patch Changes
- jazz-react@0.14.12
## 0.0.95
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "richtext",
"private": true,
"version": "0.0.95",
"version": "0.0.97",
"type": "module",
"scripts": {
"dev": "vite",

View File

@@ -1,5 +1,17 @@
# jazz-example-todo
## 0.0.226
### Patch Changes
- jazz-react@0.14.13
## 0.0.225
### Patch Changes
- jazz-react@0.14.12
## 0.0.224
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.224",
"version": "0.0.226",
"type": "module",
"scripts": {
"dev": "vite",
@@ -17,6 +17,7 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"input-otp": "^1.4.2",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*",
"lucide-react": "^0.274.0",

View File

@@ -6,12 +6,7 @@ import {
} from "react-router-dom";
import "./index.css";
import {
JazzProvider,
PassphraseAuthBasicUI,
useAcceptInvite,
useAccount,
} from "jazz-react";
import { JazzProvider, useAcceptInvite, useAccount } from "jazz-react";
import React from "react";
import { TodoAccount, TodoProject } from "./1_schema.ts";
@@ -23,8 +18,9 @@ import {
ThemeProvider,
TitleAndLogo,
} from "./basicComponents/index.ts";
import { AccountTransferLinkHandlerPage } from "./components/Auth/AccountTransferLinkHandlerPage";
import { PasskeyAndCrossDeviceAccountTransferAuth } from "./components/Auth/PasskeyAndCrossDeviceAccountTransferAuth";
import { TaskGenerator } from "./components/TaskGenerator.tsx";
import { wordlist } from "./wordlist.ts";
/**
* Walkthrough: The top-level provider `<JazzProvider/>`
@@ -46,9 +42,9 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
}}
AccountSchema={TodoAccount}
>
<PassphraseAuthBasicUI appName={appName} wordlist={wordlist}>
<PasskeyAndCrossDeviceAccountTransferAuth>
{children}
</PassphraseAuthBasicUI>
</PasskeyAndCrossDeviceAccountTransferAuth>
</JazzProvider>
);
}
@@ -90,6 +86,10 @@ export default function App() {
path: "/invite/*",
element: <p>Accepting invite...</p>,
},
{
path: "/accept-account-transfer/:x/:y",
element: <AccountTransferLinkHandlerPage />,
},
{
path: "/generate",
element: <TaskGenerator />,

View File

@@ -0,0 +1,69 @@
import { OTPInput, OTPInputContext } from "input-otp";
import { Dot } from "lucide-react";
import * as React from "react";
import { cn } from "@/basicComponents/lib/utils";
const InputOTP = React.forwardRef<
React.ElementRef<typeof OTPInput>,
React.ComponentPropsWithoutRef<typeof OTPInput>
>(({ className, containerClassName, ...props }, ref) => (
<OTPInput
ref={ref}
containerClassName={cn(
"flex items-center gap-2 has-[:disabled]:opacity-50",
containerClassName,
)}
className={cn("disabled:cursor-not-allowed", className)}
{...props}
/>
));
InputOTP.displayName = "InputOTP";
const InputOTPGroup = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div">
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex items-center", className)} {...props} />
));
InputOTPGroup.displayName = "InputOTPGroup";
const InputOTPSlot = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div"> & { index: number }
>(({ index, className, ...props }, ref) => {
const inputOTPContext = React.useContext(OTPInputContext);
const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
return (
<div
ref={ref}
className={cn(
"relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md",
isActive && "z-10 ring-2 ring-ring ring-offset-background",
className,
)}
{...props}
>
{char}
{hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
</div>
)}
</div>
);
});
InputOTPSlot.displayName = "InputOTPSlot";
const InputOTPSeparator = React.forwardRef<
React.ElementRef<"div">,
React.ComponentPropsWithoutRef<"div">
>(({ ...props }, ref) => (
<div ref={ref} role="separator" {...props}>
<Dot />
</div>
));
InputOTPSeparator.displayName = "InputOTPSeparator";
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };

View File

@@ -0,0 +1,87 @@
import { Button } from "@/basicComponents/ui/button";
import { useAcceptAccountTransfer } from "jazz-react";
import { useNavigate } from "react-router-dom";
export function AccountTransferLinkHandlerPage() {
return (
<main className="container flex flex-col items-center gap-4 px-4 py-8 text-center">
<h1 className="text-2xl font-bold">Get your device logged in</h1>
<div className="flex flex-col items-center gap-4 p-6">
<AccountTransferHandler />
</div>
</main>
);
}
export function AccountTransferHandler() {
const navigate = useNavigate();
const { status, confirmationCode } = useAcceptAccountTransfer({
as: "source",
handlerPath: "/#/accept-account-transfer",
});
const handleBackToHome = () => {
navigate("/");
};
switch (status) {
case "idle":
return <p>Loading...</p>;
case "confirmationCodeGenerated":
return (
<>
<p>Enter this code on your other device</p>
<p className="font-medium text-3xl tracking-widest text-center">
{confirmationCode ?? "empty"}
</p>
<p className="text-red-500">Never share this code with anyone!</p>
</>
);
case "authorized":
return (
<>
<p>Your device has been logged in! 🚀</p>
<Button onClick={handleBackToHome} className="w-full">
Back to home
</Button>
</>
);
case "confirmationCodeIncorrect":
return (
<>
<p>The confirmation code was incorrect - please try again</p>
<Button onClick={handleBackToHome} className="w-full">
Back to home
</Button>
</>
);
case "error":
return (
<>
<p>Oops! Something went wrong</p>
<Button onClick={handleBackToHome} className="w-full">
Back to home
</Button>
</>
);
case "cancelled":
return (
<>
<p>The login process was cancelled</p>
<Button onClick={handleBackToHome} className="w-full">
Back to home
</Button>
</>
);
default:
const check: never = status;
if (check) throw new Error(`Unhandled status: ${check}`);
}
}

View File

@@ -0,0 +1,112 @@
import { Button } from "@/basicComponents/ui/button";
import { useCreateAccountTransfer } from "jazz-react";
import { useEffect, useRef, useState } from "react";
import {
InputOTP,
InputOTPGroup,
InputOTPSlot,
} from "../../basicComponents/ui/input-otp";
import { QRCode } from "./QRCode";
interface CreateAccountTransferLinkProps {
onLoggedIn: () => void;
}
export function CreateAccountTransferLink({
onLoggedIn,
}: CreateAccountTransferLinkProps) {
const [link, setLink] = useState<string | undefined>();
const [confirmationCode, setConfirmationCode] = useState("");
const createdLinkRef = useRef<boolean>(false);
const { status, createLink, sendConfirmationCode } = useCreateAccountTransfer(
{
as: "target",
handlerPath: "/#/accept-account-transfer",
onLoggedIn,
},
);
const onCreateLink = () => createLink().then(setLink);
useEffect(() => {
if (createdLinkRef.current) return;
createdLinkRef.current = true;
onCreateLink();
}, []);
switch (status) {
case "idle":
return <p>Loading...</p>;
case "waitingForHandler":
return (
<>
<p>Scan the QR code from your logged-in mobile device</p>
{link ? <QRCode url={link} /> : null}
</>
);
case "confirmationCodeRequired":
return (
<>
<p>Enter the code displayed on your mobile device</p>
<InputOTP
maxLength={6}
value={confirmationCode}
onChange={(value) => setConfirmationCode(value)}
onComplete={() => sendConfirmationCode?.(confirmationCode)}
>
<InputOTPGroup>
<InputOTPSlot index={0} />
<InputOTPSlot index={1} />
<InputOTPSlot index={2} />
<InputOTPSlot index={3} />
<InputOTPSlot index={4} />
<InputOTPSlot index={5} />
</InputOTPGroup>
</InputOTP>
</>
);
case "confirmationCodePending":
return <p>Checking code...</p>;
case "authorized":
return <p>Authorized! 🚀</p>;
case "confirmationCodeIncorrect":
return (
<>
<p>Incorrect code - please try again</p>
<Button onClick={onCreateLink} className="w-full">
Try again
</Button>
</>
);
case "error":
return (
<>
<p>Oops! Something went wrong</p>
<Button onClick={onCreateLink} className="w-full">
Try again
</Button>
</>
);
case "cancelled":
return (
<>
<p>Authentication cancelled</p>
<Button onClick={onCreateLink} className="w-full">
Try again
</Button>
</>
);
default:
const check: never = status;
if (check) throw new Error(`Unhandled status: ${check}`);
}
}

View File

@@ -0,0 +1,109 @@
import { Button } from "@/basicComponents/ui/button";
import { useIsAuthenticated, usePasskeyAuth } from "jazz-react";
import { KeyRoundIcon, QrCodeIcon } from "lucide-react";
import { useState } from "react";
import { CreateAccountTransferLink } from "./CreateAccountTransferLink";
export const PasskeyAndCrossDeviceAccountTransferAuth = ({
children,
}: {
children: React.ReactNode;
}) => {
const isAuthenticated = useIsAuthenticated();
const [username, setUsername] = useState("");
const [isSignUp, setIsSignUp] = useState(true);
const [error, setError] = useState<string | null>(null);
const [authMethod, setAuthMethod] = useState<
"passkey" | "auth-transfer" | null
>(null);
const passkeyAuth = usePasskeyAuth({
appName: "Jazz Todo App",
});
const handleViewChange = () => {
setIsSignUp(!isSignUp);
setError(null);
};
const handleSignUp = async () => {
try {
await passkeyAuth.signUp(username);
} catch (error) {
setError(error instanceof Error ? error.message : "Unknown error");
}
};
const handleLogin = async () => {
try {
await passkeyAuth.logIn();
} catch (error) {
setError(error instanceof Error ? error.message : "Unknown error");
}
};
if (isAuthenticated) return children;
if (authMethod === "auth-transfer") {
return (
<div className="flex flex-col gap-4 items-center max-w-md mx-auto p-6 text-center">
<h2 className="text-2xl font-bold">Sign in with mobile device</h2>
<CreateAccountTransferLink onLoggedIn={() => setAuthMethod(null)} />
</div>
);
}
return (
<div className="flex flex-col items-center gap-4 p-6 text-center max-w-md mx-auto">
<h2 className="text-2xl font-bold">
{isSignUp ? "Create account" : "Welcome back"}
</h2>
<p>Sign {isSignUp ? "up" : "in"} to access your todos</p>
{isSignUp && (
<div className="w-full flex flex-col items-start">
<label htmlFor="username" className="block text-sm font-medium">
Username
</label>
<input
id="username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Enter your username"
className="w-full px-3 py-2 border rounded-md"
required
/>
</div>
)}
{error ? <div className="text-sm">{error}</div> : null}
<div className="flex flex-col gap-3 w-full">
<Button
onClick={isSignUp ? handleSignUp : handleLogin}
className="w-full"
>
<KeyRoundIcon className="w-4 h-4 mr-2" />
{isSignUp ? "Sign up" : "Login"} with passkey
</Button>
<Button
onClick={() => setAuthMethod("auth-transfer")}
className="w-full"
variant="outline"
>
<QrCodeIcon className="w-4 h-4 mr-2" />
Sign in with mobile device
</Button>
<div className="text-sm">
{isSignUp ? "Already have an account?" : "Don't have an account?"}{" "}
<Button type="button" onClick={handleViewChange} variant="link">
{isSignUp ? "Login" : "Sign up"}
</Button>
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,35 @@
import { Button } from "@/basicComponents";
import QRCodeGenerator from "qrcode";
import { useEffect, useState } from "react";
interface QRCodeProps {
url: string;
}
export function QRCode({ url }: QRCodeProps) {
const [qr, setQr] = useState<string | undefined>();
const [copied, setCopied] = useState(false);
useEffect(() => {
setQr(undefined);
QRCodeGenerator.toDataURL(url)
.then(setQr)
.catch((error) => console.error(error));
}, [url]);
return (
<div className="flex flex-col items-center gap-2">
{qr ? <img src={qr} alt="QR Code" className="w-60 h-60" /> : null}
<Button
variant="link"
onClick={() => {
navigator.clipboard.writeText(url);
setCopied(true);
}}
>
{copied ? "Copied!" : "Copy link"}
</Button>
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,19 @@
# version-history
## 0.0.105
### Patch Changes
- jazz-inspector@0.14.13
- jazz-react@0.14.13
## 0.0.104
### Patch Changes
- jazz-inspector@0.14.12
- jazz-react@0.14.12
## 0.0.103
### Patch Changes

View File

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

View File

@@ -0,0 +1,155 @@
export const metadata = { title: "Cross-Device Account Transfer" };
import { CodeGroup, ContentByFramework } from "@/components/forMdx";
# Cross-Device Account Transfer
Cross-Device Account Transfer can be used to transfer authentication from one device to another, for example using a QR code.
## How it works
Cross-Device Account Transfer supports two flows, where **Desktop** refers to the device that creates and displays the QR code, and **Mobile** refers to the device that scans and handles it:
- **Mobile to Desktop**: Mobile scans the QR code to authenticate the desktop device
- **Desktop to Mobile**: Mobile scans the QR to authenticate itself
Once the QR code has been scanned, the already-authenticated `source` device shows a 6-digit code that the user should enter on the `target` device trying to authenticate.
If the confirmation code is correct, the source device reveals the secret, and the target device uses it to authenticate.
You can pick a flow that suits your use case, or use both!
## Key benefits
- **Portable**: Apps using passkey auth for example can use cross-device account transfer links to authenticate devices where the passkey is not available
- **User-friendly**: Users familiar with WhatsApp, Discord, etc. already know how this works
## Implementation
<ContentByFramework framework="react">
### Mobile to Desktop
We start by creating a link on the **Desktop** device using `useCreateAccountTransfer` as `target`, and handling the flow's `status`:
<CodeGroup>
```tsx twoslash
import { useCreateAccountTransfer } from "jazz-react";
import React, { useState } from "react";
interface CreateAccountTransferAsTargetProps {
onLoggedIn: () => void;
}
const QRCode = (_: { url: string }) => (<></>)
const ConfirmationCodeForm = (_: { onSubmit: (code: string) => void}) => (<></>)
// ---cut---
export function CreateAccountTransferAsTarget({
onLoggedIn,
}: CreateAccountTransferAsTargetProps) {
const [link, setLink] = useState<string | undefined>();
const { status, createLink, sendConfirmationCode } = useCreateAccountTransfer({
as: "target",
onLoggedIn,
});
// Handle the status
}
```
</CodeGroup>
[Full example](https://github.com/garden-co/jazz/blob/main/examples/cross-device-account-transfer/src/components/CreateCrossDeviceAccountTransferAsTarget.tsx)
On the **Mobile** side, we need to host a page at `#/accept-account-transfer`, or provide an alternative handler path to the hooks using the `handlerPath` option.
This is the page we'll land on when the QR code is scanned, handling the link with the `useAcceptAccountTransfer` hook as `source`, and using `status` to display the current state:
<CodeGroup>
```tsx twoslash
import React from "react";
import { useAcceptAccountTransfer } from "jazz-react";
// ---cut---
export function HandleAccountTransferAsSource() {
const { status, confirmationCode } = useAcceptAccountTransfer({
as: "source",
});
// Handle the status
}
```
</CodeGroup>
[Full example](https://github.com/garden-co/jazz/blob/main/examples/cross-device-account-transfer/src/components/pages/CrossDeviceAccountTransferHandlerSource.tsx)
### Desktop to Mobile
This is similar to the **Mobile to Desktop** flow, but the roles are reversed: `useCreateAccountTransfer` on the **Desktop** device should be used as `source`, and `useAcceptAccountTransfer` on the **Mobile** device should be used as `target`.
On the **Desktop** device:
<CodeGroup>
```tsx twoslash
import React, { useState } from "react";
import { useCreateAccountTransfer } from "jazz-react";
const QRCode = (_: { url: string }) => (<></>)
// ---cut---
export function CreateAccountTransferAsSource() {
const [link, setLink] = useState<string | undefined>();
const { status, createLink, confirmationCode } = useCreateAccountTransfer({
as: "source",
});
// Handle the status
}
```
</CodeGroup>
[Full example](https://github.com/garden-co/jazz/blob/main/examples/cross-device-account-transfer/src/components/CreateCrossDeviceAccountTransferAsSource.tsx)
On the **Mobile** device we need to host a page at `#/accept-account-transfer`, or provide an alternative handler path to the hooks using the `handlerPath` option.
This is the page we'll land on when the QR code is scanned, handling the link with the `useAcceptAccountTransfer` hook as `target`, and using `status` to display the current state:
<CodeGroup>
```tsx twoslash
import React from "react";
import { useAcceptAccountTransfer } from "jazz-react";
const ConfirmationCodeForm = (_: { onSubmit: (code: string) => void}) => (<></>)
// ---cut---
export function HandleAccountTransferAsTarget() {
const { status, sendConfirmationCode } = useAcceptAccountTransfer({
as: "target",
handlerPath: "/#/accept-account-transfer",
onLoggedIn: () => {
console.log("logged in!");
},
});
// Handle the status
}
```
</CodeGroup>
[Full example](https://github.com/garden-co/jazz/blob/main/examples/cross-device-account-transfer/src/components/pages/CrossDeviceAccountTransferHandlerTarget.tsx)
</ContentByFramework>
## Examples
- [cross-device-account-transfer](https://cross-device-account-transfer-demo.jazz.tools/): Both flows implemented to demonstrate how everything works - [code](https://github.com/garden-co/jazz/blob/main/examples/cross-device-account-transfer)
- [todo](https://todo-demo.jazz.tools/): Mobile to Desktop flow implemented as a more practical demo - [code](https://github.com/garden-co/jazz/blob/main/examples/todo)
## When to use Cross-Device Account Transfer
Ideal when:
- You need a fallback authentication method alongside passkeys
- Your app is designed to be used on mobile devices
## Limitations and considerations
- **No revocation**: Once the secret has been shared with the target device, it cannot be revoked
- **Requires a camera**: The link handler needs to have a camera to scan the QR code, or another way to get the link

View File

@@ -276,6 +276,11 @@ export const docNavigationItems = [
href: "/docs/authentication/clerk",
done: 100,
},
{
name: "Cross-Device Account Transfer",
href: "/docs/authentication/cross-device-account-transfer",
done: 100,
},
{
name: "Writing your own",
href: "/docs/authentication/writing-your-own",

View File

@@ -9,10 +9,12 @@ export function connectedPeers(
peer1role = "client",
peer2role = "client",
crashOnClose = false,
deletePeerStateOnClose = true,
}: {
peer1role?: Peer["role"];
peer2role?: Peer["role"];
crashOnClose?: boolean;
deletePeerStateOnClose?: boolean;
} = {},
): [Peer, Peer] {
const [from1to2Rx, from1to2Tx] = newQueuePair();
@@ -24,6 +26,7 @@ export function connectedPeers(
outgoing: from1to2Tx,
role: peer2role,
crashOnClose: crashOnClose,
deletePeerStateOnClose,
};
const peer1AsPeer: Peer = {
@@ -32,6 +35,7 @@ export function connectedPeers(
outgoing: from2to1Tx,
role: peer1role,
crashOnClose: crashOnClose,
deletePeerStateOnClose,
};
return [peer1AsPeer, peer2AsPeer];

View File

@@ -59,6 +59,7 @@ export async function createTwoConnectedNodes(
{
peer1role: node2Role,
peer2role: node1Role,
deletePeerStateOnClose: false,
},
);
@@ -624,6 +625,7 @@ export function connectedPeersWithMessagesTracking(opts: {
const [peer1, peer2] = connectedPeers(opts.peer1.id, opts.peer2.id, {
peer1role: opts.peer1.role,
peer2role: opts.peer2.role,
deletePeerStateOnClose: false,
});
const peer1Push = peer1.outgoing.push;

View File

@@ -1,5 +1,11 @@
# create-jazz-app
## 0.1.20
### Patch Changes
- f5bae09: Remove React Native specific steps as not required anymore
## 0.1.19
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.1.19",
"version": "0.1.20",
"bin": {
"create-jazz-app": "./dist/index.js"
},

View File

@@ -248,8 +248,13 @@ async function scaffoldProject({
throw error;
}
const metroConfigPath = `${projectName}/metro.config.js`;
// Additional setup for React Native
if (starterConfig.platform === PLATFORM.REACT_NATIVE) {
if (
starterConfig.platform === PLATFORM.REACT_NATIVE &&
fs.existsSync(metroConfigPath)
) {
const rnSpinner = ora({
text: chalk.blue("Setting up React Native project..."),
spinner: "dots",
@@ -260,15 +265,14 @@ async function scaffoldProject({
execSync(`cd "${projectName}" && npx pod-install`, { stdio: "pipe" });
// Update metro.config.js
const metroConfigPath = `${projectName}/metro.config.js`;
const metroConfig = `
const { getDefaultConfig } = require("expo/metro-config");
const { withNativeWind } = require("nativewind/metro");
const { getDefaultConfig } = require("expo/metro-config");
const { withNativeWind } = require("nativewind/metro");
const config = getDefaultConfig(__dirname);
const config = getDefaultConfig(__dirname);
module.exports = withNativeWind(config, { input: "./global.css" });
`;
module.exports = withNativeWind(config, { input: "./global.css" });
`;
fs.writeFileSync(metroConfigPath, metroConfig);
rnSpinner.succeed(chalk.green("React Native setup completed"));

View File

@@ -1,5 +1,27 @@
# jazz-browser
## 0.14.13
### Patch Changes
- Updated dependencies [bb214f0]
- jazz-react-core@0.14.13
- jazz-react-native-core@0.14.13
## 0.14.12
### Patch Changes
- Updated dependencies [f167112]
- jazz-react-core@0.14.12
- jazz-react-native-core@0.14.12
## 0.14.11
### Patch Changes
- 98d697f: Remove react and react-native dev dependencies
## 0.14.10
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-expo",
"version": "0.14.10",
"version": "0.14.13",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -53,8 +53,6 @@
"devDependencies": {
"@react-native-community/netinfo": "11.4.1",
"expo-secure-store": "~14.2.3",
"react": "18.3.1",
"react-native": "0.79.2",
"typescript": "catalog:"
},
"scripts": {

View File

@@ -1,5 +1,17 @@
# jazz-inspector-element
## 0.14.13
### Patch Changes
- jazz-inspector@0.14.13
## 0.14.12
### Patch Changes
- jazz-inspector@0.14.12
## 0.14.10
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-inspector-element",
"version": "0.14.10",
"version": "0.14.13",
"type": "module",
"main": "./dist/main.js",
"types": "./dist/main.d.ts",

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