Compare commits

...

30 Commits

Author SHA1 Message Date
Anselm
2b8f5ed3ca Release 2024-09-23 14:17:35 -07:00
Anselm
fcf0baee2e Fix jazz-run 2024-09-23 14:16:54 -07:00
Anselm Eickhoff
07183ffe5b Merge pull request #443 from gardencmp/trishalim-jazz-333
Spacing improvements on landing pages (home and mesh)
2024-09-23 13:46:05 -07:00
Trisha Lim
11c36460ac Allow horizontal scroll on tabs 2024-09-23 20:28:51 +01:00
Trisha Lim
e0d7b266e7 Spacing improvements on mesh landing page 2024-09-23 20:27:02 +01:00
Trisha Lim
3706360ffc Make borders on dark mode more subtle 2024-09-23 20:22:03 +01:00
Trisha Lim
3d7a20ca64 Spacing improvements on homepage 2024-09-23 20:20:09 +01:00
Anselm Eickhoff
381ecaaf96 Merge pull request #438 from gardencmp/trishalim-jazz-331
Fix scroll issue on chat example
2024-09-23 09:30:14 -07:00
Anselm Eickhoff
25435ba597 Merge pull request #434 from gardencmp/trishalim-jazz-323
Make package name on side bar clickable
2024-09-23 09:29:03 -07:00
Trisha Lim
c56fd81bff Switch auth button copy to sentence case 2024-09-23 16:54:22 +01:00
Trisha Lim
707bf332d0 Fix scroll issue on chat example 2024-09-23 16:51:57 +01:00
Anselm Eickhoff
40225635f1 Merge pull request #436 from gardencmp/trishalim-jazz-326
Fix text colors in dark mode
2024-09-23 07:36:03 -07:00
Trisha Lim
e4ff701488 Fix text colors in dark mode 2024-09-23 15:25:06 +01:00
Anselm Eickhoff
314cdee815 Merge pull request #435 from gardencmp/trishalim-jazz-326 2024-09-23 07:07:30 -07:00
Trisha Lim
fa549f49b7 Fix heading colors in dark mode 2024-09-23 14:14:54 +01:00
Trisha Lim
c38460afd2 Fix inconsistent container width 2024-09-23 13:48:06 +01:00
Trisha Lim
7657c8469e Fix heading structure in docs 2024-09-23 13:11:28 +01:00
Trisha Lim
97abe86d7e Run pnpm format 2024-09-23 13:05:44 +01:00
Trisha Lim
a339dfaeb0 Make package name on side bar clickable 2024-09-23 13:05:25 +01:00
Anselm Eickhoff
3710776d09 Merge pull request #433 from gardencmp/anselm-jazz-330
Clean up chat example
2024-09-22 18:06:42 -07:00
Anselm
dffc2ee2fe Mention lines of code again 2024-09-22 18:01:15 -07:00
Anselm
cd47928252 Fix issues 2024-09-22 17:53:11 -07:00
Anselm
4b84ede0b5 Use two spaces for the chat examples 2024-09-22 17:46:27 -07:00
Anselm
0de4d684d8 Fix chat-clerk app.tsx 2024-09-22 17:36:33 -07:00
Anselm
d78a5364f6 Rename dumb components file 2024-09-22 17:32:52 -07:00
Anselm
228dfa1799 Clean up chat example 2024-09-22 17:25:50 -07:00
Anselm
74a66eaa94 Fix lockfile 2024-09-22 14:31:10 -07:00
Anselm
96670db342 Only build examples, don't deploy them on nomad anymore 2024-09-22 14:29:55 -07:00
Anselm
e4655c3705 Fix example auth components 2024-09-22 14:23:44 -07:00
Anselm
5a5f767b3d Fix versions & lockfile 2024-09-22 14:15:15 -07:00
129 changed files with 1166 additions and 805 deletions

View File

@@ -1,106 +0,0 @@
name: Build and Deploy
on:
push:
branches: [ "main" ]
jobs:
build-examples:
runs-on: ubuntu-latest
strategy:
matrix:
example: ["chat", "pets", "todo", "inspector"]
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: gardencmp
password: ${{ secrets.GITHUB_TOKEN }}
- name: Pnpm Build
run: |
pnpm install
pnpm turbo build;
working-directory: ./examples/${{ matrix.example }}
- name: Docker Build & Push
uses: docker/build-push-action@v4
with:
context: ./examples/${{ matrix.example }}
push: true
tags: ghcr.io/gardencmp/${{github.event.repository.name}}-example-${{ matrix.example }}:${{github.head_ref || github.ref_name}}-${{github.sha}}-${{github.run_number}}-${{github.run_attempt}}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-examples:
runs-on: ubuntu-latest
needs: build-examples
strategy:
matrix:
example: ["chat", "pets", "todo", "inspector"]
# example: ["twit", "chat", "counter-js-auth0", "pets", "twit", "file-drop", "inspector"]
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: gacts/install-nomad@v1
- name: Tailscale
uses: tailscale/github-action@v1
with:
authkey: ${{ secrets.TAILSCALE_AUTHKEY }}
- name: Deploy on Nomad
run: |
if [ "${{github.ref_name}}" == "main" ]; then
export BRANCH_SUFFIX="";
export BRANCH_SUBDOMAIN="";
else
export BRANCH_SUFFIX=-${{github.head_ref || github.ref_name}};
export BRANCH_SUBDOMAIN=${{github.head_ref || github.ref_name}}.;
fi
export DOCKER_USER=gardencmp;
export DOCKER_PASSWORD=${{ secrets.DOCKER_PULL_PAT }};
export DOCKER_TAG=ghcr.io/gardencmp/${{github.event.repository.name}}-example-${{ matrix.example }}:${{github.head_ref || github.ref_name}}-${{github.sha}}-${{github.run_number}}-${{github.run_attempt}};
envsubst '${DOCKER_USER} ${DOCKER_PASSWORD} ${DOCKER_TAG} ${BRANCH_SUFFIX} ${BRANCH_SUBDOMAIN}' < job-template.nomad > job-instance.nomad;
cat job-instance.nomad;
NOMAD_ADDR=${{ secrets.NOMAD_ADDR }} nomad job run job-instance.nomad;
working-directory: ./examples/${{ matrix.example }}

58
.github/workflows/build-examples.yaml vendored Normal file
View File

@@ -0,0 +1,58 @@
name: Build Examples
on:
push:
branches: [ "main" ]
jobs:
build-examples:
runs-on: ubuntu-latest
strategy:
matrix:
example: [
"chat",
"chat-clerk",
"inspector",
"music-player",
"password-manager",
"pets",
"todo",
]
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Enable corepack
run: corepack enable
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version-file: '.node-version'
cache: 'pnpm'
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Pnpm Build
run: |
pnpm install
pnpm turbo build;
working-directory: ./examples/${{ matrix.example }}

