Compare commits
75 Commits
create-jaz
...
jazz-react
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
597545741b | ||
|
|
5e42e2a8d8 | ||
|
|
19e2a61398 | ||
|
|
5b06a9d0a7 | ||
|
|
41c4fbb8f9 | ||
|
|
95febc7777 | ||
|
|
7ddff0a550 | ||
|
|
cebc58cd3a | ||
|
|
044626fbc8 | ||
|
|
28fdc43c89 | ||
|
|
2602a49f12 | ||
|
|
8505b0a0b6 | ||
|
|
f2418ff364 | ||
|
|
c81a49ebc3 | ||
|
|
3cd07dca31 | ||
|
|
3763a99e14 | ||
|
|
201e2fd015 | ||
|
|
3e743d56d3 | ||
|
|
b3ff726794 | ||
|
|
f98f88d89b | ||
|
|
56974412df | ||
|
|
adaedd2da2 | ||
|
|
a11472a497 | ||
|
|
3f269c7483 | ||
|
|
81b6b8fd04 | ||
|
|
2939790335 | ||
|
|
48c29435bc | ||
|
|
8668906376 | ||
|
|
6d84e9e83f | ||
|
|
1fea0ef69c | ||
|
|
e4314accb6 | ||
|
|
ee3a4048ef | ||
|
|
9ee1edef3b | ||
|
|
8ab5a09a86 | ||
|
|
2624442903 | ||
|
|
2d199089d5 | ||
|
|
683c170b9d | ||
|
|
ce0e9f0102 | ||
|
|
518406e23d | ||
|
|
4dcbafa058 | ||
|
|
7ae9e01848 | ||
|
|
dd9ecf660d | ||
|
|
4f849050dc | ||
|
|
681600220f | ||
|
|
384e239ad5 | ||
|
|
54e1a09a46 | ||
|
|
392a9c5aac | ||
|
|
478334eabf | ||
|
|
479f9b0113 | ||
|
|
812622b161 | ||
|
|
8b35fae4b6 | ||
|
|
9e2ecb0378 | ||
|
|
6edd061202 | ||
|
|
865d5385e9 | ||
|
|
a998f94789 | ||
|
|
d17eecfe16 | ||
|
|
8ebfbc86db | ||
|
|
abad8e762f | ||
|
|
037e16392e | ||
|
|
49ac65c123 | ||
|
|
3510fb1273 | ||
|
|
bc3efe7ca0 | ||
|
|
3b06a7809e | ||
|
|
58aa04bb10 | ||
|
|
034bd20b39 | ||
|
|
87f82ac7cd | ||
|
|
e8c1a3535a | ||
|
|
b05878962b | ||
|
|
54b4595f23 | ||
|
|
99a2d9be00 | ||
|
|
bf1fbdc8c4 | ||
|
|
14b3aa29c7 | ||
|
|
bfaadb9d67 | ||
|
|
cb770e565d | ||
|
|
a50a9e6c9b |
2
.github/workflows/build-starters.yaml
vendored
2
.github/workflows/build-starters.yaml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2204
|
||||
strategy:
|
||||
matrix:
|
||||
starter: ["react-passkey-auth"]
|
||||
starter: ["react-passkey-auth", "svelte-passkey-auth"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
3
.github/workflows/playwright-homepage.yml
vendored
3
.github/workflows/playwright-homepage.yml
vendored
@@ -22,6 +22,9 @@ jobs:
|
||||
- name: Setup Source Code
|
||||
uses: ./.github/actions/source-code/
|
||||
|
||||
- name: Install root dependencies
|
||||
run: pnpm install && pnpm turbo build
|
||||
|
||||
- name: Install project dependencies
|
||||
run: pnpm install
|
||||
working-directory: ./${{ matrix.project }}
|
||||
|
||||
1
.github/workflows/playwright.yml
vendored
1
.github/workflows/playwright.yml
vendored
@@ -25,6 +25,7 @@ jobs:
|
||||
"examples/organization",
|
||||
"examples/pets",
|
||||
"starters/react-passkey-auth",
|
||||
"starters/svelte-passkey-auth",
|
||||
"packages/jazz-svelte"
|
||||
]
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"**/android/**",
|
||||
"packages/jazz-svelte/**",
|
||||
"examples/*svelte*/**",
|
||||
"starters/*svelte*/**",
|
||||
"examples/jazz-paper-scissors/src/routeTree.gen.ts",
|
||||
"homepage/homepage/**",
|
||||
"**/package.json"
|
||||
|
||||
@@ -5,10 +5,12 @@ This example demonstrates how to integrate [Better Auth](https://www.better-auth
|
||||
## Getting started
|
||||
|
||||
To run this example, you may either:
|
||||
* Clone the Jazz monorepo and run this example from within.
|
||||
* Create a new Jazz project using this example as a template, and run that new project.
|
||||
|
||||
- Clone the Jazz monorepo and run this example from within.
|
||||
- Create a new Jazz project using this example as a template, and run that new project.
|
||||
|
||||
### Setting environment variables
|
||||
|
||||
- `NEXT_PUBLIC_AUTH_BASE_URL`: A URL to a Better Auth server. If undefined, the example will self-host a Better Auth server.
|
||||
- `BETTER_AUTH_SECRET`: The encryption secret used by the self-hosted Better Auth server (required only if `NEXT_PUBLIC_AUTH_BASE_URL` is undefined)
|
||||
- `GITHUB_CLIENT_ID`: The client ID for the GitHub OAuth provider used by the self-hosted Better Auth server (required only if `NEXT_PUBLIC_AUTH_BASE_URL` is undefined)
|
||||
@@ -17,38 +19,64 @@ To run this example, you may either:
|
||||
### Using this example as a template
|
||||
|
||||
1. Create a new Jazz project, and use this example as a template.
|
||||
|
||||
```sh
|
||||
npx create-jazz-app@latest betterauth-app --example betterauth
|
||||
```
|
||||
2. Navigate to the new project and start the development server.
|
||||
|
||||
2. Navigate to the new project and install dependencies.
|
||||
|
||||
```sh
|
||||
cd betterauth-app
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Create a .env file (don't forget to set your [BETTER_AUTH_SECRET](https://www.better-auth.com/docs/installation#set-environment-variables)!)
|
||||
|
||||
```sh
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
4. Start the development server
|
||||
|
||||
```sh
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
https://www.better-auth.com/docs/installation#set-environment-variables
|
||||
|
||||
### Using the monorepo
|
||||
|
||||
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
|
||||
|
||||
Clone the jazz repository.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/garden-co/jazz.git
|
||||
```
|
||||
|
||||
Install and build dependencies.
|
||||
|
||||
```bash
|
||||
pnpm i && npx turbo build
|
||||
```
|
||||
|
||||
Go to the example directory.
|
||||
|
||||
```bash
|
||||
cd jazz/examples/betterauth/
|
||||
```
|
||||
|
||||
Create a .env file (don't forget to set your [BETTER_AUTH_SECRET](https://www.better-auth.com/docs/installation#set-environment-variables)!)
|
||||
|
||||
```sh
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
Start the dev server.
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
@@ -8,9 +8,12 @@ First, install dependencies and build the project:
|
||||
|
||||
```bash
|
||||
pnpm i
|
||||
mv .env.example .env
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
Don't forget to update `VITE_CLERK_PUBLISHABLE_KEY` in `.env` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
|
||||
|
||||
### 2. Inside the `examples/chat-rn-expo-clerk` Directory
|
||||
|
||||
Next, navigate to the specific example project and run the following commands:
|
||||
|
||||
7
examples/chat-rn-expo/.gitignore
vendored
7
examples/chat-rn-expo/.gitignore
vendored
@@ -36,4 +36,9 @@ yarn-error.*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
android/
|
||||
ios/
|
||||
ios/
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/chat-rn/.gitignore
vendored
6
examples/chat-rn/.gitignore
vendored
@@ -73,3 +73,9 @@ yarn-error.log
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
21
examples/chat-svelte/.gitignore
vendored
Normal file
21
examples/chat-svelte/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
node_modules
|
||||
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
/.svelte-kit
|
||||
/build
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
# Vite
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
1
examples/chat-svelte/.npmrc
Normal file
1
examples/chat-svelte/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
engine-strict=true
|
||||
4
examples/chat-svelte/.prettierignore
Normal file
4
examples/chat-svelte/.prettierignore
Normal file
@@ -0,0 +1,4 @@
|
||||
# Package Managers
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
16
examples/chat-svelte/.prettierrc
Normal file
16
examples/chat-svelte/.prettierrc
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.svelte",
|
||||
"options": {
|
||||
"parser": "svelte"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
545
examples/chat-svelte/CHANGELOG.md
Normal file
545
examples/chat-svelte/CHANGELOG.md
Normal file
@@ -0,0 +1,545 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [99a2d9b]
|
||||
- jazz-tools@0.14.25
|
||||
- jazz-browser-media-images@0.14.25
|
||||
- jazz-svelte@0.14.25
|
||||
|
||||
## 0.0.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.30
|
||||
- jazz-tools@0.13.30
|
||||
|
||||
## 0.0.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.29
|
||||
- jazz-tools@0.13.29
|
||||
|
||||
## 0.0.82
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.28
|
||||
- jazz-tools@0.13.28
|
||||
|
||||
## 0.0.81
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.27
|
||||
- jazz-tools@0.13.27
|
||||
|
||||
## 0.0.80
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ff846d9]
|
||||
- jazz-tools@0.13.26
|
||||
- jazz-svelte@0.13.26
|
||||
|
||||
## 0.0.79
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.25
|
||||
- jazz-tools@0.13.25
|
||||
|
||||
## 0.0.78
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ec546b4]
|
||||
- jazz-svelte@0.13.24
|
||||
|
||||
## 0.0.77
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3431076]
|
||||
- Updated dependencies [02a240c]
|
||||
- jazz-svelte@0.13.23
|
||||
- jazz-tools@0.13.23
|
||||
|
||||
## 0.0.76
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.21
|
||||
- jazz-tools@0.13.21
|
||||
|
||||
## 0.0.75
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [439f0fe]
|
||||
- jazz-tools@0.13.20
|
||||
- jazz-svelte@0.13.20
|
||||
|
||||
## 0.0.74
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [80530a4]
|
||||
- jazz-tools@0.13.19
|
||||
- jazz-svelte@0.13.19
|
||||
|
||||
## 0.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [761759c]
|
||||
- jazz-tools@0.13.18
|
||||
- jazz-svelte@0.13.18
|
||||
|
||||
## 0.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.17
|
||||
- jazz-tools@0.13.17
|
||||
|
||||
## 0.0.71
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.16
|
||||
- jazz-tools@0.13.16
|
||||
|
||||
## 0.0.70
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.15
|
||||
- jazz-tools@0.13.15
|
||||
|
||||
## 0.0.69
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.14
|
||||
- jazz-tools@0.13.14
|
||||
|
||||
## 0.0.68
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.13
|
||||
- jazz-tools@0.13.13
|
||||
|
||||
## 0.0.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4547525]
|
||||
- jazz-tools@0.13.12
|
||||
- jazz-svelte@0.13.12
|
||||
|
||||
## 0.0.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [17273a6]
|
||||
- jazz-tools@0.13.11
|
||||
- jazz-svelte@0.13.11
|
||||
|
||||
## 0.0.65
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.10
|
||||
- jazz-tools@0.13.10
|
||||
|
||||
## 0.0.64
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [a6cf01f]
|
||||
- jazz-tools@0.13.9
|
||||
- jazz-svelte@0.13.9
|
||||
|
||||
## 0.0.63
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.7
|
||||
|
||||
## 0.0.62
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.5
|
||||
|
||||
## 0.0.61
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.4
|
||||
|
||||
## 0.0.60
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.3
|
||||
|
||||
## 0.0.59
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.2
|
||||
|
||||
## 0.0.58
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.13.0
|
||||
|
||||
## 0.0.57
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.12.2
|
||||
|
||||
## 0.0.56
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.12.1
|
||||
|
||||
## 0.0.55
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.12.0
|
||||
|
||||
## 0.0.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.11.8
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.11.7
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 1bfa9bb: Removed when="singedUp" from examples apps' Jazz providers. This is a really niche use-case option and can lead to broken-feeling experiences when anonymous users try to load something.
|
||||
- jazz-svelte@0.11.6
|
||||
|
||||
## 0.0.51
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.11.5
|
||||
|
||||
## 0.0.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.11.4
|
||||
|
||||
## 0.0.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.11.3
|
||||
|
||||
## 0.0.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.11.2
|
||||
|
||||
## 0.0.47
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.11.0
|
||||
|
||||
## 0.0.46
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.15
|
||||
|
||||
## 0.0.45
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.14
|
||||
|
||||
## 0.0.44
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.13
|
||||
|
||||
## 0.0.43
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [4612e05]
|
||||
- jazz-svelte@0.10.12
|
||||
|
||||
## 0.0.42
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.9
|
||||
|
||||
## 0.0.41
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.8
|
||||
|
||||
## 0.0.40
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1136d9b]
|
||||
- jazz-svelte@0.10.7
|
||||
|
||||
## 0.0.39
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.6
|
||||
|
||||
## 0.0.38
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.5
|
||||
|
||||
## 0.0.37
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.4
|
||||
|
||||
## 0.0.36
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.3
|
||||
|
||||
## 0.0.35
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.2
|
||||
|
||||
## 0.0.34
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.10.1
|
||||
|
||||
## 0.0.33
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [b426342]
|
||||
- jazz-svelte@0.10.0
|
||||
|
||||
## 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
|
||||
|
||||
- jazz-svelte@0.9.20
|
||||
|
||||
## 0.0.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.19
|
||||
|
||||
## 0.0.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.18
|
||||
|
||||
## 0.0.26
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.17
|
||||
|
||||
## 0.0.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.16
|
||||
|
||||
## 0.0.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.15
|
||||
|
||||
## 0.0.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.14
|
||||
|
||||
## 0.0.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.13
|
||||
|
||||
## 0.0.21
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.12
|
||||
|
||||
## 0.0.20
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.11
|
||||
|
||||
## 0.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.10
|
||||
|
||||
## 0.0.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.9
|
||||
|
||||
## 0.0.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.8
|
||||
|
||||
## 0.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.9.1
|
||||
|
||||
## 0.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9dd8d95]
|
||||
- jazz-svelte@0.9.0
|
||||
|
||||
## 0.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.51
|
||||
|
||||
## 0.0.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.50
|
||||
|
||||
## 0.0.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.49
|
||||
|
||||
## 0.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.48
|
||||
|
||||
## 0.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.45
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.44
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.41
|
||||
|
||||
## 0.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.40
|
||||
|
||||
## 0.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [aa21072]
|
||||
- jazz-svelte@0.8.39
|
||||
|
||||
## 0.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.8.38
|
||||
|
||||
## 0.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.0.4
|
||||
|
||||
## 0.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.0.3
|
||||
|
||||
## 0.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [0e59e65]
|
||||
- jazz-svelte@0.0.2
|
||||
68
examples/chat-svelte/README.md
Normal file
68
examples/chat-svelte/README.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Chat example with Jazz and Svelte
|
||||
|
||||
## Getting started
|
||||
|
||||
You can either
|
||||
|
||||
1. Clone the jazz repository, and run the app within the monorepo.
|
||||
2. Or create a new Jazz project using this example as a template.
|
||||
|
||||
### Using the example as a template
|
||||
|
||||
Create a new Jazz project, and use this example as a template.
|
||||
|
||||
```bash
|
||||
npx create-jazz-app@latest chat-app --example chat-svelte
|
||||
```
|
||||
|
||||
Go to the new project directory.
|
||||
|
||||
```bash
|
||||
cd chat-app
|
||||
```
|
||||
|
||||
Run the dev server.
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Using the monorepo
|
||||
|
||||
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
|
||||
|
||||
Clone the jazz repository.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/garden-co/jazz.git
|
||||
```
|
||||
|
||||
Install and build dependencies.
|
||||
|
||||
```bash
|
||||
pnpm i && npx turbo build
|
||||
```
|
||||
|
||||
Go to the example directory.
|
||||
|
||||
```bash
|
||||
cd jazz/examples/chat-svelte/
|
||||
```
|
||||
|
||||
Start the dev server.
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
|
||||
|
||||
## Questions / problems / feedback
|
||||
|
||||
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.
|
||||
|
||||
## Configuration: sync server
|
||||
|
||||
By default, the example app uses [Jazz Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work.
|
||||
|
||||
You can also run a local sync server by running `npx jazz-run sync`, and setting the `sync` parameter of `JazzProvider` in [./src/routes/+layout.svelte](./src/routes/+layout.svelte) to `{ peer: "ws://localhost:4200" }`.
|
||||
33
examples/chat-svelte/eslint.config.js
Normal file
33
examples/chat-svelte/eslint.config.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import prettier from 'eslint-config-prettier';
|
||||
import js from '@eslint/js';
|
||||
import svelte from 'eslint-plugin-svelte';
|
||||
import globals from 'globals';
|
||||
import ts from 'typescript-eslint';
|
||||
|
||||
export default ts.config(
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
...svelte.configs['flat/recommended'],
|
||||
prettier,
|
||||
...svelte.configs['flat/prettier'],
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte'],
|
||||
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: ts.parser
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
ignores: ['build/', '.svelte-kit/', 'dist/']
|
||||
}
|
||||
);
|
||||
44
examples/chat-svelte/package.json
Normal file
44
examples/chat-svelte/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "chat-svelte",
|
||||
"version": "0.0.85",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"format": "prettier --write .",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format-and-lint": "pnpm run format && pnpm run lint",
|
||||
"format-and-lint:fix": "pnpm run format --write && pnpm run lint --fix",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e:ui": "playwright test --ui"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
"@sveltejs/kit": "^2.21.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@types/eslint": "^9.6.1",
|
||||
"eslint": "^9.27.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"globals": "^15.15.0",
|
||||
"jazz-inspector-element": "workspace:*",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"svelte": "^5.31.1",
|
||||
"svelte-check": "^4.2.1",
|
||||
"typescript": "5.6.2",
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"vite": "6.0.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tailwindcss/vite": "^4.1.7",
|
||||
"jazz-browser-media-images": "workspace:*",
|
||||
"jazz-svelte": "workspace:*",
|
||||
"jazz-tools": "workspace:*",
|
||||
"tailwindcss": "^4.1.7"
|
||||
}
|
||||
}
|
||||
54
examples/chat-svelte/playwright.config.ts
Normal file
54
examples/chat-svelte/playwright.config.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
import isCI from 'is-ci';
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
// import dotenv from 'dotenv';
|
||||
// dotenv.config({ path: path.resolve(__dirname, '.env') });
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: './tests',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: isCI,
|
||||
/* Retry on CI only */
|
||||
retries: isCI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: isCI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: 'http://localhost:5173/',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
permissions: ['clipboard-read', 'clipboard-write']
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] }
|
||||
}
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: [
|
||||
{
|
||||
command: 'pnpm preview --port 5173',
|
||||
url: 'http://localhost:5173/',
|
||||
reuseExistingServer: !isCI
|
||||
}
|
||||
]
|
||||
});
|
||||
1
examples/chat-svelte/src/apiKey.ts
Normal file
1
examples/chat-svelte/src/apiKey.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const apiKey = 'svelte-chat-example@garden.co';
|
||||
21
examples/chat-svelte/src/app.css
Normal file
21
examples/chat-svelte/src/app.css
Normal file
@@ -0,0 +1,21 @@
|
||||
@import 'tailwindcss';
|
||||
@theme {
|
||||
--color-stone-50: oklch(0.988281 0.002 75);
|
||||
--color-stone-75: oklch(0.980563 0.002 75);
|
||||
--color-stone-100: oklch(0.964844 0.002 75);
|
||||
--color-stone-200: oklch(0.917969 0.002 75);
|
||||
--color-stone-300: oklch(0.853516 0.002 75);
|
||||
--color-stone-400: oklch(0.789063 0.002 75);
|
||||
--color-stone-500: oklch(0.726563 0.002 75);
|
||||
--color-stone-600: oklch(0.613281 0.002 75);
|
||||
--color-stone-700: oklch(0.523438 0.002 75);
|
||||
--color-stone-800: oklch(0.412109 0.002 75);
|
||||
--color-stone-900: oklch(0.302734 0.002 75);
|
||||
--color-stone-925: oklch(0.22 0.002 75);
|
||||
--color-stone-950: oklch(0.193359 0.002 75);
|
||||
--color-blue: oklch(0.57 0.2346 261.2);
|
||||
}
|
||||
|
||||
:focus {
|
||||
@apply outline-hidden;
|
||||
}
|
||||
13
examples/chat-svelte/src/app.d.ts
vendored
Normal file
13
examples/chat-svelte/src/app.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
12
examples/chat-svelte/src/app.html
Normal file
12
examples/chat-svelte/src/app.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/jazz-icon.png" type="image/png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex flex-col justify-between w-screen h-screen bg-stone-50 dark:bg-stone-925 dark:text-white"
|
||||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
13
examples/chat-svelte/src/lib/components/BubbleBody.svelte
Normal file
13
examples/chat-svelte/src/lib/components/BubbleBody.svelte
Normal file
@@ -0,0 +1,13 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
let { children, fromMe }: { children: Snippet; fromMe: boolean | undefined } = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="line-clamp-10 text-ellipsis rounded-2xl overflow-hidden max-w-[calc(100%-5rem)] shadow-sm p-1 {fromMe
|
||||
? 'bg-white dark:bg-stone-900 dark:text-white'
|
||||
: 'bg-blue text-white'}"
|
||||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
let { children, fromMe }: { children: Snippet; fromMe: boolean | undefined } = $props();
|
||||
const align = fromMe ? 'items-end' : 'items-start';
|
||||
</script>
|
||||
|
||||
<div class="{align} flex flex-col m-3" role="row">
|
||||
{@render children()}
|
||||
</div>
|
||||
12
examples/chat-svelte/src/lib/components/BubbleImage.svelte
Normal file
12
examples/chat-svelte/src/lib/components/BubbleImage.svelte
Normal file
@@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { ImageDefinition, type Loaded } from 'jazz-tools';
|
||||
import { useProgressiveImg } from '$lib/utils/useProgressiveImage.svelte';
|
||||
let { image }: { image: Loaded<typeof ImageDefinition> } = $props();
|
||||
const { src } = $derived(
|
||||
useProgressiveImg({
|
||||
image
|
||||
})
|
||||
);
|
||||
</script>
|
||||
|
||||
<img class="h-auto max-h-[20rem] max-w-full rounded-t-xl mb-1" {src} alt="" />
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
let { by, madeAt }: { by: string | undefined; madeAt: Date } = $props();
|
||||
</script>
|
||||
|
||||
<div class="text-xs text-neutral-500 mt-1.5">
|
||||
{by} · {madeAt.toLocaleTimeString()}
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { CoPlainText } from 'jazz-tools';
|
||||
|
||||
let { text, className }: { text: CoPlainText | string | null; className?: string } = $props();
|
||||
</script>
|
||||
|
||||
<div class="whitespace-pre-wrap"><p class="px-2 leading-relaxed {className}">{text}</p></div>
|
||||
7
examples/chat-svelte/src/lib/components/ChatBody.svelte
Normal file
7
examples/chat-svelte/src/lib/components/ChatBody.svelte
Normal file
@@ -0,0 +1,7 @@
|
||||
<script lang="ts">
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col-reverse flex-1 overflow-y-auto" role="application">
|
||||
{@render children()}
|
||||
</div>
|
||||
36
examples/chat-svelte/src/lib/components/ChatBubble.svelte
Normal file
36
examples/chat-svelte/src/lib/components/ChatBubble.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import BubbleBody from '$lib/components/BubbleBody.svelte';
|
||||
import BubbleContainer from '$lib/components/BubbleContainer.svelte';
|
||||
import BubbleImage from '$lib/components/BubbleImage.svelte';
|
||||
import BubbleInfo from '$lib/components/BubbleInfo.svelte';
|
||||
import BubbleText from '$lib/components/BubbleText.svelte';
|
||||
import type { Message } from '$lib/schema';
|
||||
import type { Account, Loaded } from 'jazz-tools';
|
||||
let {
|
||||
me,
|
||||
msg
|
||||
}: {
|
||||
me: Loaded<typeof Account> | null | undefined;
|
||||
msg: Loaded<typeof Message>;
|
||||
} = $props();
|
||||
const lastEdit = $derived(msg._edits.text);
|
||||
const fromMe = $derived(lastEdit?.by?.isMe);
|
||||
const { text, image } = $derived(msg);
|
||||
</script>
|
||||
|
||||
{#if me && (!me.canRead(msg) || !msg.text?.toString())}
|
||||
<BubbleContainer fromMe={false}>
|
||||
<BubbleBody fromMe={false}>
|
||||
<BubbleText text="Message not readable" className="italic text-gray-500"></BubbleText>
|
||||
</BubbleBody>
|
||||
</BubbleContainer>
|
||||
{:else}
|
||||
<BubbleContainer {fromMe}>
|
||||
<BubbleBody {fromMe}>
|
||||
{#if image}
|
||||
<BubbleImage {image} />{/if}
|
||||
<BubbleText {text} />
|
||||
</BubbleBody>
|
||||
<BubbleInfo by={lastEdit?.by?.profile?.name} madeAt={lastEdit?.madeAt || new Date()} />
|
||||
</BubbleContainer>
|
||||
{/if}
|
||||
@@ -0,0 +1,3 @@
|
||||
<div class="flex items-center justify-center h-full px-3 text-base text-stone-500 md:text-2xl">
|
||||
Start a conversation below.
|
||||
</div>
|
||||
19
examples/chat-svelte/src/lib/components/ImageIcon.svelte
Normal file
19
examples/chat-svelte/src/lib/components/ImageIcon.svelte
Normal file
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
let { size = 24, strokeWidth = 2 } = $props();
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size}
|
||||
height={size}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width={strokeWidth}
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="lucide lucide-image-icon lucide-image"
|
||||
><rect width="18" height="18" x="3" y="3" rx="2" ry="2" /><circle cx="9" cy="9" r="2" /><path
|
||||
d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"
|
||||
/></svg
|
||||
>
|
||||
32
examples/chat-svelte/src/lib/components/ImageInput.svelte
Normal file
32
examples/chat-svelte/src/lib/components/ImageInput.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import type { ChangeEventHandler } from 'svelte/elements';
|
||||
|
||||
import ImageIcon from '$lib/components/ImageIcon.svelte';
|
||||
let { onImageChange }: { onImageChange?: ChangeEventHandler<HTMLInputElement> } = $props();
|
||||
|
||||
let input = $state<HTMLInputElement>();
|
||||
|
||||
const onUploadClick = () => {
|
||||
input?.click();
|
||||
};
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Send image"
|
||||
title="Send image"
|
||||
onclick={onUploadClick}
|
||||
class="text-stone-500 p-1.5 rounded-full hover:bg-stone-100 hover:text-stone-800 dark:hover:bg-stone-800 dark:hover:text-stone-200 transition-colors"
|
||||
>
|
||||
<ImageIcon size={24} strokeWidth={1.5} />
|
||||
</button>
|
||||
|
||||
<label class="sr-only">
|
||||
Image
|
||||
<input
|
||||
bind:this={input}
|
||||
type="file"
|
||||
accept="image/png, image/jpeg, image/gif"
|
||||
onchange={onImageChange}
|
||||
/>
|
||||
</label>
|
||||
9
examples/chat-svelte/src/lib/components/InputBar.svelte
Normal file
9
examples/chat-svelte/src/lib/components/InputBar.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex gap-1 p-3 mt-auto bg-white border-t shadow-2xl border-stone-200 dark:bg-transparent dark:border-stone-900"
|
||||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
20
examples/chat-svelte/src/lib/components/TextInput.svelte
Normal file
20
examples/chat-svelte/src/lib/components/TextInput.svelte
Normal file
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
let { onSubmit }: { onSubmit: (text: string) => void } = $props();
|
||||
const inputId = $props.id();
|
||||
</script>
|
||||
|
||||
<div class="flex-1">
|
||||
<label class="sr-only" for={inputId}> Type a message and press Enter </label>
|
||||
<input
|
||||
id={inputId}
|
||||
class="block w-full px-3 py-1 border rounded-full border-stone-200 placeholder:text-stone-500 dark:bg-stone-925 dark:text-white dark:border-stone-900"
|
||||
placeholder="Type a message and press Enter"
|
||||
maxLength={2048}
|
||||
type="text"
|
||||
onkeydown={({ key, currentTarget: input }) => {
|
||||
if (key !== 'Enter' || !input.value) return;
|
||||
onSubmit(input.value);
|
||||
input.value = '';
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
9
examples/chat-svelte/src/lib/components/TopBar.svelte
Normal file
9
examples/chat-svelte/src/lib/components/TopBar.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex justify-between w-full gap-2 p-3 bg-white border-b border-stone-200 dark:bg-transparent dark:border-stone-900"
|
||||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
8
examples/chat-svelte/src/lib/schema.ts
Normal file
8
examples/chat-svelte/src/lib/schema.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { co, z } from 'jazz-tools';
|
||||
|
||||
export const Message = co.map({
|
||||
text: co.plainText(),
|
||||
image: z.optional(co.image())
|
||||
});
|
||||
|
||||
export const Chat = co.list(Message);
|
||||
16
examples/chat-svelte/src/lib/utils.ts
Normal file
16
examples/chat-svelte/src/lib/utils.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
const animals = [
|
||||
'elephant',
|
||||
'penguin',
|
||||
'giraffe',
|
||||
'octopus',
|
||||
'kangaroo',
|
||||
'dolphin',
|
||||
'cheetah',
|
||||
'koala',
|
||||
'platypus',
|
||||
'pangolin'
|
||||
];
|
||||
|
||||
export function getRandomUsername() {
|
||||
return `Anonymous ${animals[Math.floor(Math.random() * animals.length)]}`;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { ImageDefinition, type Loaded } from 'jazz-tools';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
export function useProgressiveImg({
|
||||
image,
|
||||
maxWidth,
|
||||
targetWidth
|
||||
}: {
|
||||
image: Loaded<typeof ImageDefinition> | null | undefined;
|
||||
maxWidth?: number;
|
||||
targetWidth?: number;
|
||||
}) {
|
||||
let current = $state<{
|
||||
src?: string;
|
||||
res?: `${number}x${number}` | 'placeholder';
|
||||
}>();
|
||||
const originalSize = $state(image?.originalSize);
|
||||
|
||||
const unsubscribe = image?.subscribe({}, (update: Loaded<typeof ImageDefinition>) => {
|
||||
const highestRes = ImageDefinition.highestResAvailable(update, { maxWidth, targetWidth });
|
||||
if (highestRes) {
|
||||
if (highestRes.res !== current?.res) {
|
||||
const blob = highestRes.stream.toBlob();
|
||||
if (blob) {
|
||||
const blobURI = URL.createObjectURL(blob);
|
||||
current = { src: blobURI, res: highestRes.res };
|
||||
|
||||
setTimeout(() => URL.revokeObjectURL(blobURI), 200);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current = {
|
||||
src: update?.placeholderDataURL,
|
||||
res: 'placeholder'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(() => () => {
|
||||
unsubscribe?.();
|
||||
});
|
||||
|
||||
return {
|
||||
get src() {
|
||||
return current?.src;
|
||||
},
|
||||
get res() {
|
||||
return current?.res;
|
||||
},
|
||||
|
||||
originalSize
|
||||
};
|
||||
}
|
||||
32
examples/chat-svelte/src/routes/+layout.svelte
Normal file
32
examples/chat-svelte/src/routes/+layout.svelte
Normal file
@@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { JazzProvider } from 'jazz-svelte';
|
||||
import 'jazz-inspector-element';
|
||||
import { page } from '$app/state';
|
||||
import { apiKey } from '../apiKey';
|
||||
import { getRandomUsername } from '$lib/utils';
|
||||
let { children } = $props();
|
||||
const defaultProfileName = getRandomUsername();
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Jazz Chat Example</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="h-full bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-925">
|
||||
<JazzProvider
|
||||
sync={{
|
||||
peer: `wss://cloud.jazz.tools/?key=${apiKey}`
|
||||
}}
|
||||
{defaultProfileName}
|
||||
>
|
||||
{@render children?.()}
|
||||
</JazzProvider>
|
||||
<jazz-inspector></jazz-inspector>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
:global(html, body) {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
16
examples/chat-svelte/src/routes/+page.svelte
Normal file
16
examples/chat-svelte/src/routes/+page.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import { Chat } from '$lib/schema';
|
||||
import { AccountCoState } from 'jazz-svelte';
|
||||
import { Account, Group } from 'jazz-tools';
|
||||
|
||||
const account = new AccountCoState(Account);
|
||||
const me = $derived(account.current);
|
||||
$effect(() => {
|
||||
if (!me) return;
|
||||
const group = Group.create();
|
||||
group.addMember('everyone', 'writer');
|
||||
const chat = Chat.create([], group);
|
||||
goto(`/chat/${chat.id}`);
|
||||
});
|
||||
</script>
|
||||
116
examples/chat-svelte/src/routes/chat/[id]/+page.svelte
Normal file
116
examples/chat-svelte/src/routes/chat/[id]/+page.svelte
Normal file
@@ -0,0 +1,116 @@
|
||||
<script lang="ts">
|
||||
import { createImage } from 'jazz-browser-media-images';
|
||||
import { AccountCoState, CoState } from 'jazz-svelte';
|
||||
import { Account, CoPlainText, type ID } from 'jazz-tools';
|
||||
|
||||
import { page } from '$app/state';
|
||||
|
||||
import { Chat, Message } from '$lib/schema';
|
||||
|
||||
import AppContainer from '$lib/components/AppContainer.svelte';
|
||||
import ChatBody from '$lib/components/ChatBody.svelte';
|
||||
import ChatBubble from '$lib/components/ChatBubble.svelte';
|
||||
import EmptyChatMessage from '$lib/components/EmptyChatMessage.svelte';
|
||||
import ImageInput from '$lib/components/ImageInput.svelte';
|
||||
import InputBar from '$lib/components/InputBar.svelte';
|
||||
import TopBar from '$lib/components/TopBar.svelte';
|
||||
import TextInput from '$lib/components/TextInput.svelte';
|
||||
|
||||
const chatId = $derived(page.params.id) as ID<typeof Chat>;
|
||||
const chat = $derived(
|
||||
new CoState(Chat, chatId, {
|
||||
resolve: {
|
||||
$each: {
|
||||
text: true
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
const account = new AccountCoState(Account, {
|
||||
resolve: {
|
||||
profile: true
|
||||
}
|
||||
});
|
||||
const me = $derived(account.current);
|
||||
let showNLastMessages = $state(30);
|
||||
const sendImage = (event: Event & { currentTarget: HTMLInputElement }) => {
|
||||
const file = event.currentTarget.files?.[0];
|
||||
|
||||
if (!file || !chat.current) return;
|
||||
|
||||
if (file.size > 5000000) {
|
||||
alert('Please upload an image less than 5MB.');
|
||||
return;
|
||||
}
|
||||
|
||||
createImage(file, { owner: chat.current._owner }).then((image) => {
|
||||
if (!chat.current) return;
|
||||
chat.current.push(
|
||||
Message.create(
|
||||
{
|
||||
text: CoPlainText.create(file.name, chat.current._owner),
|
||||
image: image
|
||||
},
|
||||
chat.current._owner
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<AppContainer>
|
||||
<TopBar>
|
||||
<input
|
||||
type="text"
|
||||
value={me?.profile?.name ?? ''}
|
||||
class="bg-transparent"
|
||||
onchange={(e) => {
|
||||
if (!me?.profile) return;
|
||||
const target = e.target as HTMLInputElement;
|
||||
me.profile.name = target.value;
|
||||
}}
|
||||
placeholder="Set username"
|
||||
/>
|
||||
<button
|
||||
onclick={() => {
|
||||
account.logOut();
|
||||
window.location.reload(); // Otherwise the provider will not update with default profile name
|
||||
}}>Log out</button
|
||||
>
|
||||
</TopBar>
|
||||
{#if !chat}
|
||||
<div class="flex items-center justify-center flex-1">Loading...</div>
|
||||
{:else}
|
||||
<ChatBody>
|
||||
{#if chat.current && chat.current.length > 0}
|
||||
{#each chat.current.slice(-showNLastMessages).reverse() as msg (msg.id)}
|
||||
<ChatBubble {me} {msg} />
|
||||
{/each}
|
||||
{:else}
|
||||
<EmptyChatMessage />
|
||||
{/if}
|
||||
{#if chat.current && chat.current.length > showNLastMessages}
|
||||
<button
|
||||
class="block px-4 py-1 mx-auto my-2 border rounded"
|
||||
onclick={() => (showNLastMessages += 10)}
|
||||
>
|
||||
Show more
|
||||
</button>
|
||||
{/if}
|
||||
</ChatBody>
|
||||
<InputBar>
|
||||
<ImageInput onImageChange={sendImage} />
|
||||
<TextInput
|
||||
onSubmit={(text: string) => {
|
||||
if (!chat.current) return;
|
||||
chat.current.push(
|
||||
Message.create(
|
||||
{ text: CoPlainText.create(text, chat.current._owner) },
|
||||
chat.current._owner
|
||||
)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</InputBar>
|
||||
{/if}
|
||||
</AppContainer>
|
||||
BIN
examples/chat-svelte/static/jazz-icon.png
Normal file
BIN
examples/chat-svelte/static/jazz-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 KiB |
18
examples/chat-svelte/svelte.config.js
Normal file
18
examples/chat-svelte/svelte.config.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://svelte.dev/docs/kit/integrations
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
40
examples/chat-svelte/tests/chat.spec.ts
Normal file
40
examples/chat-svelte/tests/chat.spec.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { expect } from 'vitest';
|
||||
import { ChatPage } from './pages/ChatPage';
|
||||
import { test } from '@playwright/test';
|
||||
|
||||
test('chat works between two windows', async ({ page: marioPage, browser }) => {
|
||||
await marioPage.goto('/');
|
||||
const context = await browser.newContext();
|
||||
const luigiPage = await context.newPage();
|
||||
|
||||
await marioPage.waitForURL('/chat/**');
|
||||
const roomUrl = marioPage.url();
|
||||
await luigiPage.goto(roomUrl);
|
||||
|
||||
const marioChat = new ChatPage(marioPage);
|
||||
const luigiChat = new ChatPage(luigiPage);
|
||||
|
||||
|
||||
await marioChat.setUsername('Mario');
|
||||
|
||||
const message1ByMario = 'Hello Luigi, are you ready to save the princess?';
|
||||
|
||||
await marioChat.sendMessage(message1ByMario);
|
||||
await marioChat.expectMessageRow(message1ByMario);
|
||||
|
||||
const roomURL = marioPage.url();
|
||||
await luigiPage.goto(roomURL);
|
||||
|
||||
await luigiChat.setUsername('Luigi');
|
||||
|
||||
await luigiChat.expectMessageRow(message1ByMario);
|
||||
|
||||
const message2ByLuigi = "No, I'm not ready yet. I'm still trying to find the key to the castle.";
|
||||
|
||||
await luigiChat.sendMessage(message2ByLuigi);
|
||||
await luigiChat.expectMessageRow(message2ByLuigi);
|
||||
|
||||
await marioChat.expectMessageRow(message1ByMario);
|
||||
await luigiChat.expectMessageRow(message2ByLuigi);
|
||||
await context.close();
|
||||
});
|
||||
41
examples/chat-svelte/tests/pages/ChatPage.ts
Normal file
41
examples/chat-svelte/tests/pages/ChatPage.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { type Locator, type Page, expect } from '@playwright/test';
|
||||
|
||||
export class ChatPage {
|
||||
readonly page: Page;
|
||||
readonly messageInput: Locator;
|
||||
readonly logoutButton: Locator;
|
||||
readonly usernameInput: Locator;
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.messageInput = page.getByRole('textbox', {
|
||||
name: 'Type a message and press Enter'
|
||||
});
|
||||
this.logoutButton = page.getByRole('button', {
|
||||
name: 'Log out'
|
||||
});
|
||||
this.usernameInput = page.getByPlaceholder('Set username');
|
||||
}
|
||||
|
||||
async setUsername(username: string) {
|
||||
await this.usernameInput.fill(username);
|
||||
}
|
||||
|
||||
async sendMessage(message: string) {
|
||||
await this.messageInput.fill(message);
|
||||
await this.messageInput.press('Enter');
|
||||
}
|
||||
|
||||
async expectMessageRow(message: string) {
|
||||
await expect(this.page.getByText(message)).toBeVisible();
|
||||
}
|
||||
|
||||
async expectUserNameNotToBeAnonymousUser() {
|
||||
await expect(this.usernameInput).not.toHaveValue(/anonymous user/i);
|
||||
await expect(this.usernameInput).toHaveValue(/^Anonymous \w+/);
|
||||
}
|
||||
|
||||
async logout() {
|
||||
await this.logoutButton.click();
|
||||
await this.page.goto('/');
|
||||
}
|
||||
}
|
||||
19
examples/chat-svelte/tsconfig.json
Normal file
19
examples/chat-svelte/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
}
|
||||
7
examples/chat-svelte/vite.config.ts
Normal file
7
examples/chat-svelte/vite.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit()]
|
||||
});
|
||||
7
examples/chat-vue/.gitignore
vendored
7
examples/chat-vue/.gitignore
vendored
@@ -1 +1,6 @@
|
||||
dist
|
||||
dist
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/chat/.gitignore
vendored
6
examples/chat/.gitignore
vendored
@@ -29,3 +29,9 @@ sync-db/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
@@ -7,23 +7,34 @@ Live version: [https://clerk-demo.jazz.tools](https://clerk-demo.jazz.tools)
|
||||
## Getting started
|
||||
|
||||
You can either
|
||||
|
||||
1. Clone the jazz repository, and run the app within the monorepo.
|
||||
2. Or create a new Jazz project using this example as a template.
|
||||
|
||||
|
||||
### Using the example as a template
|
||||
|
||||
Create a new Jazz project, and use this example as a template.
|
||||
|
||||
```bash
|
||||
npx create-jazz-app@latest clerk-app --example clerk
|
||||
```
|
||||
|
||||
Go to the new project directory.
|
||||
|
||||
```bash
|
||||
cd clerk-app
|
||||
```
|
||||
|
||||
Rename .env.example to .env
|
||||
|
||||
```bash
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
Update `VITE_CLERK_PUBLISHABLE_KEY` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
|
||||
|
||||
Run the dev server.
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
@@ -33,21 +44,33 @@ npm run dev
|
||||
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
|
||||
|
||||
Clone the jazz repository.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/garden-co/jazz.git
|
||||
```
|
||||
|
||||
Install and build dependencies.
|
||||
|
||||
```bash
|
||||
pnpm i && npx turbo build
|
||||
```
|
||||
|
||||
Go to the example directory.
|
||||
|
||||
```bash
|
||||
cd jazz/examples/clerk/
|
||||
```
|
||||
|
||||
Rename .env.example to .env
|
||||
|
||||
```bash
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
Update `VITE_CLERK_PUBLISHABLE_KEY` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
|
||||
|
||||
Start the dev server.
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
@@ -57,4 +80,3 @@ Open [http://localhost:5173](http://localhost:5173) with your browser to see the
|
||||
## 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.
|
||||
|
||||
|
||||
8
examples/filestream/.gitignore
vendored
8
examples/filestream/.gitignore
vendored
@@ -25,4 +25,10 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
playwright-report
|
||||
playwright-report
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/form/.gitignore
vendored
6
examples/form/.gitignore
vendored
@@ -25,3 +25,9 @@ dist-ssr
|
||||
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/image-upload/.gitignore
vendored
6
examples/image-upload/.gitignore
vendored
@@ -22,3 +22,9 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
8
examples/inspector/.gitignore
vendored
8
examples/inspector/.gitignore
vendored
@@ -23,4 +23,10 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
sync-db/
|
||||
sync-db/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/jazz-nextjs/.gitignore
vendored
6
examples/jazz-nextjs/.gitignore
vendored
@@ -39,3 +39,9 @@ yarn-error.log*
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/jazz-paper-scissors/.gitignore
vendored
6
examples/jazz-paper-scissors/.gitignore
vendored
@@ -1,3 +1,7 @@
|
||||
dist
|
||||
|
||||
.env
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
VITE_CURSOR_FEED_ID=multi-cursors-250425-1708
|
||||
VITE_GROUP_ID=co_zXE8C8sd9QxEbxnt3neRvFRPFUc
|
||||
VITE_OLD_CURSOR_AGE_SECONDS=5
|
||||
7
examples/multi-cursors/.gitignore
vendored
7
examples/multi-cursors/.gitignore
vendored
@@ -25,4 +25,9 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
playwright-report
|
||||
playwright-report
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
@@ -6,58 +6,80 @@ Live version: [https://multi-cursors.demo.jazz.tools/](https://multi-cursors.dem
|
||||
## Getting started
|
||||
|
||||
You can either
|
||||
|
||||
1. Clone the jazz repository, and run the app within the monorepo.
|
||||
2. Or create a new Jazz project using this example as a template.
|
||||
|
||||
|
||||
### Using the example as a template
|
||||
|
||||
Create a new Jazz project, and use this example as a template.
|
||||
|
||||
```bash
|
||||
npx create-jazz-app@latest multi-cursors-app --example multi-cursors
|
||||
```
|
||||
|
||||
Go to the new project directory.
|
||||
|
||||
```bash
|
||||
cd multi-cursors-app
|
||||
```
|
||||
|
||||
Run the dev server.
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
If you want to persist the cursors between server restarts, you'll need to update your .env file. Check your console logs for the correct values to add to your .env file.
|
||||
|
||||
```
|
||||
VITE_CURSOR_FEED_ID=
|
||||
VITE_GROUP_ID=
|
||||
VITE_OLD_CURSOR_AGE_SECONDS=5
|
||||
```
|
||||
|
||||
### Using the monorepo
|
||||
|
||||
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
|
||||
|
||||
Clone the jazz repository.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/garden-co/jazz.git
|
||||
```
|
||||
|
||||
Install and build dependencies.
|
||||
|
||||
```bash
|
||||
pnpm i && npx turbo build
|
||||
```
|
||||
|
||||
Go to the example directory.
|
||||
|
||||
```bash
|
||||
cd jazz/examples/multi-cursors/
|
||||
```
|
||||
|
||||
Start the dev server.
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
|
||||
|
||||
If you want to persist the cursors between server restarts, you'll need to update your .env file. Check your console logs for the correct values to add to your .env file.
|
||||
|
||||
```
|
||||
VITE_CURSOR_FEED_ID=
|
||||
VITE_GROUP_ID=
|
||||
VITE_OLD_CURSOR_AGE_SECONDS=5
|
||||
```
|
||||
|
||||
## Questions / problems / feedback
|
||||
|
||||
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.
|
||||
|
||||
|
||||
## Configuration: sync server
|
||||
|
||||
By default, the example app uses [Jazz Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work.
|
||||
|
||||
@@ -5,19 +5,35 @@ This example demonstrates using Jazz with multiple authentication methods; in th
|
||||
## Getting started
|
||||
|
||||
To run this example, you may either:
|
||||
* Clone the Jazz monorepo and run this example from within.
|
||||
* Create a new Jazz project using this example as a template, and run that new project.
|
||||
|
||||
- Clone the Jazz monorepo and run this example from within.
|
||||
- Create a new Jazz project using this example as a template, and run that new project.
|
||||
|
||||
### Using this example as a template
|
||||
|
||||
1. Create a new Jazz project, and use this example as a template.
|
||||
|
||||
```bash
|
||||
npx create-jazz-app@latest multiauth-app --example multiauth
|
||||
```
|
||||
2. Navigate to the new project and start the development server.
|
||||
|
||||
2. Navigate to the new project.
|
||||
|
||||
```bash
|
||||
cd multiauth-app
|
||||
```
|
||||
|
||||
3. Rename .env.example to .env
|
||||
|
||||
```bash
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
4. Update `VITE_CLERK_PUBLISHABLE_KEY` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
|
||||
|
||||
5. Run the development server
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
@@ -26,17 +42,35 @@ npm run dev
|
||||
This requires `pnpm` to be installed; see [https://pnpm.io/installation](https://pnpm.io/installation).
|
||||
|
||||
1. Clone the `jazz` repository.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/garden-co/jazz.git
|
||||
```
|
||||
|
||||
2. Install dependencies.
|
||||
|
||||
```bash
|
||||
cd jazz
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. Navigate to the example and start the development server.
|
||||
|
||||
```bash
|
||||
cd examples/multiauth
|
||||
```
|
||||
|
||||
4. Rename .env.example to .env
|
||||
|
||||
```bash
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
5. Update `VITE_CLERK_PUBLISHABLE_KEY` with your [Publishable Key](https://clerk.com/docs/deployments/clerk-environment-variables#clerk-publishable-and-secret-keys) from Clerk.
|
||||
|
||||
6. Run the development server
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
|
||||
8
examples/music-player/.gitignore
vendored
8
examples/music-player/.gitignore
vendored
@@ -28,4 +28,10 @@ dist-ssr
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
sync-db/
|
||||
sync-db/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
@@ -110,7 +110,7 @@ export async function addTrackToPlaylist(
|
||||
* visible to the Playlist user
|
||||
*/
|
||||
const trackGroup = track._owner;
|
||||
trackGroup.extend(playlist._owner);
|
||||
trackGroup.addMember(playlist._owner);
|
||||
|
||||
playlist.tracks?.push(track);
|
||||
return;
|
||||
@@ -129,7 +129,7 @@ export async function removeTrackFromPlaylist(
|
||||
|
||||
if (track._owner._type === "Group" && playlist._owner._type === "Group") {
|
||||
const trackGroup = track._owner;
|
||||
await trackGroup.revokeExtend(playlist._owner);
|
||||
await trackGroup.removeMember(playlist._owner);
|
||||
|
||||
const index =
|
||||
playlist.tracks?.findIndex(
|
||||
|
||||
@@ -54,7 +54,15 @@ export function AuthModal({ open, onOpenChange }: AuthModalProps) {
|
||||
}
|
||||
onOpenChange(false);
|
||||
} catch (error) {
|
||||
setError(error instanceof Error ? error.message : "Unknown error");
|
||||
if (error instanceof Error) {
|
||||
if (error.cause instanceof Error) {
|
||||
setError(error.cause.message);
|
||||
} else {
|
||||
setError(error.message);
|
||||
}
|
||||
} else {
|
||||
setError("Unknown error");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
8
examples/organization/.gitignore
vendored
8
examples/organization/.gitignore
vendored
@@ -23,4 +23,10 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
playwright-report
|
||||
playwright-report
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
@@ -7,16 +7,9 @@ import { Organization } from "../schema.ts";
|
||||
export function InviteLink({
|
||||
organization,
|
||||
}: { organization: Loaded<typeof Organization> }) {
|
||||
const [inviteLink, setInviteLink] = useState<string>();
|
||||
let [copyCount, setCopyCount] = useState(0);
|
||||
let copied = copyCount > 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (organization) {
|
||||
setInviteLink(createInviteLink(organization, "writer"));
|
||||
}
|
||||
}, [organization.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (copyCount > 0) {
|
||||
let timeout = setTimeout(() => setCopyCount(0), 1000);
|
||||
@@ -27,11 +20,10 @@ export function InviteLink({
|
||||
}, [copyCount]);
|
||||
|
||||
const copyUrl = () => {
|
||||
if (inviteLink) {
|
||||
navigator.clipboard.writeText(inviteLink).then(() => {
|
||||
setCopyCount((count) => count + 1);
|
||||
});
|
||||
}
|
||||
const inviteLink = createInviteLink(organization, "writer");
|
||||
navigator.clipboard.writeText(inviteLink).then(() => {
|
||||
setCopyCount((count) => count + 1);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
6
examples/passkey/.gitignore
vendored
6
examples/passkey/.gitignore
vendored
@@ -22,3 +22,9 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/passphrase/.gitignore
vendored
6
examples/passphrase/.gitignore
vendored
@@ -22,3 +22,9 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/password-manager/.gitignore
vendored
6
examples/password-manager/.gitignore
vendored
@@ -22,3 +22,9 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
8
examples/pets/.gitignore
vendored
8
examples/pets/.gitignore
vendored
@@ -27,4 +27,10 @@ dist-ssr
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
sync-db/
|
||||
sync-db/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/reactions/.gitignore
vendored
6
examples/reactions/.gitignore
vendored
@@ -22,3 +22,9 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
36
examples/richtext-prosekit/.gitignore
vendored
Normal file
36
examples/richtext-prosekit/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
sync-db/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
36
examples/richtext-prosemirror/.gitignore
vendored
Normal file
36
examples/richtext-prosemirror/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
sync-db/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
36
examples/richtext-tiptap/.gitignore
vendored
Normal file
36
examples/richtext-tiptap/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
sync-db/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
8
examples/todo-vue/.gitignore
vendored
8
examples/todo-vue/.gitignore
vendored
@@ -1 +1,7 @@
|
||||
dist
|
||||
dist
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
8
examples/todo/.gitignore
vendored
8
examples/todo/.gitignore
vendored
@@ -23,4 +23,10 @@ dist-ssr
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
sync-db/
|
||||
sync-db/
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
6
examples/version-history/.gitignore
vendored
6
examples/version-history/.gitignore
vendored
@@ -22,3 +22,9 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# env files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
"jazz-react": "link:../../packages/jazz-react",
|
||||
"jazz-tools": "link:../../packages/jazz-tools",
|
||||
"lucide-react": "^0.436.0",
|
||||
"next": "14.2.7",
|
||||
"next": "15.2.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"postcss": "^8",
|
||||
"radix-ui": "^1.4.2",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"resend": "^4.0.0",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
"tailwindcss": "^3.4.17",
|
||||
@@ -34,8 +34,8 @@
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@csstools/postcss-oklab-function": "^3.0.6",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
"paths": {
|
||||
"@/*": ["./src/*"],
|
||||
"@components/*": ["./src/components/*"]
|
||||
}
|
||||
},
|
||||
"target": "ES2017"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
@@ -12,5 +12,6 @@
|
||||
"persistent": true,
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"extends": ["//"]
|
||||
}
|
||||
|
||||
@@ -10,8 +10,15 @@ import { Metadata } from "next";
|
||||
import Image from "next/image";
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
type Params = {
|
||||
params: Promise<{
|
||||
slug: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export default async function Post({ params }: Params) {
|
||||
const post = getPostBySlug(params.slug);
|
||||
const { slug } = await params;
|
||||
const post = getPostBySlug(slug);
|
||||
|
||||
if (!post) {
|
||||
return notFound();
|
||||
@@ -65,14 +72,9 @@ export default async function Post({ params }: Params) {
|
||||
);
|
||||
}
|
||||
|
||||
type Params = {
|
||||
params: {
|
||||
slug: string;
|
||||
};
|
||||
};
|
||||
|
||||
export function generateMetadata({ params }: Params): Metadata {
|
||||
const post = getPostBySlug(params.slug);
|
||||
export async function generateMetadata({ params }: Params): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const post = getPostBySlug(slug);
|
||||
|
||||
if (!post) {
|
||||
return notFound();
|
||||
|
||||
@@ -18,15 +18,17 @@ const membersNameToInfoMap = {
|
||||
brad: "Bradley Kowalski",
|
||||
};
|
||||
|
||||
export default function TeamMemberPage({
|
||||
export default async function TeamMemberPage({
|
||||
params,
|
||||
}: { params: { member: string } }) {
|
||||
if (!(params.member in membersNameToInfoMap)) {
|
||||
}: { params: Promise<{ member: string }> }) {
|
||||
const { member } = await params;
|
||||
|
||||
if (!(member in membersNameToInfoMap)) {
|
||||
Router.push("/team");
|
||||
}
|
||||
|
||||
const memberName =
|
||||
membersNameToInfoMap[params.member as keyof typeof membersNameToInfoMap];
|
||||
membersNameToInfoMap[member as keyof typeof membersNameToInfoMap];
|
||||
const memberInfo = team.find(
|
||||
(m: { name: string }) => m.name.toLowerCase() === memberName.toLowerCase(),
|
||||
);
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
"mdast-util-from-markdown": "^2.0.0",
|
||||
"mdast-util-mdx": "^3.0.0",
|
||||
"micromark-extension-mdxjs": "^3.0.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.2.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"shiki": "^0.14.6",
|
||||
"shiki-twoslash": "^3.1.2",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
@@ -39,8 +39,8 @@
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"autoprefixer": "^10",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3",
|
||||
|
||||
@@ -650,6 +650,13 @@ const svelteExamples: Example[] = [
|
||||
tech: [tech.svelte],
|
||||
features: [features.fileUpload, features.passkey, features.inviteLink],
|
||||
illustration: <FileShareIllustration />,
|
||||
}, {
|
||||
name: "Chat",
|
||||
slug: "chat-svelte",
|
||||
description:
|
||||
"A simple Svelte app that creates a chat room with a shareable link.",
|
||||
tech: [tech.svelte],
|
||||
illustration: <ChatIllustration />,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ export const docNavigationItems = [
|
||||
done: 10,
|
||||
},
|
||||
{
|
||||
name: "Group inheritance",
|
||||
name: "Groups as members",
|
||||
href: "/docs/groups/inheritance",
|
||||
done: 100,
|
||||
},
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { ContentByFramework, CodeGroup } from '@/components/forMdx'
|
||||
|
||||
export const metadata = {
|
||||
description: "Create Groups that inherit members from other Groups."
|
||||
export const metadata = {
|
||||
description: "Add groups as members of other groups."
|
||||
};
|
||||
|
||||
# Group Inheritance
|
||||
# Groups as members
|
||||
|
||||
Groups can inherit members from other groups using the `extend` method.
|
||||
Groups can be added to other groups using the `addMember` method.
|
||||
|
||||
When a group extends another group, members of the parent group will become automatically part of the child group.
|
||||
When a group is added as a member of another group, members of the added group will become part of the containing group.
|
||||
|
||||
## Basic Usage
|
||||
## Basic usage
|
||||
|
||||
Here's how to extend a group:
|
||||
Here's how to add a group as a member of another group:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -21,19 +21,98 @@ import { Group } from "jazz-tools";
|
||||
const playlistGroup = Group.create();
|
||||
const trackGroup = Group.create();
|
||||
|
||||
// This way track becomes visible to the members of playlist
|
||||
trackGroup.extend(playlistGroup);
|
||||
// Tracks are now visible to the members of playlist
|
||||
trackGroup.addMember(playlistGroup);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
When you extend a group:
|
||||
- Members of the parent group get access to the child group
|
||||
- Their roles are inherited (with some exceptions, see [below](#role-inheritance-rules))
|
||||
- Removing a member from the parent group also removes their access to child groups
|
||||
When you add groups as members:
|
||||
- Members of the added group become members of the container group
|
||||
- Their roles are inherited (with some exceptions, see [below](#the-rules-of-role-inheritance))
|
||||
- Revoking access from the member group also removes its access to the container group
|
||||
|
||||
## Inheriting members but overriding their role
|
||||
## Levels of inheritance
|
||||
|
||||
In some cases you might want to inherit all members from a parent group but override/flatten their roles to the same specific role in the child group. You can do so by passing an "override role" as a second argument to `extend`:
|
||||
Adding a group as a member of another is not limited in depth:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
// ---cut---
|
||||
const grandParentGroup = Group.create();
|
||||
const parentGroup = Group.create();
|
||||
const childGroup = Group.create();
|
||||
|
||||
childGroup.addMember(parentGroup);
|
||||
parentGroup.addMember(grandParentGroup);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Members of the grandparent group will get access to all descendant groups based on their roles.
|
||||
|
||||
## Roles
|
||||
|
||||
### The rules of role inheritance
|
||||
|
||||
If the account is already a member of the container group, it will get the more permissive role:
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
import { createJazzTestAccount } from 'jazz-tools/testing';
|
||||
const bob = await createJazzTestAccount();
|
||||
// ---cut---
|
||||
const addedGroup = Group.create();
|
||||
addedGroup.addMember(bob, "reader");
|
||||
|
||||
const containingGroup = Group.create();
|
||||
addedGroup.addMember(bob, "writer");
|
||||
containingGroup.addMember(addedGroup);
|
||||
|
||||
// Bob stays a writer because his role is higher
|
||||
// than the inherited reader role.
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
When adding a group to another group, only admin, writer and reader roles are inherited:
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
import { createJazzTestAccount } from 'jazz-tools/testing';
|
||||
const bob = await createJazzTestAccount();
|
||||
// ---cut---
|
||||
const addedGroup = Group.create();
|
||||
addedGroup.addMember(bob, "writeOnly");
|
||||
|
||||
const containingGroup = Group.create();
|
||||
containingGroup.addMember(addedGroup);
|
||||
|
||||
// Bob does not become a member of the containing group
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
To add a group to another group:
|
||||
|
||||
1. The current account must be an admin in the containing group
|
||||
2. The current account must be a member of the added group
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { co, Group } from "jazz-tools";
|
||||
const group = Group.create();
|
||||
const Company = co.map({});
|
||||
const company = Company.create({ owner: group });
|
||||
// ---cut---
|
||||
const companyGroup = company._owner.castAs(Group);
|
||||
const teamGroup = Group.create();
|
||||
|
||||
// Works only if I'm a member of `companyGroup`
|
||||
teamGroup.addMember(companyGroup);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Overriding the added group's roles
|
||||
|
||||
In some cases you might want to inherit all members from an added group but override their roles to the same specific role in the containing group. You can do so by passing an "override role" as a second argument to `addMember`:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
@@ -46,8 +125,9 @@ organizationGroup.addMember(bob, "admin");
|
||||
|
||||
const billingGroup = Group.create();
|
||||
|
||||
// This way the members of the organization can only read the billing data
|
||||
billingGroup.extend(organizationGroup, "reader");
|
||||
// This way the members of the organization
|
||||
// can only read the billing data
|
||||
billingGroup.addMember(organizationGroup, "reader");
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -57,147 +137,71 @@ The "override role" works in both directions:
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
import { createJazzTestAccount } from 'jazz-tools/testing';
|
||||
const bob = await createJazzTestAccount();
|
||||
const alice = await createJazzTestAccount();
|
||||
const bob = await createJazzTestAccount();
|
||||
// ---cut---
|
||||
const parentGroup = Group.create();
|
||||
parentGroup.addMember(bob, "reader");
|
||||
parentGroup.addMember(alice, "admin");
|
||||
const addedGroup = Group.create();
|
||||
addedGroup.addMember(bob, "reader");
|
||||
addedGroup.addMember(alice, "admin");
|
||||
|
||||
const childGroup = Group.create();
|
||||
childGroup.extend(parentGroup, "writer");
|
||||
const containingGroup = Group.create();
|
||||
containingGroup.addMember(addedGroup, "writer");
|
||||
|
||||
// Bob and Alice are now writers in the child group
|
||||
// Bob and Alice are now writers in the containing group
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Multiple Levels of Inheritance
|
||||
### Permission changes
|
||||
|
||||
Groups can be extended multiple levels deep:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
// ---cut---
|
||||
const grandParentGroup = Group.create();
|
||||
const parentGroup = Group.create();
|
||||
const childGroup = Group.create();
|
||||
|
||||
childGroup.extend(parentGroup);
|
||||
parentGroup.extend(grandParentGroup);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Members of the grandparent group will get access to all descendant groups based on their roles.
|
||||
|
||||
## Permission Changes
|
||||
|
||||
When you remove a member from a parent group, they automatically lose access to all child groups. We handle key rotation automatically to ensure security.
|
||||
When you remove a member from an added group, they automatically lose access to all containing groups. We handle key rotation automatically to ensure security.
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
import { createJazzTestAccount } from 'jazz-tools/testing';
|
||||
const bob = await createJazzTestAccount();
|
||||
const parentGroup = Group.create();
|
||||
const addedGroup = Group.create();
|
||||
// ---cut---
|
||||
// Remove member from parent
|
||||
await parentGroup.removeMember(bob);
|
||||
// Remove member from added group
|
||||
await addedGroup.removeMember(bob);
|
||||
|
||||
// Bob loses access to both parent and child groups
|
||||
// Bob loses access to both groups.
|
||||
// If Bob was also a member of the containing group,
|
||||
// he wouldn't have lost access.
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Role Inheritance Rules
|
||||
## Removing groups from other groups
|
||||
|
||||
If the account is already a member of the child group, it will get the more permissive role:
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
import { createJazzTestAccount } from 'jazz-tools/testing';
|
||||
const bob = await createJazzTestAccount();
|
||||
// ---cut---
|
||||
const parentGroup = Group.create();
|
||||
parentGroup.addMember(bob, "reader");
|
||||
|
||||
const childGroup = Group.create();
|
||||
parentGroup.addMember(bob, "writer");
|
||||
childGroup.extend(parentGroup);
|
||||
|
||||
// Bob stays a writer because his role is higher
|
||||
// than the inherited reader role.
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
When extending groups, only admin, writer and reader roles are inherited:
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
import { createJazzTestAccount } from 'jazz-tools/testing';
|
||||
const bob = await createJazzTestAccount();
|
||||
// ---cut---
|
||||
const parentGroup = Group.create();
|
||||
parentGroup.addMember(bob, "writeOnly");
|
||||
|
||||
const childGroup = Group.create();
|
||||
childGroup.extend(parentGroup);
|
||||
|
||||
// Bob does not become a member of the child group
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
To extend a group:
|
||||
|
||||
1. The current account must be an admin in the child group
|
||||
2. The current account must be a member of the parent group
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group, co, z } from "jazz-tools";
|
||||
const Company = co.map({
|
||||
name: z.string(),
|
||||
});
|
||||
const company = Company.create({ name: "Garden Computing" });
|
||||
// ---cut---
|
||||
const companyGroup = company._owner.castAs(Group)
|
||||
const teamGroup = Group.create();
|
||||
|
||||
// Works only if I'm a member of companyGroup
|
||||
teamGroup.extend(companyGroup);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Revoking a group extension
|
||||
|
||||
You can revoke a group extension by using the `revokeExtend` method:
|
||||
You can remove a group from another group by using the `removeMember` method:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
// ---cut---
|
||||
const parentGroup = Group.create();
|
||||
const childGroup = Group.create();
|
||||
const addedGroup = Group.create();
|
||||
const containingGroup = Group.create();
|
||||
|
||||
childGroup.extend(parentGroup);
|
||||
containingGroup.addMember(addedGroup);
|
||||
|
||||
// Revoke the extension
|
||||
await childGroup.revokeExtend(parentGroup);
|
||||
await containingGroup.removeMember(addedGroup);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## Getting all parent groups
|
||||
## Getting all added groups
|
||||
|
||||
You can get all the parent groups of a group by calling the `getParentGroups` method:
|
||||
You can get all of the groups added to a group by calling the `getParentGroups` method:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import { Group } from "jazz-tools";
|
||||
// ---cut---
|
||||
const childGroup = Group.create();
|
||||
const parentGroup = Group.create();
|
||||
childGroup.extend(parentGroup);
|
||||
const containingGroup = Group.create();
|
||||
const addedGroup = Group.create();
|
||||
containingGroup.addMember(addedGroup);
|
||||
|
||||
console.log(childGroup.getParentGroups()); // [parentGroup]
|
||||
console.log(containingGroup.getParentGroups()); // [addedGroup]
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -220,13 +224,13 @@ companyGroup.addMember(CEO, "admin");
|
||||
|
||||
// Team group with elevated permissions
|
||||
const teamGroup = Group.create();
|
||||
teamGroup.extend(companyGroup); // Inherits company-wide access
|
||||
teamGroup.addMember(companyGroup); // Inherits company-wide access
|
||||
teamGroup.addMember(teamLead, "admin");
|
||||
teamGroup.addMember(developer, "writer");
|
||||
|
||||
// Project group with specific permissions
|
||||
const projectGroup = Group.create();
|
||||
projectGroup.extend(teamGroup); // Inherits team permissions
|
||||
projectGroup.addMember(teamGroup); // Inherits team permissions
|
||||
projectGroup.addMember(client, "reader"); // Client can only read project items
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
@@ -6,7 +6,11 @@ export const metadata = {
|
||||
|
||||
# Learn some <span className="sr-only">Jazz</span> <JazzLogo className="h-[41px] -ml-0.5 -mt-[3px] inline" />
|
||||
|
||||
Welcome to the Jazz documentation!
|
||||
**Jazz is a toolkit for building backendless apps**. You get data without needing a database — plus auth, permissions, files and multiplayer without needing a backend. Jazz lets you do everything right from the frontend and you'll ship better apps, faster.
|
||||
|
||||
Instead of wrestling with databases, APIs, and server infrastructure, you work with **CoValues** ("collaborative values") — your new cloud-synced building blocks that feel like local state but automatically sync across all devices and users in real-time.
|
||||
|
||||
---
|
||||
|
||||
**Note:** We just released [Jazz 0.14.0](/docs/upgrade/0-14-0) with a bunch of breaking changes and are still cleaning the docs up - see the [upgrade guide](/docs/upgrade/0-14-0) for details.
|
||||
|
||||
@@ -20,37 +24,44 @@ npx create-jazz-app@latest --api-key you@example.com
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Or set up Jazz yourself, using the following instructions for your framework of choice:
|
||||
|
||||
- [React](/docs/react/project-setup)
|
||||
- [Next.js](/docs/react/project-setup#nextjs)
|
||||
- [React Native](/docs/react-native/project-setup)
|
||||
- [React Native Expo](/docs/react-native-expo/project-setup)
|
||||
- [Vue](/docs/vue/project-setup)
|
||||
- [Svelte](/docs/svelte/project-setup)
|
||||
|
||||
{/* <ContentByFramework framework="react">
|
||||
Or you can follow this [React step-by-step guide](/docs/react/guide) where we walk you through building an issue tracker app.
|
||||
</ContentByFramework> */}
|
||||
|
||||
## Example apps
|
||||
## Why Jazz is different
|
||||
|
||||
You can also find [example apps](/examples) with code most similar to what you want to build. These apps
|
||||
make use of different features such as auth, file upload, and more.
|
||||
Most apps rebuild the same thing: shared state that syncs between users and devices. Jazz starts from that shared state, giving you:
|
||||
|
||||
- **No backend required** — Focus on building features, not infrastructure
|
||||
- **Real-time sync** — Changes appear everywhere immediately
|
||||
- **Multiplayer by default** — Collaboration just works
|
||||
- **Local-first** — Your app works offline and feels instant
|
||||
|
||||
Think Figma, Notion, or Linear — but you don't need years to build a custom stack.
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Define your data** with CoValues schemas
|
||||
2. **Connect to sync infrastructure** (Jazz Cloud or self-hosted)
|
||||
3. **Create and edit CoValues** like normal objects
|
||||
4. **Get automatic sync and persistence** across all devices and users
|
||||
|
||||
Your UI updates instantly on every change, everywhere. It's like having reactive local state that happens to be shared with the world.
|
||||
|
||||
## Ready to see Jazz in action?
|
||||
|
||||
Have a look at our [example apps](/examples) for inspiration and to see what's possible with Jazz. From real-time chat and collaborative editors to file sharing and social features — these are just the beginning of what you can build.
|
||||
|
||||
## Core concepts
|
||||
|
||||
Learn how to structure your data using [collaborative values](/docs/schemas/covalues) — the building blocks that make Jazz apps work.
|
||||
|
||||
## Sync and storage
|
||||
|
||||
Sync and persist your data by setting up a [sync and storage infrastructure](/docs/sync-and-storage) using Jazz Cloud, or do it yourself.
|
||||
Sync and persist your data by setting up [sync and storage infrastructure](/docs/sync-and-storage) using Jazz Cloud, or host it yourself.
|
||||
|
||||
## Collaborative values
|
||||
|
||||
Learn how to structure your data using [collaborative values](/docs/schemas/covalues).
|
||||
|
||||
## LLM Docs
|
||||
## Going deeper
|
||||
|
||||
Get better results with AI by [importing the Jazz docs](/docs/ai-tools) into your context window.
|
||||
|
||||
## Get support
|
||||
|
||||
If you have any questions or need assistance, please don't hesitate to reach out to us on [Discord](https://discord.gg/utDMjHYg42).
|
||||
We would love to help you get started.
|
||||
If you have any questions or need assistance, please don't hesitate to reach out to us on [Discord](https://discord.gg/utDMjHYg42). We'd love to help you get started.
|
||||
|
||||
@@ -253,14 +253,13 @@ Here's a quick overview of the primitive types you can use:
|
||||
|
||||
<CodeGroup>
|
||||
```ts twoslash
|
||||
import {z} from "jazz-tools";
|
||||
// ---cut---
|
||||
z.string(); // For simple strings
|
||||
z.number(); // For numbers
|
||||
import { z } from "jazz-tools";
|
||||
// ---cut---
|
||||
z.string(); // For simple strings
|
||||
z.number(); // For numbers
|
||||
z.boolean(); // For booleans
|
||||
z.null(); // For null
|
||||
z.date(); // For dates
|
||||
z.literal(["waiting", "ready"]); // For enums
|
||||
z.date(); // For dates
|
||||
z.literal(["waiting", "ready"]); // For enums
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ We're introducing a new resolve API for deep loading, more friendly to TypeScrip
|
||||
|
||||
<CodeGroup>
|
||||
```tsx twoslash
|
||||
// @noErrors: 2451
|
||||
// @noErrors: 2451 2769
|
||||
import { CoMap, CoList, coField, Account } from "jazz-tools";
|
||||
import { useAccount } from "jazz-react";
|
||||
class AccountRoot extends CoMap { friends = coField.ref(ListOfAccounts); }
|
||||
|
||||
@@ -84,18 +84,22 @@ function highlightPlugin() {
|
||||
transformers: [
|
||||
transformerTwoslash({
|
||||
explicitTrigger: true,
|
||||
throws: false, //process.env.NODE_ENV === "production",
|
||||
onTwoslashError:
|
||||
process.env.NODE_ENV !== "production"
|
||||
? (e, code) => {
|
||||
const { description, recommendation } = e;
|
||||
console.error("\nTwoslash error: ");
|
||||
console.log(description);
|
||||
console.log(recommendation);
|
||||
console.log("\nCode: \n```\n" + code + "\n```");
|
||||
error = e;
|
||||
}
|
||||
: undefined,
|
||||
throws: process.env.NODE_ENV === "production",
|
||||
onTwoslashError: (e, code) => {
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
// Re-throw to actually fail the build in production
|
||||
throw e;
|
||||
}
|
||||
|
||||
const { description, recommendation } = e;
|
||||
console.error("\nTwoslash error: ");
|
||||
console.log(description);
|
||||
console.log(recommendation);
|
||||
console.log("\nCode: \n```\n" + code + "\n```");
|
||||
|
||||
// In development, store the error to show inline
|
||||
error = e;
|
||||
},
|
||||
}),
|
||||
transformerNotationDiff(),
|
||||
],
|
||||
@@ -103,7 +107,7 @@ function highlightPlugin() {
|
||||
|
||||
node.type = "html";
|
||||
node.value = error
|
||||
? `<div style="color: red;">${error}</div>` + html
|
||||
? `<div style="color: red; background: #fee; padding: 8px; border: 1px solid #fcc; margin: 8px 0;"><strong>Twoslash Error:</strong> ${error.description || error.message} ${error.recommendation}</div>` + html
|
||||
: html;
|
||||
node.children = [];
|
||||
return SKIP;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"@stefanprobst/rehype-extract-toc": "^2.2.0",
|
||||
"@turf/turf": "^7.1.0",
|
||||
"@types/mdx": "^2.0.8",
|
||||
"@types/react-native": "^0.73.0",
|
||||
"@types/topojson-client": "^3.1.5",
|
||||
"@vercel/analytics": "^1.3.1",
|
||||
"@vercel/speed-insights": "^1.0.12",
|
||||
@@ -57,8 +58,8 @@
|
||||
"next": "15.2.1",
|
||||
"next-themes": "^0.2.1",
|
||||
"qrcode": "^1.5.4",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"react-singleton-hook": "^4.0.1",
|
||||
"shiki": "^3.2.1",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
@@ -70,8 +71,8 @@
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@types/geojson": "^7946.0.14",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"autoprefixer": "^10",
|
||||
"pagefind": "^1.3.0",
|
||||
"postcss": "^8",
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
"persistent": true,
|
||||
"dependsOn": ["build"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"extends": ["//"]
|
||||
}
|
||||
|
||||
4169
homepage/pnpm-lock.yaml
generated
4169
homepage/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,10 @@
|
||||
packages:
|
||||
- "homepage"
|
||||
- "design-system"
|
||||
- "gcmp"
|
||||
- "gcmp"
|
||||
|
||||
catalog:
|
||||
"react": "19.0.0"
|
||||
"react-dom": "19.0.0"
|
||||
"@types/react": "19.0.0"
|
||||
"@types/react-dom": "19.0.0"
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"esbuild": "0.24.0"
|
||||
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"expo-router": "patches/expo-router.patch"
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.14.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.14.25
|
||||
- cojson-storage@0.14.25
|
||||
|
||||
## 0.14.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.14.24",
|
||||
"version": "0.14.25",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.14.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.14.25
|
||||
- cojson-storage@0.14.25
|
||||
|
||||
## 0.14.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.14.24",
|
||||
"version": "0.14.25",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"cojson": "workspace:0.14.24",
|
||||
"cojson": "workspace:0.14.25",
|
||||
"cojson-storage": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# cojson-storage
|
||||
|
||||
## 0.14.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cojson@0.14.25
|
||||
|
||||
## 0.14.24
|
||||
|
||||
### Patch Changes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user