View File

@@ -0,0 +1,9 @@
# @jazz-e2e/binarycostream
## 0.0.82
### Patch Changes
- Updated dependencies
- jazz-tools@0.8.1
- jazz-react@0.8.1

View File

@@ -1,7 +1,7 @@
{
"name": "@jazz-e2e/binarycostream",
"private": true,
"version": "0.0.81",
"version": "0.0.82",
"type": "module",
"scripts": {
"dev": "vite",
@@ -14,11 +14,11 @@
"*.{js,jsx,mdx,json}": "prettier --write"
},
"dependencies": {
"cojson": "workspace:0.7.35-guest-auth.5",
"cojson": "workspace:0.8.0",
"hash-slash": "workspace:0.2.0",
"is-ci": "^3.0.1",
"jazz-react": "workspace:0.7.35-guest-auth.6",
"jazz-tools": "workspace:0.7.35-guest-auth.6",
"jazz-react": "workspace:0.8.1",
"jazz-tools": "workspace:0.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},

View File

@@ -2,12 +2,12 @@ module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser",
plugins: ["react-refresh"],
rules: {},
}
};

View File

@@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
sync-db/

View File

@@ -0,0 +1,10 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": false,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "avoid",
"printWidth": 80
}

View File

@@ -1,88 +1,184 @@
# jazz-example-chat
## 0.0.80
## 0.0.81
### Patch Changes
- Updated dependencies
- jazz-tools@0.8.1
- jazz-react@0.8.1
- jazz-react-auth-clerk@0.8.1
## 0.0.82
### Patch Changes
- bcec3be: Implement Clerk auth method + example
- Updated dependencies [6a147c2]
- Updated dependencies [ad40b88]
- Updated dependencies [23369dc]
- Updated dependencies [c2b62a0]
- Updated dependencies [bcec3be]
- Updated dependencies [1a979b6]
- Updated dependencies [bcec3be]
- Updated dependencies [6ce2051]
- cojson@0.8.0
- jazz-tools@0.8.0
- jazz-react@0.8.0
- jazz-react-auth-clerk@0.8.0
## 0.0.80-guest-auth.6
## 0.0.82-guest-auth.6
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.6
- jazz-tools@0.7.35-guest-auth.6
- jazz-react-auth-clerk@0.7.35-guest-auth.6
## 0.0.80-guest-auth.5
## 0.0.82
### Patch Changes
- Updated dependencies [49a8b54]
- Updated dependencies [35bbcd9]
- Updated dependencies [6f80282]
- Updated dependencies [35bbcd9]
- Updated dependencies [cac2ec9]
- Updated dependencies [f350e90]
- jazz-tools@0.7.35
- cojson@0.7.35
- jazz-react@0.7.35
## 0.0.81
### Patch Changes
- Updated dependencies [5d91f9f]
- Updated dependencies [5094e6d]
- Updated dependencies [b09589b]
- Updated dependencies [2c3a40c]
- Updated dependencies [4e16575]
- Updated dependencies [ea882ab]
- cojson@0.7.34
- jazz-react@0.7.34
- jazz-tools@0.7.34
## 0.0.81-neverthrow.6
### Patch Changes
- Updated dependencies
- cojson@0.7.35-guest-auth.5
- jazz-react@0.7.35-guest-auth.5
- jazz-react-auth-clerk@0.7.35-guest-auth.5
- jazz-tools@0.7.35-guest-auth.5
- cojson@0.7.34-neverthrow.8
- jazz-react@0.7.34-neverthrow.8
- jazz-tools@0.7.34-neverthrow.8
## 0.0.80-guest-auth.4
## 0.0.81-neverthrow.5
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.4
- jazz-tools@0.7.35-guest-auth.4
- jazz-react-auth-clerk@0.7.35-guest-auth.4
- cojson@0.7.34-neverthrow.7
- jazz-react@0.7.34-neverthrow.7
- jazz-tools@0.7.34-neverthrow.7
## 0.0.80-guest-auth.3
## 0.0.81-neverthrow.4
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-guest-auth.3
- jazz-tools@0.7.35-guest-auth.3
- jazz-react-auth-clerk@0.7.33-guest-auth.3
- cojson@0.7.34-neverthrow.4
- jazz-react@0.7.34-neverthrow.4
- jazz-tools@0.7.34-neverthrow.4
## 0.0.80-unique.2
## 0.0.81-neverthrow.3
### Patch Changes
- Updated dependencies
- jazz-tools@0.7.35-unique.2
- cojson@0.7.35-unique.2
- jazz-react@0.7.35-unique.2
- jazz-react-auth-clerk@0.7.33-unique.2
- cojson@0.7.34-neverthrow.3
- jazz-react@0.7.34-neverthrow.3
- jazz-tools@0.7.34-neverthrow.3
## 0.0.80-new-auth.1
## 0.0.81-neverthrow.2
### Patch Changes
- jazz-react@0.7.34-neverthrow.2
## 0.0.81-neverthrow.1
### Patch Changes
- Updated dependencies
- jazz-react@0.7.35-new-auth.1
- jazz-react-auth-clerk@0.7.33-new-auth.1
- cojson@0.7.34-neverthrow.1
- jazz-react@0.7.34-neverthrow.1
- jazz-tools@0.7.34-neverthrow.1
## 0.0.80-new-auth.0
## 0.0.81-neverthrow.0
### Patch Changes
- Implement Clerk auth method + example
- Updated dependencies
- cojson@0.7.34-neverthrow.0
- jazz-react@0.7.34-neverthrow.0
- jazz-tools@0.7.34-neverthrow.0
## 0.0.80
### Patch Changes
- Updated dependencies [b297c93]
- Updated dependencies [3bf5127]
- Updated dependencies [a8b74ff]
- Updated dependencies [db53161]
- cojson@0.7.33
- jazz-react@0.7.33
- jazz-tools@0.7.33
## 0.0.80-hotfixes.5
### Patch Changes
- Updated dependencies
- jazz-react-auth-clerk@0.7.33-new-auth.0
- jazz-react@0.7.35-new-auth.0
- jazz-tools@0.7.35-new-auth.0
- cojson@0.7.33-hotfixes.5
- jazz-react@0.7.33-hotfixes.5
- jazz-tools@0.7.33-hotfixes.5
## 0.0.80-hotfixes.4
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.4
- jazz-react@0.7.33-hotfixes.4
- jazz-tools@0.7.33-hotfixes.4
## 0.0.80-hotfixes.3
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.3
- jazz-react@0.7.33-hotfixes.3
- jazz-tools@0.7.33-hotfixes.3
## 0.0.80-hotfixes.2
### Patch Changes
- jazz-react@0.7.33-hotfixes.2
## 0.0.80-hotfixes.1
### Patch Changes
- jazz-react@0.7.33-hotfixes.1
## 0.0.80-hotfixes.0
### Patch Changes
- Updated dependencies
- cojson@0.7.33-hotfixes.0
- jazz-react@0.7.33-hotfixes.0
- jazz-tools@0.7.33-hotfixes.0
## 0.0.79

View File

@@ -1,19 +1,20 @@
# Jazz Chat Example
# Jazz Chat Example (with Clerk auth)
Live version: https://example-chat.jazz.tools
<!-- Live version: [https://chat-clerk.jazz.tools](https://chat-clerk.jazz.tools) -->
## Installing & running the example locally
(this requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation))
Start by checking out `jazz`
```bash
git clone https://github.com/gardencmp/jazz.git
cd jazz/examples/chat
cd jazz/examples/chat-clerk
pnpm pack --pack-destination /tmp
mkdir -p ~/jazz-examples/chat # or any other directory
tar -xf /tmp/jazz-example-chat-* --strip-components 1 -C ~/jazz-examples/chat
cd ~/jazz-examples/chat
mkdir -p ~/jazz-examples/chat-clerk # or any other directory
tar -xf /tmp/jazz-example-chat-* --strip-components 1 -C ~/jazz-examples/chat-clerk
cd ~/jazz-examples/chat-clerk
```
This ensures that you have the example app without git history and independent of the Jazz multi-package monorepo.
@@ -34,9 +35,8 @@ pnpm dev
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 Global Mesh](https://jazz.tools/mesh) (`wss://sync.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx cojson-simple-sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?sync=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/2_main.tsx](./src/2_main.tsx).
You can also run a local sync server by running `npx jazz-run sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of the `<Jazz.Provider>` provider component in [./src/2_main.tsx](./src/2_main.tsx).

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat-clerk",
"private": true,
"version": "0.0.80",
"version": "0.0.81",
"type": "module",
"scripts": {
"dev": "vite",
@@ -23,9 +23,9 @@
"clsx": "^2.0.0",
"cojson": "workspace:0.8.0",
"hash-slash": "workspace:0.2.0",
"jazz-react": "workspace:0.8.0",
"jazz-react-auth-clerk": "workspace:0.8.0",
"jazz-tools": "workspace:0.8.0",
"jazz-react": "workspace:0.8.1",
"jazz-react-auth-clerk": "workspace:0.8.1",
"jazz-tools": "workspace:0.8.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -3,4 +3,4 @@ export default {
tailwindcss: {},
autoprefixer: {},
},
}
};

View File

@@ -1,85 +1,30 @@
import { CoMap, CoList, co, Group, ID } from "jazz-tools";
import { createJazzReactApp } from "jazz-react";
import { useJazzClerkAuth } from "jazz-react-auth-clerk";
import { createRoot } from "react-dom/client";
import { Group, ID } from "jazz-tools";
import { useIframeHashRouter } from "hash-slash";
import { useAccount } from "./main.tsx";
import { Chat } from "./schema.ts";
import { ChatScreen } from "./chatScreen.tsx";
import { StrictMode } from "react";
import { AppContainer, TopBar } from "./ui.tsx";
import {
ClerkProvider,
SignInButton,
useAuth,
useClerk,
} from "@clerk/clerk-react";
export function App() {
const { me, logOut } = useAccount();
export class Message extends CoMap {
text = co.string;
const createChat = () => {
if (!me) return;
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
location.hash = "/chat/" + chat.id;
};
return (
<AppContainer>
<TopBar>
{me?.profile?.name} · <button onClick={logOut}>Log out</button>
</TopBar>
{useIframeHashRouter().route({
"/": () => createChat() as never,
"/chat/:id": id => <ChatScreen chatID={id as ID<Chat>} />,
})}
</AppContainer>
);
}
export class Chat extends CoList.Of(co.ref(Message)) {}
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
function AuthAndJazz({ children }: { children: React.ReactNode }) {
const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk);
return (
<>
{state.errors.map((error) => (
<div key={error}>{error}</div>
))}
{auth ? (
<Jazz.Provider
auth={auth}
peer="wss://mesh.jazz.tools/?key=chat-example-jazz-clerk@gcmp.io"
>
{children}
</Jazz.Provider>
) : (
<SignInButton />
)}
</>
);
}
function App() {
const { signOut } = useAuth();
const { me } = useAccount();
const createChat = () => {
if (!me) return;
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
location.hash = "/chat/" + chat.id;
};
return (
<div className="flex flex-col items-center justify-between w-screen h-screen p-2 dark:bg-black dark:text-white">
<div className="rounded mb-5 px-2 py-1 text-sm self-end">
{me?.profile?.name} ·{" "}
<button onClick={() => signOut()}>Log Out</button>
</div>
{useIframeHashRouter().route({
"/": () => createChat() as never,
"/chat/:id": (id) => <ChatScreen chatID={id as ID<Chat>} />,
})}
</div>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ClerkProvider
publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}
afterSignOutUrl="/"
>
<AuthAndJazz>
<App />
</AuthAndJazz>
</ClerkProvider>
</StrictMode>
);

View File

@@ -1,42 +1,43 @@
import { ID } from 'jazz-tools';
import { Chat, Message, useCoState } from './app.tsx';
import { ID } from "jazz-tools";
import { Chat, Message } from "./schema.ts";
import { useCoState } from "./main.tsx";
import {
BubbleBody,
BubbleContainer,
BubbleInfo,
ChatContainer,
ChatInput,
EmptyChatMessage,
} from "./ui.tsx";
export function ChatScreen(props: { chatID: ID<Chat> }) {
const chat = useCoState(Chat, props.chatID, [{}]);
return chat ? <div className='w-full max-w-xl h-full flex flex-col items-stretch'>
{chat.length > 0
? chat.map((msg) => <ChatBubble msg={msg} key={msg.id} />)
: <div className='m-auto text-sm'>(Empty chat)</div>}
<ChatInput onSubmit={(text) => {
chat.push(
Message.create({ text }, { owner: chat._owner })
);
}} />
</div> : <div>Loading...</div>;
return chat ? (
<ChatContainer>
{chat.length > 0 ? (
chat.map(msg => <ChatBubble msg={msg} key={msg.id} />)
) : (
<EmptyChatMessage />
)}
<ChatInput
onSubmit={text => {
chat.push(Message.create({ text }, { owner: chat._owner }));
}}
/>
</ChatContainer>
) : (
<div>Loading...</div>
);
}
function ChatBubble(props: { msg: Message }) {
const lastEdit = props.msg._edits.text;
const align = lastEdit.by?.isMe ? 'items-end' : 'items-start';
return <div className={`${align} flex flex-col`}>
<div className='rounded-xl bg-stone-100 dark:bg-stone-700 dark:text-white py-2 px-4 mt-2 min-w-[5rem]'>
{ props.msg.text }
</div>
<div className='text-xs text-neutral-500 ml-2'>
{ lastEdit.by?.profile?.name }{' '}
{ lastEdit.madeAt?.toLocaleTimeString() }
</div>
</div>;
}
function ChatInput(props: { onSubmit: (text: string) => void }) {
return <input className='rounded p-2 border mt-auto dark:bg-black dark:text-white border-stone-300 dark:border-stone-700'
placeholder='Type a message and press Enter'
onKeyDown={({ key, currentTarget: input }) => {
if (key !== 'Enter' || !input.value) return;
props.onSubmit(input.value);
input.value = '';
}} />;
return (
<BubbleContainer fromMe={lastEdit.by?.isMe}>
<BubbleBody>{props.msg.text}</BubbleBody>
<BubbleInfo by={lastEdit.by?.profile?.name} madeAt={lastEdit.madeAt} />
</BubbleContainer>
);
}

View File

@@ -75,4 +75,4 @@
margin: 0;
padding: 0;
}
}
}

View File

@@ -0,0 +1,46 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { createJazzReactApp } from "jazz-react";
import { App } from "./app.tsx";
import { ClerkProvider, SignInButton, useClerk } from "@clerk/clerk-react";
import { useJazzClerkAuth } from "jazz-react-auth-clerk";
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const clerk = useClerk();
const [auth, state] = useJazzClerkAuth(clerk);
return (
<>
{state.errors.map(error => (
<div key={error}>{error}</div>
))}
{auth ? (
<Jazz.Provider
auth={auth}
peer="wss://mesh.jazz.tools/?key=chat-example-jazz-clerk@gcmp.io"
>
{children}
</Jazz.Provider>
) : (
<SignInButton />
)}
</>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<ClerkProvider
publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}
afterSignOutUrl="/"
>
<JazzAndAuth>
<App />
</JazzAndAuth>
</ClerkProvider>
</StrictMode>
);

View File

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

View File

@@ -0,0 +1,63 @@
export function AppContainer(props: { children: React.ReactNode }) {
return (
<div className="flex flex-col items-center justify-between w-screen h-screen p-2 dark:bg-black dark:text-white">
{props.children}
</div>
);
}
export function TopBar(props: { children: React.ReactNode }) {
return (
<div className="mb-5 px-2 py-1 text-sm self-end">{props.children}</div>
);
}
export function ChatContainer(props: { children: React.ReactNode }) {
return (
<div className="w-full max-w-xl h-full flex flex-col items-stretch">
{props.children}
</div>
);
}
export function EmptyChatMessage() {
return <div className="m-auto text-sm">(Empty chat)</div>;
}
export function BubbleContainer(props: {
children: React.ReactNode;
fromMe: boolean | undefined;
}) {
const align = props.fromMe ? "items-end" : "items-start";
return <div className={`${align} flex flex-col`}>{props.children}</div>;
}
export function BubbleBody(props: { children: React.ReactNode }) {
return (
<div className="rounded-xl bg-stone-100 dark:bg-stone-700 dark:text-white py-2 px-4 mt-2 min-w-[5rem]">
{props.children}
</div>
);
}
export function BubbleInfo(props: { by: string | undefined; madeAt: Date }) {
return (
<div className="text-xs text-neutral-500 ml-2">
{props.by} {props.madeAt.toLocaleTimeString()}
</div>
);
}
export function ChatInput(props: { onSubmit: (text: string) => void }) {
return (
<input
className="rounded p-2 border mt-auto dark:bg-black dark:text-white border-stone-300 dark:border-stone-700"
placeholder="Type a message and press Enter"
onKeyDown={({ key, currentTarget: input }) => {
if (key !== "Enter" || !input.value) return;
props.onSubmit(input.value);
input.value = "";
}}
/>
);
}

View File

@@ -1,11 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}",
],
theme: {
container: {
center: true,
@@ -72,4 +72,4 @@ module.exports = {
},
},
plugins: [require("tailwindcss-animate")],
}
};

View File

@@ -1,5 +1,5 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";
// https://vitejs.dev/config/
@@ -11,6 +11,6 @@ export default defineConfig({
},
},
build: {
minify: false
}
})
minify: false,
},
});

View File

@@ -2,12 +2,12 @@ module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended",
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parser: "@typescript-eslint/parser",
plugins: ["react-refresh"],
rules: {},
}
};

10
examples/chat/.prettierrc Normal file
View File

@@ -0,0 +1,10 @@
{
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": false,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "avoid",
"printWidth": 80
}

View File

@@ -1,5 +1,13 @@
# jazz-example-chat
## 0.0.83
### Patch Changes
- Updated dependencies
- jazz-tools@0.8.1
- jazz-react@0.8.1
## 0.0.82
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-chat",
"private": true,
"version": "0.0.82",
"version": "0.0.83",
"type": "module",
"scripts": {
"dev": "vite",
@@ -22,8 +22,8 @@
"clsx": "^2.0.0",
"cojson": "workspace:0.8.0",
"hash-slash": "workspace:0.2.0",
"jazz-react": "workspace:0.8.0",
"jazz-tools": "workspace:0.8.0",
"jazz-react": "workspace:0.8.1",
"jazz-tools": "workspace:0.8.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -3,4 +3,4 @@ export default {
tailwindcss: {},
autoprefixer: {},
},
}
};

View File

@@ -1,65 +1,30 @@
import { CoMap, CoList, co, Group, ID } from "jazz-tools";
import { createJazzReactApp, DemoAuthBasicUI, useDemoAuth } from "jazz-react";
import { createRoot } from "react-dom/client";
import { Group, ID } from "jazz-tools";
import { useIframeHashRouter } from "hash-slash";
import { useAccount } from "./main.tsx";
import { Chat } from "./schema.ts";
import { ChatScreen } from "./chatScreen.tsx";
import { StrictMode } from "react";
import { AppContainer, TopBar } from "./ui.tsx";
export class Message extends CoMap {
text = co.string;
export function App() {
const { me, logOut } = useAccount();
const createChat = () => {
if (!me) return;
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
location.hash = "/chat/" + chat.id;
};
return (
<AppContainer>
<TopBar>
{me?.profile?.name} · <button onClick={logOut}>Log out</button>
</TopBar>
{useIframeHashRouter().route({
"/": () => createChat() as never,
"/chat/:id": id => <ChatScreen chatID={id as ID<Chat>} />,
})}
</AppContainer>
);
}
export class Chat extends CoList.Of(co.ref(Message)) {}
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
function App() {
const { me, logOut } = useAccount();
const createChat = () => {
if (!me) return;
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
location.hash = "/chat/" + chat.id;
};
return (
<div className="flex flex-col items-center justify-between w-screen h-screen p-2 dark:bg-black dark:text-white">
<div className="rounded mb-5 px-2 py-1 text-sm self-end">
{me?.profile?.name} ·{" "}
<button onClick={logOut}>Log Out</button>
</div>
{useIframeHashRouter().route({
"/": () => createChat() as never,
"/chat/:id": (id) => <ChatScreen chatID={id as ID<Chat>} />,
})}
</div>
);
}
function AuthAndJazz({ children }: { children: React.ReactNode }) {
const [auth, state] = useDemoAuth();
return (
<Jazz.Provider
auth={auth}
peer="wss://mesh.jazz.tools/?key=chat-example-jazz@gcmp.io"
>
{state.state === "signedIn" ? (
children
) : (
<DemoAuthBasicUI appName="Jazz Chat" state={state} />
)}
</Jazz.Provider>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<AuthAndJazz>
<App />
</AuthAndJazz>
</StrictMode>
);

View File

@@ -1,42 +1,43 @@
import { ID } from 'jazz-tools';
import { Chat, Message, useCoState } from './app.tsx';
import { ID } from "jazz-tools";
import { Chat, Message } from "./schema.ts";
import { useCoState } from "./main.tsx";
import {
BubbleBody,
BubbleContainer,
BubbleInfo,
ChatContainer,
ChatInput,
EmptyChatMessage,
} from "./ui.tsx";
export function ChatScreen(props: { chatID: ID<Chat> }) {
const chat = useCoState(Chat, props.chatID, [{}]);
return chat ? <div className='w-full max-w-xl h-full flex flex-col items-stretch'>
{chat.length > 0
? chat.map((msg) => <ChatBubble msg={msg} key={msg.id} />)
: <div className='m-auto text-sm'>(Empty chat)</div>}
<ChatInput onSubmit={(text) => {
chat.push(
Message.create({ text }, { owner: chat._owner })
);
}} />
</div> : <div>Loading...</div>;
return chat ? (
<ChatContainer>
{chat.length > 0 ? (
chat.map(msg => <ChatBubble msg={msg} key={msg.id} />)
) : (
<EmptyChatMessage />
)}
<ChatInput
onSubmit={text => {
chat.push(Message.create({ text }, { owner: chat._owner }));
}}
/>
</ChatContainer>
) : (
<div>Loading...</div>
);
}
function ChatBubble(props: { msg: Message }) {
const lastEdit = props.msg._edits.text;
const align = lastEdit.by?.isMe ? 'items-end' : 'items-start';
return <div className={`${align} flex flex-col`}>
<div className='rounded-xl bg-stone-100 dark:bg-stone-700 dark:text-white py-2 px-4 mt-2 min-w-[5rem]'>
{ props.msg.text }
</div>
<div className='text-xs text-neutral-500 ml-2'>
{ lastEdit.by?.profile?.name }{' '}
{ lastEdit.madeAt?.toLocaleTimeString() }
</div>
</div>;
}
function ChatInput(props: { onSubmit: (text: string) => void }) {
return <input className='rounded p-2 border mt-auto dark:bg-black dark:text-white border-stone-300 dark:border-stone-700'
placeholder='Type a message and press Enter'
onKeyDown={({ key, currentTarget: input }) => {
if (key !== 'Enter' || !input.value) return;
props.onSubmit(input.value);
input.value = '';
}} />;
return (
<BubbleContainer fromMe={lastEdit.by?.isMe}>
<BubbleBody>{props.msg.text}</BubbleBody>
<BubbleInfo by={lastEdit.by?.profile?.name} madeAt={lastEdit.madeAt} />
</BubbleContainer>
);
}

View File

@@ -75,4 +75,4 @@
margin: 0;
padding: 0;
}
}
}

View File

@@ -0,0 +1,33 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { createJazzReactApp, DemoAuthBasicUI, useDemoAuth } from "jazz-react";
import { App } from "./app.tsx";
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = useDemoAuth();
console.log(state, auth)
return (
<>
<Jazz.Provider
auth={auth}
peer="wss://mesh.jazz.tools/?key=chat-example-jazz@gcmp.io"
>
{children}
</Jazz.Provider>
{state.state !== "signedIn" && <DemoAuthBasicUI appName="Jazz Chat" state={state} />}
</>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<JazzAndAuth>
<App />
</JazzAndAuth>
</StrictMode>
);

View File

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

63
examples/chat/src/ui.tsx Normal file
View File

@@ -0,0 +1,63 @@
export function AppContainer(props: { children: React.ReactNode }) {
return (
<div className="flex flex-col items-center justify-between w-screen h-screen p-2 dark:bg-black dark:text-white">
{props.children}
</div>
);
}
export function TopBar(props: { children: React.ReactNode }) {
return (
<div className="mb-5 px-2 py-1 text-sm self-end">{props.children}</div>
);
}
export function ChatContainer(props: { children: React.ReactNode }) {
return (
<div className="w-full max-w-xl h-full flex flex-col items-stretch">
{props.children}
</div>
);
}
export function EmptyChatMessage() {
return <div className="m-auto text-sm">(Empty chat)</div>;
}
export function BubbleContainer(props: {
children: React.ReactNode;
fromMe: boolean | undefined;
}) {
const align = props.fromMe ? "items-end" : "items-start";
return <div className={`${align} flex flex-col`}>{props.children}</div>;
}
export function BubbleBody(props: { children: React.ReactNode }) {
return (
<div className="rounded-xl bg-stone-100 dark:bg-stone-700 dark:text-white py-2 px-4 mt-2 min-w-[5rem]">
{props.children}
</div>
);
}
export function BubbleInfo(props: { by: string | undefined; madeAt: Date }) {
return (
<div className="text-xs text-neutral-500 ml-2">
{props.by} {props.madeAt.toLocaleTimeString()}
</div>
);
}
export function ChatInput(props: { onSubmit: (text: string) => void }) {
return (
<input
className="rounded p-2 border mt-auto dark:bg-black dark:text-white border-stone-300 dark:border-stone-700"
placeholder="Type a message and press Enter"
onKeyDown={({ key, currentTarget: input }) => {
if (key !== "Enter" || !input.value) return;
props.onSubmit(input.value);
input.value = "";
}}
/>
);
}

View File

@@ -1,11 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}",
],
theme: {
container: {
center: true,
@@ -72,4 +72,4 @@ module.exports = {
},
},
plugins: [require("tailwindcss-animate")],
}
};

View File

@@ -1,5 +1,5 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import path from "path";
// https://vitejs.dev/config/
@@ -11,6 +11,6 @@ export default defineConfig({
},
},
build: {
minify: false
}
})
minify: false,
},
});

View File

@@ -1,5 +1,13 @@
# jazz-example-musicplayer
## 0.0.4
### Patch Changes
- Updated dependencies
- jazz-tools@0.8.1
- jazz-react@0.8.1
## 0.0.3
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.3",
"version": "0.0.4",
"type": "module",
"scripts": {
"dev": "vite",
@@ -17,8 +17,8 @@
"dependencies": {
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.8.0",
"jazz-tools": "workspace:0.8.0",
"jazz-react": "workspace:0.8.1",
"jazz-tools": "workspace:0.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.16.0",

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -96,20 +96,23 @@ function Main() {
);
}
function AuthAndJazz({ children }: { children: React.ReactNode }) {
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = useDemoAuth();
return (
<Jazz.Provider auth={auth} peer="wss://mesh.jazz.tools/?key=music-player-example-jazz@gcmp.io">
{state.state === "signedIn" ? children : <DemoAuthBasicUI appName="Jazz Music Player" state={state} />}
</Jazz.Provider>
<>
<Jazz.Provider auth={auth} peer="wss://mesh.jazz.tools/?key=music-player-example-jazz@gcmp.io">
{children}
</Jazz.Provider>
<DemoAuthBasicUI appName="Jazz Music Player" state={state} />
</>
);
}
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<AuthAndJazz>
<JazzAndAuth>
<Main />
</AuthAndJazz>
</JazzAndAuth>
</React.StrictMode>,
);

View File

@@ -1,5 +1,13 @@
# jazz-password-manager
## 0.0.3
### Patch Changes
- Updated dependencies
- jazz-tools@0.8.1
- jazz-react@0.8.1
## 0.0.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-password-manager",
"private": true,
"version": "0.0.2",
"version": "0.0.3",
"type": "module",
"scripts": {
"dev": "vite",
@@ -11,8 +11,8 @@
"clean-install": "rm -rf node_modules pnpm-lock.yaml && pnpm install"
},
"dependencies": {
"jazz-react": "workspace:0.8.0",
"jazz-tools": "workspace:0.8.0",
"jazz-react": "workspace:0.8.1",
"jazz-tools": "workspace:0.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.41.5",

View File

@@ -15,29 +15,28 @@ const Jazz = createJazzReactApp<PasswordManagerAccount>({
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
function AuthAndJazz({ children }: { children: React.ReactNode }) {
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, state] = usePasskeyAuth({
appName: "Jazz Password Manager",
});
return (
<Jazz.Provider
auth={auth}
peer="wss://mesh.jazz.tools/?key=password-manager-example-jazz@gcmp.io"
>
{state.state === "signedIn" ? (
children
) : (
<PasskeyAuthBasicUI state={state} />
)}
</Jazz.Provider>
<>
<Jazz.Provider
auth={auth}
peer="wss://mesh.jazz.tools/?key=password-manager-example-jazz@gcmp.io"
>
{children}
</Jazz.Provider>
<PasskeyAuthBasicUI state={state} />
</>
);
}
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<AuthAndJazz>
<JazzAndAuth>
<App />
</AuthAndJazz>
</JazzAndAuth>
</React.StrictMode>
);

View File

@@ -1,5 +1,14 @@
# jazz-example-pets
## 0.0.101
### Patch Changes
- Updated dependencies
- jazz-tools@0.8.1
- jazz-browser-media-images@0.8.1
- jazz-react@0.8.1
## 0.0.100
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-pets",
"private": true,
"version": "0.0.100",
"version": "0.0.101",
"type": "module",
"scripts": {
"dev": "vite",
@@ -22,9 +22,9 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-browser-media-images": "workspace:0.8.0",
"jazz-react": "workspace:0.8.0",
"jazz-tools": "workspace:0.8.0",
"jazz-browser-media-images": "workspace:0.8.1",
"jazz-react": "workspace:0.8.1",
"jazz-tools": "workspace:0.8.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -36,7 +36,7 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
auth={auth}
peer="wss://mesh.jazz.tools/?key=pets-example-jazz@gcmp.io"
>
{authState.state === "signedIn" && children}
{children}
</Jazz.Provider>
<DemoAuthBasicUI appName={appName} state={authState} />
</>
@@ -100,7 +100,7 @@ export default function App() {
}
variant="outline"
>
Log Out
Log out
</Button>
</>
);

View File

@@ -9,7 +9,7 @@ export class LoginPage {
this.page = page;
this.usernameInput = page.getByRole("textbox");
this.signupButton = page.getByRole("button", {
name: "Sign Up as new account",
name: "Sign up as new account",
});
}
@@ -25,7 +25,7 @@ export class LoginPage {
async loginAs(value: string) {
await this.page
.getByRole("button", {
name: `Log In as "${value}"`,
name: `Log in as "${value}"`,
})
.click();
}

View File

@@ -1,5 +1,13 @@
# jazz-example-todo
## 0.0.100
### Patch Changes
- Updated dependencies
- jazz-tools@0.8.1
- jazz-react@0.8.1
## 0.0.99
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-todo",
"private": true,
"version": "0.0.99",
"version": "0.0.100",
"type": "module",
"scripts": {
"dev": "vite",
@@ -20,8 +20,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.8.0",
"jazz-tools": "workspace:0.8.0",
"jazz-react": "workspace:0.8.1",
"jazz-tools": "workspace:0.8.1",
"lucide-react": "^0.274.0",
"qrcode": "^1.5.3",
"react": "^18.2.0",

View File

@@ -41,16 +41,15 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName });
return (
<Jazz.Provider
auth={passkeyAuth}
peer="wss://mesh.jazz.tools/?key=todo-example-jazz@gcmp.io"
>
{passKeyState.state === "signedIn" ? (
children
) : (
<PasskeyAuthBasicUI state={passKeyState} />
)}
</Jazz.Provider>
<>
<Jazz.Provider
auth={passkeyAuth}
peer="wss://mesh.jazz.tools/?key=todo-example-jazz@gcmp.io"
>
{children}
</Jazz.Provider>
<PasskeyAuthBasicUI state={passKeyState} />
</>
);
}
@@ -111,7 +110,7 @@ export default function App() {
}
variant="outline"
>
Log Out
Log out
</Button>
</>
);

View File

@@ -11,7 +11,7 @@ export function CodeRef({ children }: { children: React.ReactNode }) {
"border",
"text-stone-800 dark:text-stone-200",
"bg-stone-100 dark:bg-stone-800",
"border-stone-200 dark:border-stone-800"
"border-stone-200 dark:border-stone-900"
)}
>
{children}

View File

@@ -6,11 +6,11 @@ export function GridCard(props: { children: ReactNode; className?: string }) {
<div
className={clsx(
"col-span-2 p-4 [&>h4]:mt-0 [&>h3]:mt-0 [&>:last-child]:mb-0",
"border border-stone-200 dark:border-stone-800 rounded-xl shadow-sm",
"border border-stone-200 dark:border-stone-900 rounded-xl shadow-sm",
props.className
)}
>
{props.children}
</div>
);
}
}

View File

@@ -1,10 +1,17 @@
import clsx from "clsx";
export function H1({ children }: { children: React.ReactNode }) {
interface HeadingProps {
children: React.ReactNode;
className?: string;
}
export function H1({ children, className }: HeadingProps) {
return (
<h1
className={clsx(
className,
"font-display",
"text-stone-950 dark:text-white",
"text-5xl lg:text-6xl",
"mb-3",
"font-medium",
@@ -16,11 +23,13 @@ export function H1({ children }: { children: React.ReactNode }) {
);
}
export function H2({ children }: { children: React.ReactNode }) {
export function H2({ children, className }: HeadingProps) {
return (
<h2
className={clsx(
className,
"font-display",
"text-stone-950 dark:text-white",
"text-2xl",
"mb-2",
"font-semibold",
@@ -32,11 +41,13 @@ export function H2({ children }: { children: React.ReactNode }) {
);
}
export function H3({ children }: { children: React.ReactNode }) {
export function H3({ children, className }: HeadingProps) {
return (
<h3
className={clsx(
className,
"font-display",
"text-stone-950 dark:text-white",
"text-xl",
"mb-2",
"font-semibold",
@@ -48,6 +59,6 @@ export function H3({ children }: { children: React.ReactNode }) {
);
}
export function H4({ children }: { children: React.ReactNode }) {
return <h4 className="text-bold">{children}</h4>;
export function H4({ children, className }: HeadingProps) {
return <h4 className={clsx(className, "text-bold")}>{children}</h4>;
}

View File

@@ -1,5 +1,6 @@
import clsx from "clsx";
import { ReactNode } from "react";
import { H2 } from "../../components/atoms/Headings";
export function GappedGrid({
children,
@@ -18,14 +19,8 @@ export function GappedGrid({
className
)}
>
{title && <h2 className={clsx(
"col-span-full",
"font-display",
"text-2xl",
"font-semibold",
"tracking-tight"
)}>{title}</h2>}
{title && <H2 className="col-span-full">{title}</H2>}
{children}
</div>
);
}
}

View File

@@ -8,7 +8,6 @@ export function HairlineBleedGrid({ children }: { children: React.ReactNode }) {
"mb-10",
"items-stretch",
"gap-[1px]",
"-mx-4 md:-mx-6",
"rounded-xl",
"overflow-hidden",
"bg-stone-50 dark:bg-stone-950",

View File

@@ -29,7 +29,7 @@ export function HeroHeader({
pt?: boolean;
}) {
return (
<hgroup className={clsx(pt && "md:pt-20", "mb-10")}>
<hgroup className={clsx(pt && "pt-12 md:pt-20", "mb-10")}>
<H1>{title}</H1>
<H1Sub>{slogan}</H1Sub>
</hgroup>

View File

@@ -13,13 +13,15 @@ export function LabelledFeatureIcon({
className={clsx(
"p-4 flex flex-col items-center justify-center gap-2",
"not-prose text-base",
"border border-stone-200 dark:border-stone-800 rounded-xl"
"border border-stone-200 dark:border-stone-900 rounded-xl"
)}
>
<div className="text-stone-500 mr-2">
<Icon strokeWidth={1} strokeLinecap="butt" size={40} />
</div>
<div className="text-stone-700 dark:text-stone-300">{label}</div>
<div className="text-sm text-stone-700 md:text-base dark:text-stone-300">
{label}
</div>
</div>
);
}

View File

@@ -13,7 +13,7 @@ export function Prose(props: { children: ReactNode; className?: string }) {
"prose-p:max-w-3xl prose-p:leading-snug",
"prose-strong:font-medium",
"prose-code:font-normal prose-code:leading-tight prose-code:before:content-none prose-code:after:content-none prose-code:bg-stone-100 prose-code:dark:bg-stone-900 prose-code:p-1 prose-code:rounded",
"prose-pre:text-black dark:prose-pre:text-white prose-pre:max-w-3xl prose-pre:text-[0.8em] prose-pre:leading-[1.3] prose-pre:-mt-2 prose-pre:my-4 prose-pre:px-10 prose-pre:py-2 prose-pre:-mx-10 prose-pre:bg-transparent",
"prose-pre:text-black dark:prose-pre:text-white prose-pre:max-w-3xl prose-pre:text-[0.8em] prose-pre:leading-[1.3] prose-pre:-mt-2 prose-pre:my-4 prose-pre:px-10 prose-pre:py-2 prose-pre:bg-transparent",
"[&_pre_.line]:relative [&_pre_.line]:min-h-[1.3em] [&_pre_.lineNo]:text-[0.75em] [&_pre_.lineNo]:text-stone-300 [&_pre_.lineNo]:dark:text-stone-700 [&_pre_.lineNo]:absolute [&_pre_.lineNo]:text-right [&_pre_.lineNo]:w-8 [&_pre_.lineNo]:-left-10 [&_pre_.lineNo]:top-[0.3em] [&_pre_.lineNo]:select-none",
props.className || "prose lg:prose-lg"
)}
@@ -25,4 +25,4 @@ export function Prose(props: { children: ReactNode; className?: string }) {
export function SmallProse(props: { children: ReactNode; className?: string }) {
return <Prose className="prose prose-sm">{props.children}</Prose>;
}
}

View File

@@ -34,7 +34,7 @@ export function Footer({ logo, companyName, sections }: FooterProps) {
</div>
{sections.map((section, index) => (
<div key={index} className="flex flex-col gap-2 text-sm">
<h1 className="font-medium">{section.title}</h1>
<h2 className="font-medium">{section.title}</h2>
{section.links.map((link, linkIndex) => (
<FooterLink
key={linkIndex}

View File

@@ -1,7 +1,7 @@
<h2 className="not-prose text-2xl font-medium tracking-tight mt-3 md:mt-5 mb-6">Hard things are easy now.</h2>
<h2 className="not-prose text-2xl font-medium tracking-tight mt-3 md:mt-5 mb-6 dark:text-white">Hard things are easy now.</h2>
Jazz is an **open-source toolkit** that replaces APIs, databases and message queues with **a&nbsp;single new abstraction:**
**&ldquo;Collaborative Values&rdquo;** &mdash; **distributed state** with **secure permissions built-in.**
Features that used to take months to build now work out-of-the-box.
Features that used to take months to build now work out-of-the-box.

View File

@@ -27,8 +27,13 @@ import {
MousePointerSquareDashedIcon,
} from "lucide-react";
import { App_tsx, ChatScreen_tsx } from "@/codeSamples/examples/chat/src";
import Link from "next/link";
import {
Main_tsx,
Schema_ts,
App_tsx,
ChatScreen_tsx,
Ui_tsx,
} from "@/codeSamples/examples/chat/src";
import Intro from "./intro.mdx";
import CoValuesIntro from "./coValuesIntro.mdx";
@@ -52,7 +57,7 @@ import { H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
export default function Home() {
return (
<>
<div className="space-y-16">
<HeroHeader
title="Instant sync."
slogan="A new way to build apps with distributed state."
@@ -99,41 +104,56 @@ export default function Home() {
</div>
</HairlineBleedGrid>
<div className="-mx-[calc(min(0,(100vw-95rem)/2))]">
<div>
<SectionHeader
title="First impressions..."
slogan="A chat app in 84 lines of code."
slogan="A chat app in 174 lines of code."
/>
<GappedGrid className="mt-0 -mx-4 md:-mx-6">
<GappedGrid className="mt-0">
<CodeExampleTabs
tabs={[
{
name: "main.tsx",
content: <Main_tsx />,
},
{
name: "app.tsx",
content: <App_tsx />,
},
{
name: "schema.ts",
content: <Schema_ts />,
},
{
name: "chatScreen.tsx",
content: <ChatScreen_tsx />,
},
{
name: "ui.tsx",
content: <Ui_tsx />,
},
]}
className="col-span-full md:col-span-4"
/>
<ResponsiveIframe
src="https://chat.jazz.tools"
localSrc="http://localhost:5173"
className="col-span-full lg:col-span-2 rounded-xl overflow-hidden min-h-[50vh]"
className="order-first col-span-full lg:col-span-2 rounded-sm overflow-hidden min-h-[50vh] lg:order-last"
/>
</GappedGrid>
</div>
<SectionHeader
title="Collaborative Values"
slogan="Your new building blocks."
/>
<Prose>
<CoValuesIntro />
</Prose>
<div>
<SectionHeader
title="Collaborative Values"
slogan="Your new building blocks."
/>
<Prose>
<CoValuesIntro />
</Prose>
</div>
<GappedGrid
title="Bread-and-butter datastructures"
@@ -222,15 +242,17 @@ export default function Home() {
</GridCard>
</GappedGrid>
<SectionHeader
title="The Jazz Toolkit"
slogan="A high-level toolkit for building apps around CoValues."
/>
<div>
<SectionHeader
title="The Jazz Toolkit"
slogan="A high-level toolkit for building apps around CoValues."
/>
<Prose>Supported environments:</Prose>
<SmallProse>
<SupportedEnvironments />
</SmallProse>
<Prose>Supported environments:</Prose>
<SmallProse>
<SupportedEnvironments />
</SmallProse>
</div>
<GappedGrid>
<GridCard>
@@ -299,38 +321,42 @@ export default function Home() {
</GridCard>
</GappedGrid>
<SectionHeader
title="Jazz Mesh"
slogan="Serverless sync & storage for Jazz apps"
/>
<div>
<SectionHeader
title="Jazz Mesh"
slogan="Serverless sync & storage for Jazz apps"
/>
<Prose>
<MeshIntro />
</Prose>
<Prose>
<MeshIntro />
</Prose>
<P>
{"->"}{" "}
<TextLink href="/mesh" target="_blank">
Learn more about Jazz Mesh
</TextLink>
</P>
<H3>Get Started</H3>
<UL>
<LI>
<TextLink href="/docs" target="_blank">
Read the docs
<P>
{"->"}{" "}
<TextLink href="/mesh" target="_blank">
Learn more about Jazz Mesh
</TextLink>
</LI>
<LI>
<TextLink
href="https://discord.gg/utDMjHYg42"
target="_blank"
>
Join our Discord
</TextLink>
</LI>
</UL>
</>
</P>
</div>
<div>
<H3>Get Started</H3>
<UL>
<LI>
<TextLink href="/docs" target="_blank">
Read the docs
</TextLink>
</LI>
<LI>
<TextLink
href="https://discord.gg/utDMjHYg42"
target="_blank"
>
Join our Discord
</TextLink>
</LI>
</UL>
</div>
</div>
);
}

View File

@@ -1,11 +1,8 @@
import { ComingSoonBadge } from "gcmp-design-system/src/app/components/atoms/ComingSoonBadge";
Supported environments:
- Browser (sync via WebSockets, IndexedDB persistence)
- React
- Vanilla JS / framework agnostic base
- React Native <ComingSoonBadge/>
- NodeJS (sync via WebSockets, SQLite persistence) <ComingSoonBadge/>
- Swift, Kotlin, Rust <ComingSoonBadge when="later"/>
- Swift, Kotlin, Rust <ComingSoonBadge when="later"/>

View File

@@ -69,7 +69,7 @@ We'll build everything **step-by-step,** in typical, immediately usable stages.
// replace `you@example.com` with your email as a temporary API key
peer="wss://mesh.jazz.tools/?key=you@example.com"
>
{ authState.state === "signedIn" && children }
{children}
</Jazz.Provider>
<DemoAuthBasicUI appName="Circular" state={authState} />
</>
@@ -354,7 +354,7 @@ This works because CoValues
- notify subscribers of the change (who will receive a fresh, updated view of the CoValue)
<aside className="text-sm border border-stone-300 dark:border-stone-700 rounded px-4 my-4 max-w-3xl [&_pre]:mx-0">
<h4 className="not-prose text-base py-2 mb-3 -mx-4 px-4 border-b border-stone-300 dark:border-stone-700">💡 A Quick Overview of Subscribing to CoValues</h4>
<h4 className="not-prose text-base py-2 mb-3 px-4 border-b border-stone-300 dark:border-stone-700">💡 A Quick Overview of Subscribing to CoValues</h4>
There are three main ways to subscribe to a CoValue:

View File

@@ -72,9 +72,7 @@
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
.overlay-close {
background-color: "black";
}
@@ -134,7 +132,7 @@ pre.twoslash data-lsp::before {
position: absolute;
transform: translate(0, 1.2rem);
max-width: 30rem;
@apply text-xs px-1.5 py-1 rounded border border-stone-200 dark:border-stone-800 shadow-lg overflow-hidden whitespace-pre-wrap text-stone-700 bg-stone-50 dark:text-stone-200 dark:bg-stone-950;
@apply text-xs px-1.5 py-1 rounded border border-stone-200 dark:border-stone-900 shadow-lg overflow-hidden whitespace-pre-wrap text-stone-700 bg-stone-50 dark:text-stone-200 dark:bg-stone-950;
text-align: left;
z-index: 100;
opacity: 0;

View File

@@ -57,7 +57,7 @@ export default function RootLayout({
manrope.variable,
commitMono.variable,
inter.className,
"flex flex-col items-center bg-stone-50 dark:bg-stone-950",
"flex flex-col items-center bg-stone-50 text-stone-700 dark:text-stone-400 dark:bg-stone-950",
].join(" ")}
>
<SpeedInsights />
@@ -69,7 +69,7 @@ export default function RootLayout({
disableTransitionOnChange
>
<JazzNav />
<main className="flex min-h-screen flex-col p-8 max-w-[80rem] w-full [&_*]:scroll-mt-[6rem]">
<main className="flex min-h-screen flex-col container w-full">
{children}
</main>
<JazzFooter />

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