Compare commits
103 Commits
jazz-run@0
...
jazz-run@0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44497fd058 | ||
|
|
a18c187818 | ||
|
|
f49949cc25 | ||
|
|
3cc6aee7e6 | ||
|
|
c80763100e | ||
|
|
6ed75ebb35 | ||
|
|
30d7734124 | ||
|
|
bca8b44189 | ||
|
|
36a069c90f | ||
|
|
757b37e8ed | ||
|
|
46b1163058 | ||
|
|
8c261b0409 | ||
|
|
fdacde57dd | ||
|
|
817dd7dde1 | ||
|
|
6279dd1467 | ||
|
|
abd4b94392 | ||
|
|
68c6ee77c0 | ||
|
|
d2b2812428 | ||
|
|
fe214cc3c2 | ||
|
|
2cca9506ad | ||
|
|
17e69aff8f | ||
|
|
c45331e645 | ||
|
|
4337001526 | ||
|
|
9eef1ec031 | ||
|
|
9d31bbc3aa | ||
|
|
911add3dea | ||
|
|
eed9ad99d8 | ||
|
|
a1d0793e95 | ||
|
|
ac9aad2ff5 | ||
|
|
1ed4ab5a8e | ||
|
|
b03aed69b6 | ||
|
|
1120d84c89 | ||
|
|
4e4671cba2 | ||
|
|
cdca330f1e | ||
|
|
bf8840cfbc | ||
|
|
89d4da8d50 | ||
|
|
74e441d233 | ||
|
|
58e5682e3d | ||
|
|
4915422646 | ||
|
|
2337d66ec5 | ||
|
|
e2c73d3c3d | ||
|
|
e4c0314368 | ||
|
|
228cd42b23 | ||
|
|
1c80492482 | ||
|
|
bfbd5c514b | ||
|
|
520bcfd72a | ||
|
|
566fde6ecc | ||
|
|
c44b5db09a | ||
|
|
477d0aa899 | ||
|
|
60171b477f | ||
|
|
9d4fb4d32b | ||
|
|
d5b9544f6b | ||
|
|
357e6ba59f | ||
|
|
0aca58af1e | ||
|
|
790caf6f0d | ||
|
|
d4fbae2bf3 | ||
|
|
da3bc74fe5 | ||
|
|
a1bd53100b | ||
|
|
46d8b19918 | ||
|
|
8ed31452b9 | ||
|
|
dfb8c3f329 | ||
|
|
de0045e2ee | ||
|
|
7dfb485558 | ||
|
|
81ac9d0dfd | ||
|
|
8759bccc02 | ||
|
|
5dd0e31e2a | ||
|
|
a2c1586d26 | ||
|
|
7000698629 | ||
|
|
f52511721e | ||
|
|
94e84dd5bd | ||
|
|
ee0b1695fd | ||
|
|
e338e9005b | ||
|
|
e20c4a2d76 | ||
|
|
675e72aec3 | ||
|
|
f8945e99a9 | ||
|
|
830a007a08 | ||
|
|
b6f7c61445 | ||
|
|
e37369027e | ||
|
|
47eedc4433 | ||
|
|
3f78c011df | ||
|
|
fbb22f1e9d | ||
|
|
280e1e25e3 | ||
|
|
c7306a0c57 | ||
|
|
d40ef934e8 | ||
|
|
3e8d4fdd53 | ||
|
|
e4180308ea | ||
|
|
2b48954693 | ||
|
|
49d8287b24 | ||
|
|
b6af85c91e | ||
|
|
308ab80736 | ||
|
|
b759f29ab8 | ||
|
|
b826adabbf | ||
|
|
2b4e839926 | ||
|
|
c7aae14e03 | ||
|
|
263fe7a4b3 | ||
|
|
8049f1d972 | ||
|
|
f3263e60ff | ||
|
|
6d6849707e | ||
|
|
11ac24e3f4 | ||
|
|
7a30f3c025 | ||
|
|
c84f96c436 | ||
|
|
0ecf666c9f | ||
|
|
42e4969f92 |
51
.github/workflows/jazz-run.yml
vendored
Normal file
51
.github/workflows/jazz-run.yml
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
name: Jazz Run Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
|
||||
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: Build jazz-run
|
||||
run: pnpm exec turbo build && chmod +x dist/index.js;
|
||||
working-directory: ./packages/jazz-run
|
||||
|
||||
- name: Run create account
|
||||
run: ./dist/index.js account create --name "Jazz Run CI test"
|
||||
working-directory: ./packages/jazz-run
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# @jazz-e2e/binarycostream
|
||||
|
||||
## 0.0.90
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6ed75eb]
|
||||
- Updated dependencies [3cc6aee]
|
||||
- cojson@0.8.12
|
||||
- jazz-react@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
|
||||
## 0.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1ed4ab5]
|
||||
- cojson@0.8.11
|
||||
- jazz-react@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@jazz-e2e/binarycostream",
|
||||
"private": true,
|
||||
"version": "0.0.88",
|
||||
"version": "0.0.90",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -14,11 +14,11 @@
|
||||
"*.{js,jsx,mdx,json}": "prettier --write"
|
||||
},
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.8.5",
|
||||
"cojson": "workspace:0.8.12",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"is-ci": "^3.0.1",
|
||||
"jazz-react": "workspace:0.8.7",
|
||||
"jazz-tools": "workspace:0.8.5",
|
||||
"jazz-react": "workspace:0.8.12",
|
||||
"jazz-tools": "workspace:0.8.12",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
|
||||
@@ -27,10 +27,11 @@ export default defineConfig({
|
||||
/* 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: isCI ? "http://localhost:4173/" : "http://localhost:5173",
|
||||
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 */
|
||||
@@ -42,8 +43,11 @@ export default defineConfig({
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: isCI ? {
|
||||
command: "pnpm preview",
|
||||
url: "http://localhost:4173/",
|
||||
} : undefined,
|
||||
webServer: [
|
||||
{
|
||||
command: "pnpm preview --port 5173",
|
||||
url: "http://localhost:5173/",
|
||||
reuseExistingServer: !isCI,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# @jazz-e2e/covalues
|
||||
|
||||
## 0.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6ed75eb]
|
||||
- Updated dependencies [3cc6aee]
|
||||
- cojson@0.8.12
|
||||
- jazz-react@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1ed4ab5]
|
||||
- cojson@0.8.11
|
||||
- jazz-react@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
|
||||
## 0.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@jazz-e2e/covalues",
|
||||
"private": true,
|
||||
"version": "0.0.87",
|
||||
"version": "0.0.89",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -27,10 +27,11 @@ export default defineConfig({
|
||||
/* 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: isCI ? "http://localhost:4173/" : "http://localhost:5173",
|
||||
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 */
|
||||
@@ -42,8 +43,11 @@ export default defineConfig({
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: isCI ? {
|
||||
command: "pnpm preview",
|
||||
url: "http://localhost:4173/",
|
||||
} : undefined,
|
||||
webServer: [
|
||||
{
|
||||
command: "pnpm preview --port 5173",
|
||||
url: "http://localhost:5173/",
|
||||
reuseExistingServer: !isCI,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -2,15 +2,20 @@ import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { AuthAndJazz } from "./jazz";
|
||||
import { TestInput } from "./pages/TestInput";
|
||||
import { RouterProvider, createHashRouter } from "react-router-dom";
|
||||
import { ResumeSyncState } from "./pages/ResumeSyncState";
|
||||
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
||||
|
||||
const router = createHashRouter([
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
path: "/test-input",
|
||||
element: <TestInput />,
|
||||
},
|
||||
{
|
||||
path: "/test-input",
|
||||
path: "/resume-sync",
|
||||
element: <ResumeSyncState />,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
element: <TestInput />,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
import { createJazzReactApp } from "jazz-react";
|
||||
import { ephemeralCredentialsAuth } from "jazz-tools";
|
||||
import { useState } from "react";
|
||||
import { createJazzReactApp, useDemoAuth } from "jazz-react";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
const key = `test-comap@jazz.tools`;
|
||||
|
||||
const localSync = new URLSearchParams(location.search).has("localSync");
|
||||
const url = new URL(window.location.href);
|
||||
const peer =
|
||||
(url.searchParams.get("peer") as `ws://${string}`) ??
|
||||
`wss://cloud.jazz.tools/`;
|
||||
|
||||
const Jazz = createJazzReactApp();
|
||||
|
||||
export const { useAccount, useCoState } = Jazz;
|
||||
|
||||
export function AuthAndJazz({ children }: { children: React.ReactNode }) {
|
||||
const [ephemeralAuth] = useState(ephemeralCredentialsAuth())
|
||||
|
||||
return (
|
||||
<Jazz.Provider auth={ephemeralAuth} peer={
|
||||
localSync
|
||||
? `ws://localhost:4200?key=${key}`
|
||||
: `wss://cloud.jazz.tools/?key=${key}`
|
||||
}>
|
||||
{children}
|
||||
</Jazz.Provider>
|
||||
);
|
||||
function getUserInfo() {
|
||||
return url.searchParams.get("userName") ?? "Mister X";
|
||||
}
|
||||
|
||||
export function AuthAndJazz({ children }: { children: React.ReactNode }) {
|
||||
const [auth, state] = useDemoAuth();
|
||||
|
||||
const signedUp = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (state.state === "ready" && !signedUp.current) {
|
||||
const userName = getUserInfo();
|
||||
|
||||
if (state.existingUsers.includes(userName)) {
|
||||
state.logInAs(userName);
|
||||
} else {
|
||||
state.signUp(userName);
|
||||
}
|
||||
|
||||
signedUp.current = true;
|
||||
}
|
||||
}, [state.state]);
|
||||
|
||||
return (
|
||||
<Jazz.Provider auth={auth} peer={`${peer}?key=${key}`}>
|
||||
{children}
|
||||
</Jazz.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
53
e2e/CoValues/src/pages/ResumeSyncState.tsx
Normal file
53
e2e/CoValues/src/pages/ResumeSyncState.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { co, CoMap, Group, ID } from "jazz-tools";
|
||||
import { useAccount, useCoState } from "../jazz";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export class ResumeSyncCoMap extends CoMap {
|
||||
value = co.string;
|
||||
}
|
||||
|
||||
function getIdParam() {
|
||||
const url = new URL(window.location.href);
|
||||
return (url.searchParams.get("id") as ID<ResumeSyncCoMap>) ?? undefined;
|
||||
}
|
||||
|
||||
export function ResumeSyncState() {
|
||||
const [id, setId] = useState(getIdParam);
|
||||
const coMap = useCoState(ResumeSyncCoMap, id);
|
||||
const { me } = useAccount();
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set("id", id);
|
||||
history.pushState({}, "", url.toString());
|
||||
}
|
||||
}, [id])
|
||||
|
||||
useEffect(() => {
|
||||
if (!me || id) return;
|
||||
|
||||
const group = Group.create({ owner: me });
|
||||
|
||||
group.addMember("everyone", "writer");
|
||||
|
||||
setId(ResumeSyncCoMap.create({ value: "" }, { owner: group }).id);
|
||||
}, [me]);
|
||||
|
||||
if (!coMap) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Resume Sync State</h1>
|
||||
<p data-testid="id">{coMap.id}</p>
|
||||
<label htmlFor="value">Value</label>
|
||||
<input
|
||||
id="value"
|
||||
value={coMap.value ?? ""}
|
||||
onChange={(e) => {
|
||||
coMap.value = e.target.value;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
47
e2e/CoValues/tests/ResumeSyncState.spec.ts
Normal file
47
e2e/CoValues/tests/ResumeSyncState.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { setTimeout } from "node:timers/promises";
|
||||
|
||||
test.describe("ResumeSyncState", () => {
|
||||
test.skip("should resume the sync even after a page reload", async ({ page, browser }) => {
|
||||
const context = page.context();
|
||||
|
||||
await page.goto("/resume-sync?userName=SuperMario");
|
||||
|
||||
const id = await page.getByTestId("id").textContent();
|
||||
|
||||
// Sync an initial value
|
||||
await page.getByRole("textbox", { name: "Value" }).fill("Let's go!");
|
||||
await setTimeout(1000);
|
||||
|
||||
await context.setOffline(true);
|
||||
|
||||
// Change the value while offline
|
||||
await page.getByRole("textbox", { name: "Value" }).fill("Mammamia!");
|
||||
|
||||
// Navigate away from the page
|
||||
await page.goto(`about:blank`);
|
||||
|
||||
await setTimeout(1000);
|
||||
await context.setOffline(false);
|
||||
|
||||
// Reload the page but without loading the coValue
|
||||
// await page.goto(`/resume-sync?userName=SuperMario`);
|
||||
await page.goto(`/resume-sync?userName=SuperMario`);
|
||||
|
||||
await setTimeout(1000);
|
||||
|
||||
await expect(page.getByTestId("id")).toBeInViewport();
|
||||
|
||||
// Create a new incognito instance and try to load the coValue
|
||||
const newUserPage = await (await browser.newContext()).newPage();
|
||||
await newUserPage.goto(`/resume-sync?userName=Luigi&id=${id}`);
|
||||
|
||||
await expect(newUserPage.getByTestId("id")).toBeInViewport();
|
||||
|
||||
// The initial user should have synced the value even if the coValue was not loaded
|
||||
// when the user is back online
|
||||
await expect(newUserPage.getByRole("textbox", { name: "Value" })).toHaveValue("Mammamia!", {
|
||||
timeout: 20_000
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,22 @@
|
||||
# jazz-example-book-shelf
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3cc6aee]
|
||||
- jazz-react@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
- jazz-browser-media-images@0.8.12
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
- jazz-browser-media-images@0.8.11
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-example-book-shelf",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
@@ -11,9 +11,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-browser-media-images": "workspace:0.8.7",
|
||||
"jazz-react": "workspace:0.8.7",
|
||||
"jazz-tools": "workspace:0.8.5",
|
||||
"jazz-browser-media-images": "workspace:0.8.12",
|
||||
"jazz-react": "workspace:0.8.12",
|
||||
"jazz-tools": "workspace:0.8.12",
|
||||
"next": "14.2.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6ed75eb]
|
||||
- Updated dependencies [3cc6aee]
|
||||
- cojson@0.8.12
|
||||
- jazz-react@0.8.12
|
||||
- jazz-react-auth-clerk@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1ed4ab5]
|
||||
- cojson@0.8.11
|
||||
- jazz-react@0.8.11
|
||||
- jazz-react-auth-clerk@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
|
||||
## 0.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat-clerk",
|
||||
"private": true,
|
||||
"version": "0.0.87",
|
||||
"version": "0.0.89",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -21,11 +21,11 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"cojson": "workspace:0.8.5",
|
||||
"cojson": "workspace:0.8.12",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"jazz-react": "workspace:0.8.7",
|
||||
"jazz-react-auth-clerk": "workspace:0.8.7",
|
||||
"jazz-tools": "workspace:0.8.5",
|
||||
"jazz-react": "workspace:0.8.12",
|
||||
"jazz-react-auth-clerk": "workspace:0.8.12",
|
||||
"jazz-tools": "workspace:0.8.12",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# chat-rn-clerk
|
||||
|
||||
## 1.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-auth-clerk@0.8.12
|
||||
- jazz-react-native@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
- jazz-react-native-media-images@0.8.8
|
||||
|
||||
## 1.0.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-auth-clerk@0.8.11
|
||||
- jazz-react-native@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
- jazz-react-native-media-images@0.8.7
|
||||
|
||||
## 1.0.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.5",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
"start": "expo start",
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# chat-rn
|
||||
|
||||
## 1.0.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
|
||||
## 1.0.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
|
||||
## 1.0.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.7",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.91
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6ed75eb]
|
||||
- Updated dependencies [3cc6aee]
|
||||
- cojson@0.8.12
|
||||
- jazz-react@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
|
||||
## 0.0.90
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1ed4ab5]
|
||||
- cojson@0.8.11
|
||||
- jazz-react@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
|
||||
## 0.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat",
|
||||
"private": true,
|
||||
"version": "0.0.89",
|
||||
"version": "0.0.91",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -22,10 +22,10 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"cojson": "workspace:0.8.5",
|
||||
"cojson": "workspace:0.8.12",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"jazz-react": "workspace:0.8.7",
|
||||
"jazz-tools": "workspace:0.8.5",
|
||||
"jazz-react": "workspace:0.8.12",
|
||||
"jazz-tools": "workspace:0.8.12",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -27,7 +27,7 @@ export default defineConfig({
|
||||
/* 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: isCI ? "http://localhost:4173/" : "http://localhost:5173",
|
||||
baseURL: "http://localhost:5173/",
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: "on-first-retry",
|
||||
@@ -43,8 +43,11 @@ export default defineConfig({
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: isCI ? {
|
||||
command: "pnpm preview",
|
||||
url: "http://localhost:4173/",
|
||||
} : undefined,
|
||||
webServer: [
|
||||
{
|
||||
command: "pnpm preview --port 5173",
|
||||
url: "http://localhost:5173/",
|
||||
reuseExistingServer: !isCI,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# jazz-example-inspector
|
||||
|
||||
## 0.0.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [6ed75eb]
|
||||
- cojson@0.8.12
|
||||
- cojson-transport-ws@0.8.12
|
||||
|
||||
## 0.0.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [1ed4ab5]
|
||||
- cojson@0.8.11
|
||||
- cojson-transport-ws@0.8.11
|
||||
|
||||
## 0.0.65
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-inspector",
|
||||
"private": true,
|
||||
"version": "0.0.65",
|
||||
"version": "0.0.67",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -15,8 +15,8 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"cojson": "workspace:0.8.5",
|
||||
"cojson-transport-ws": "workspace:0.8.7",
|
||||
"cojson": "workspace:0.8.12",
|
||||
"cojson-transport-ws": "workspace:0.8.12",
|
||||
"hash-slash": "workspace:0.2.1",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# jazz-example-musicplayer
|
||||
|
||||
## 0.0.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3cc6aee]
|
||||
- jazz-react@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
|
||||
## 0.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-music-player",
|
||||
"private": true,
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.11",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -22,8 +22,8 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-react": "workspace:0.8.7",
|
||||
"jazz-tools": "workspace:0.8.5",
|
||||
"jazz-react": "workspace:0.8.12",
|
||||
"jazz-tools": "workspace:0.8.12",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# jazz-password-manager
|
||||
|
||||
## 0.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3cc6aee]
|
||||
- jazz-react@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-password-manager",
|
||||
"private": true,
|
||||
"version": "0.0.8",
|
||||
"version": "0.0.10",
|
||||
"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.7",
|
||||
"jazz-tools": "workspace:0.8.5",
|
||||
"jazz-react": "workspace:0.8.12",
|
||||
"jazz-tools": "workspace:0.8.12",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.41.5",
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# jazz-example-pets
|
||||
|
||||
## 0.0.108
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3cc6aee]
|
||||
- jazz-react@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
- jazz-browser-media-images@0.8.12
|
||||
|
||||
## 0.0.107
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
- jazz-browser-media-images@0.8.11
|
||||
|
||||
## 0.0.106
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-pets",
|
||||
"private": true,
|
||||
"version": "0.0.106",
|
||||
"version": "0.0.108",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -23,9 +23,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.7",
|
||||
"jazz-react": "workspace:0.8.7",
|
||||
"jazz-tools": "workspace:0.8.5",
|
||||
"jazz-browser-media-images": "workspace:0.8.12",
|
||||
"jazz-react": "workspace:0.8.12",
|
||||
"jazz-tools": "workspace:0.8.12",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
@@ -50,7 +50,7 @@
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.3",
|
||||
"is-ci": "^3.0.1",
|
||||
"jazz-run": "workspace:0.8.10",
|
||||
"jazz-run": "workspace:0.8.12",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "3.3.2",
|
||||
"typescript": "^5.3.3",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# jazz-example-todo
|
||||
|
||||
## 0.0.107
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [3cc6aee]
|
||||
- jazz-react@0.8.12
|
||||
- jazz-tools@0.8.12
|
||||
|
||||
## 0.0.106
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.8.11
|
||||
- jazz-tools@0.8.11
|
||||
|
||||
## 0.0.105
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.105",
|
||||
"version": "0.0.107",
|
||||
"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.7",
|
||||
"jazz-tools": "workspace:0.8.5",
|
||||
"jazz-react": "workspace:0.8.12",
|
||||
"jazz-tools": "workspace:0.8.12",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.436.0",
|
||||
"next": "14.2.7",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"tailwind-merge": "^1.14.0",
|
||||
|
||||
@@ -28,7 +28,7 @@ export function Button(props: ButtonProps) {
|
||||
primary:
|
||||
"bg-blue border-blue text-white font-medium bg-blue hover:bg-blue-800 hover:border-blue-800",
|
||||
secondary:
|
||||
"text-stone-900 border border-stone-200 dark:border-stone-800 font-medium hover:border-stone-300 hover:dark:border-stone-700 dark:text-white",
|
||||
"text-stone-900 border font-medium hover:border-stone-300 hover:dark:border-stone-700 dark:text-white",
|
||||
};
|
||||
|
||||
const classNames = clsx(
|
||||
|
||||
@@ -6,12 +6,10 @@ export function CodeRef({ children }: { children: React.ReactNode }) {
|
||||
className={clsx(
|
||||
"font-mono",
|
||||
"text-[0.9em]",
|
||||
"px-1 py-0.5",
|
||||
"rounded-sm",
|
||||
"border",
|
||||
"text-stone-800 dark:text-stone-200",
|
||||
"px-2 py-1",
|
||||
"rounded",
|
||||
"text-stone-900 dark:text-stone-200",
|
||||
"bg-stone-100 dark:bg-stone-800",
|
||||
"border-stone-200 dark:border-stone-900",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -6,7 +6,7 @@ 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-900 rounded-xl shadow-sm",
|
||||
"border rounded-xl shadow-sm",
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -30,7 +30,7 @@ export function H2({ children, className }: HeadingProps) {
|
||||
className,
|
||||
"font-display",
|
||||
"text-stone-950 dark:text-white",
|
||||
"text-2xl",
|
||||
"text-2xl md:text-4xl",
|
||||
"mb-2",
|
||||
"font-semibold",
|
||||
"tracking-tight",
|
||||
@@ -48,7 +48,7 @@ export function H3({ children, className }: HeadingProps) {
|
||||
className,
|
||||
"font-display",
|
||||
"text-stone-950 dark:text-white",
|
||||
"text-xl",
|
||||
"text-xl md:text-2xl",
|
||||
"mb-2",
|
||||
"font-semibold",
|
||||
"tracking-tight",
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
export function P({ children }: { children: React.ReactNode }) {
|
||||
return <p className="mb-4">{children}</p>;
|
||||
import clsx from "clsx";
|
||||
|
||||
export function P({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return <p className={clsx(className, "mb-4")}>{children}</p>;
|
||||
}
|
||||
|
||||
@@ -84,7 +84,15 @@ export function CodeGroup({
|
||||
|
||||
return (
|
||||
<div className="group relative">
|
||||
<pre ref={textRef}>{children}</pre>
|
||||
<pre
|
||||
className={clsx(
|
||||
"border p-0 bg-stone-50 dark:bg-stone-900",
|
||||
"text-black dark:text-white",
|
||||
)}
|
||||
ref={textRef}
|
||||
>
|
||||
{children}
|
||||
</pre>
|
||||
|
||||
{code ? <CopyButton size={size} code={code} /> : <></>}
|
||||
</div>
|
||||
@@ -15,7 +15,7 @@ export function Input(props: Props) {
|
||||
const inputClassName = clsx(
|
||||
"w-full rounded-md border px-3.5 py-2 shadow-sm",
|
||||
"font-medium text-stone-900",
|
||||
"dark:border-stone-900 dark:text-white",
|
||||
"dark:text-white",
|
||||
);
|
||||
|
||||
const containerClassName = clsx("grid gap-1", className);
|
||||
|
||||
@@ -15,10 +15,10 @@ export function LabelledFeatureIcon({
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
className,
|
||||
className,
|
||||
"p-4 flex flex-col gap-3",
|
||||
"not-prose text-base",
|
||||
"border border-stone-200 dark:border-stone-900 rounded-xl",
|
||||
"border rounded-xl",
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
|
||||
@@ -1,23 +1,32 @@
|
||||
import clsx from "clsx";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
export function Prose(props: { children: ReactNode; className?: string }) {
|
||||
export function Prose({
|
||||
children,
|
||||
className,
|
||||
size = "md",
|
||||
}: {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
size?: "sm" | "md" | "lg";
|
||||
}) {
|
||||
const sizeClassName = {
|
||||
sm: "prose-sm",
|
||||
md: "",
|
||||
lg: "prose-xl",
|
||||
}[size];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"max-w-4xl prose-stone dark:prose-invert",
|
||||
"prose-headings:font-display",
|
||||
"lg:prose-h1:text-5xl prose-h1:font-medium prose-h1:tracking-tight",
|
||||
"lg:prose-h2:text-3xl prose-h2:font-medium prose-h2:tracking-tight",
|
||||
"prose-p:leading-snug",
|
||||
"prose-strong:font-medium",
|
||||
"prose-code:font-normal 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:border prose-pre:p-0 prose-pre:bg-stone-50 prose-pre:dark:bg-stone-900 dark:prose-pre:border-stone-800",
|
||||
"prose-pre:text-black dark:prose-pre:text-white",
|
||||
props.className || "prose",
|
||||
className,
|
||||
"prose",
|
||||
sizeClassName,
|
||||
"dark:prose-invert",
|
||||
"prose-code:dark:bg-stone-900",
|
||||
)}
|
||||
>
|
||||
{props.children}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ function H2Sub({ children }: { children: React.ReactNode }) {
|
||||
"text-lg lg:text-xl",
|
||||
"leading-snug",
|
||||
"tracking-tight",
|
||||
"max-w-4xl",
|
||||
"max-w-2xl",
|
||||
"text-stone-700 dark:text-stone-500",
|
||||
)}
|
||||
>
|
||||
@@ -21,12 +21,14 @@ function H2Sub({ children }: { children: React.ReactNode }) {
|
||||
export function SectionHeader({
|
||||
title,
|
||||
slogan,
|
||||
className,
|
||||
}: {
|
||||
title: ReactNode;
|
||||
slogan: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<hgroup className="mb-5">
|
||||
<hgroup className={clsx(className, "mb-5")}>
|
||||
<H2>{title}</H2>
|
||||
<H2Sub>{slogan}</H2Sub>
|
||||
</hgroup>
|
||||
|
||||
@@ -22,8 +22,12 @@ export function Testimonial({
|
||||
</blockquote>
|
||||
<figcaption className="mt-6 flex items-center gap-x-6">
|
||||
<div className="text-sm leading-6 sm:text-base">
|
||||
<div className="font-semibold text-stone-900 dark:text-white">{name}</div>
|
||||
<div className="mt-0.5 text-stone-600 dark:text-stone-500">{role}</div>
|
||||
<div className="font-semibold text-stone-900 dark:text-white">
|
||||
{name}
|
||||
</div>
|
||||
<div className="mt-0.5 text-stone-600 dark:text-stone-500">
|
||||
{role}
|
||||
</div>
|
||||
</div>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes";
|
||||
import { type ThemeProviderProps } from "next-themes/dist/types";
|
||||
import { useEffect } from "react";
|
||||
|
||||
function ThemeWatcher() {
|
||||
let { resolvedTheme, setTheme } = useTheme();
|
||||
|
||||
useEffect(() => {
|
||||
let media = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
function onMediaChange() {
|
||||
let systemTheme = media.matches ? "dark" : "light";
|
||||
if (resolvedTheme === systemTheme) {
|
||||
setTheme("system");
|
||||
}
|
||||
}
|
||||
|
||||
onMediaChange();
|
||||
media.addEventListener("change", onMediaChange);
|
||||
|
||||
return () => {
|
||||
media.removeEventListener("change", onMediaChange);
|
||||
};
|
||||
}, [resolvedTheme, setTheme]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return (
|
||||
<NextThemesProvider {...props}>
|
||||
<ThemeWatcher />
|
||||
{children}
|
||||
</NextThemesProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { MoonIcon, SunIcon } from "lucide-react";
|
||||
import clsx from "clsx";
|
||||
|
||||
export function ThemeToggle({ className }: { className?: string }) {
|
||||
let { resolvedTheme, setTheme } = useTheme();
|
||||
let otherTheme = resolvedTheme === "dark" ? "light" : "dark";
|
||||
let [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={clsx(
|
||||
className,
|
||||
"md:p-2 md:rounded-full md:border",
|
||||
"text-stone-400 hover:text-stone-900 dark:text-stone-400 dark:hover:text-white",
|
||||
"md:hover:bg-stone-200 md:dark:hover:bg-stone-900",
|
||||
"transition-colors",
|
||||
)}
|
||||
aria-label={
|
||||
mounted ? `Switch to ${otherTheme} theme` : "Toggle theme"
|
||||
}
|
||||
onClick={() => setTheme(otherTheme)}
|
||||
>
|
||||
<MoonIcon
|
||||
size={24}
|
||||
strokeWidth={1.5}
|
||||
className="size-5 md:size-6 stroke-stone-900 dark:hidden"
|
||||
/>
|
||||
<SunIcon
|
||||
size={24}
|
||||
strokeWidth={1.5}
|
||||
className="size-5 md:size-6 hidden stroke-white dark:block"
|
||||
/>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { ReactNode } from "react";
|
||||
import { ThemeToggle } from "../molecules/ThemeToggle";
|
||||
|
||||
type FooterSection = {
|
||||
title: string;
|
||||
@@ -20,20 +21,38 @@ type FooterProps = {
|
||||
sections: FooterSection[];
|
||||
};
|
||||
|
||||
function Copyright({
|
||||
className,
|
||||
companyName,
|
||||
}: {
|
||||
companyName: string;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<p className={clsx(className, "text-sm")}>
|
||||
© {new Date().getFullYear()} {companyName}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
export function Footer({ logo, companyName, sections }: FooterProps) {
|
||||
return (
|
||||
<footer className="flex z-10 mt-10 min-h-[15rem] border-t bg-stone-100 dark:bg-stone-925 text-stone-600 dark:text-stone-400 w-full justify-center dark:border-stone-900">
|
||||
<div className="p-8 container w-full grid grid-cols-3 md:grid-cols-4 lg:grid-cols-7 gap-8 max-sm:mb-12">
|
||||
<div className="col-span-full md:col-span-1 sm:row-start-4 md:row-start-auto lg:col-span-2 md:row-span-2 md:flex-1 flex flex-row md:flex-col max-sm:mt-4 justify-between max-sm:items-start gap-2 text-sm min-w-[10rem]">
|
||||
<footer className="w-full border-t bg-stone-100 mt-12 md:mt-20 dark:bg-stone-925">
|
||||
<div className="container py-8 md:py-16 grid gap-y-8 grid-cols-12">
|
||||
<div className="flex flex-col justify-between col-span-full md:col-span-4">
|
||||
{logo}
|
||||
<p className="max-sm:text-right">
|
||||
© {new Date().getFullYear()}
|
||||
<br />
|
||||
{companyName}
|
||||
</p>
|
||||
|
||||
<Copyright
|
||||
className="hidden md:block"
|
||||
companyName={companyName}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{sections.map((section, index) => (
|
||||
<div key={index} className="flex flex-col gap-2 text-sm">
|
||||
<div
|
||||
key={index}
|
||||
className="flex flex-col gap-2 text-sm col-span-4 sm:col-span-4 md:col-span-2"
|
||||
>
|
||||
<h2 className="font-medium">{section.title}</h2>
|
||||
{section.links.map((link, linkIndex) => (
|
||||
<FooterLink
|
||||
@@ -46,6 +65,15 @@ export function Footer({ logo, companyName, sections }: FooterProps) {
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="hidden md:flex justify-end items-end md:col-span-2">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
|
||||
<Copyright
|
||||
className="col-span-full md:hidden"
|
||||
companyName={companyName}
|
||||
/>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
import { MenuIcon, XIcon } from "lucide-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { ReactNode, useLayoutEffect, useRef, useState } from "react";
|
||||
import { ReactNode, useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { BreadCrumb } from "../molecules/Breadcrumb";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
import { ThemeToggle } from "../molecules/ThemeToggle";
|
||||
|
||||
export function Nav({
|
||||
mainLogo,
|
||||
@@ -34,16 +35,18 @@ export function Nav({
|
||||
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
setMenuOpen(false);
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav
|
||||
className={[
|
||||
clsx(
|
||||
"hidden md:flex sticky left-0 right-0 top-0 max-sm:bottom-0 w-full justify-center",
|
||||
"bg-white dark:bg-stone-950 border-b max-sm:border-t border-stone-200 dark:border-stone-900",
|
||||
"max-h-none overflow-hidden transition[max-height] duration-300 ease-in-out",
|
||||
"hidden md:flex sticky left-0 right-0 top-0 w-full justify-center",
|
||||
"bg-white dark:bg-stone-950 border-b",
|
||||
"z-50",
|
||||
menuOpen ? "h-[100dvh]" : "h-16",
|
||||
),
|
||||
].join(" ")}
|
||||
>
|
||||
@@ -90,6 +93,7 @@ export function Nav({
|
||||
setMenuOpen((o) => !o);
|
||||
setSearchOpen(false);
|
||||
}}
|
||||
aria-label="Open menu"
|
||||
>
|
||||
<MenuIcon />
|
||||
<BreadCrumb items={items} />
|
||||
@@ -108,7 +112,7 @@ export function Nav({
|
||||
<nav
|
||||
className={clsx(
|
||||
"md:hidden fixed flex flex-col bottom-4 right-4 z-50",
|
||||
"bg-stone-50 dark:bg-stone-925 border border-stone-100 dark:border-stone-900 dark:outline dark:outline-1 dark:outline-black/60 rounded-lg shadow-lg",
|
||||
"bg-stone-50 dark:bg-stone-925 border rounded-lg shadow-lg",
|
||||
menuOpen || searchOpen ? "left-4" : "",
|
||||
)}
|
||||
>
|
||||
@@ -118,7 +122,7 @@ export function Nav({
|
||||
" px-2 pb-2",
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center w-full border-b border-stone-100 dark:border-stone-900">
|
||||
<div className="flex items-center w-full border-b">
|
||||
<NavLinkLogo
|
||||
prominent
|
||||
href="/"
|
||||
@@ -141,7 +145,7 @@ export function Nav({
|
||||
</div>
|
||||
|
||||
{pathname.startsWith("/docs") && docNav && (
|
||||
<div className="max-h-[calc(100dvh-15rem)] p-4 border-b border-stone-100 dark:border-stone-900 overflow-x-auto">
|
||||
<div className="max-h-[calc(100dvh-15rem)] p-4 border-b overflow-x-auto">
|
||||
{docNav}
|
||||
</div>
|
||||
)}
|
||||
@@ -162,7 +166,7 @@ export function Nav({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 justify-end border-b border-stone-100 dark:border-stone-900">
|
||||
<div className="flex gap-4 justify-end border-b">
|
||||
{items
|
||||
.filter((item) => !("icon" in item))
|
||||
.slice(3)
|
||||
@@ -179,12 +183,12 @@ export function Nav({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center self-stretch justify-end">
|
||||
<div className="flex items-center self-stretch justify-between">
|
||||
{/* <input
|
||||
type="text"
|
||||
className={clsx(
|
||||
menuOpen || searchOpen ? "" : "hidden",
|
||||
"ml-2 border border-stone-200 dark:border-stone-900 px-2 py-1 rounded w-full"
|
||||
"ml-2 border px-2 py-1 rounded w-full"
|
||||
)}
|
||||
placeholder="Search docs..."
|
||||
ref={searchRef}
|
||||
@@ -202,12 +206,16 @@ export function Nav({
|
||||
>
|
||||
<SearchIcon className="" />
|
||||
</button> */}
|
||||
{(menuOpen || searchOpen) && (
|
||||
<ThemeToggle className="p-3" />
|
||||
)}
|
||||
<button
|
||||
className="flex gap-2 p-3 rounded-xl items-center"
|
||||
onMouseDown={() => {
|
||||
setMenuOpen((o) => !o);
|
||||
setSearchOpen(false);
|
||||
}}
|
||||
aria-label="Close menu"
|
||||
>
|
||||
{menuOpen || searchOpen ? (
|
||||
<XIcon />
|
||||
|
||||
@@ -1,45 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* .manrope-bold {
|
||||
font-family: "Manrope", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
} */
|
||||
|
||||
:root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
|
||||
font-family: "Manrope", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ export default function RootLayout({
|
||||
commitMono.variable,
|
||||
inter.className,
|
||||
"h-full",
|
||||
"bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-950",
|
||||
].join(" ")}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,9 +1,72 @@
|
||||
import { Prose } from "@components/molecules/Prose";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="container h-full flex flex-col gap-8 py-8 lg:py-16">
|
||||
<main className="container flex flex-col gap-8 py-8 lg:py-16">
|
||||
<h1 className="text-2xl font-semibold font-display">
|
||||
Jazz Design System
|
||||
</h1>
|
||||
|
||||
<h2>Typography (Prose)</h2>
|
||||
|
||||
<div className="grid gap-4">
|
||||
<div>
|
||||
Heading 1
|
||||
<Prose className="p-3 border">
|
||||
<h1>Ship top-tier apps at high tempo</h1>
|
||||
</Prose>
|
||||
</div>
|
||||
<div>
|
||||
Heading 2
|
||||
<Prose className="p-3 border">
|
||||
<h2>Ship top-tier apps at high tempo</h2>
|
||||
</Prose>
|
||||
</div>
|
||||
<div>
|
||||
Heading 3
|
||||
<Prose className="p-3 border">
|
||||
<h3>Ship top-tier apps at high tempo</h3>
|
||||
</Prose>
|
||||
</div>
|
||||
<div>
|
||||
Heading 4
|
||||
<Prose className="p-3 border">
|
||||
<h4>Ship top-tier apps at high tempo</h4>
|
||||
</Prose>
|
||||
</div>
|
||||
<div>
|
||||
Paragraph
|
||||
<Prose className="p-3 border">
|
||||
<p>
|
||||
<strong>
|
||||
Jazz is a framework for building local-first
|
||||
apps
|
||||
</strong>{" "}
|
||||
— an architecture that lets companies like Figma and
|
||||
Linear play in a league of their own.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Open source. Self-host or use Jazz Cloud for
|
||||
zero-config magic.
|
||||
</p>
|
||||
</Prose>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Link
|
||||
<Prose className="p-3 border">
|
||||
This is a <a href="https://jazz.tools">link</a>
|
||||
</Prose>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Code
|
||||
<Prose className="p-3 border">
|
||||
This is a one-line <code>piece of code</code>
|
||||
</Prose>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,28 @@ import tailwindCSSAnimate from "tailwindcss-animate";
|
||||
const colors = require("tailwindcss/colors")
|
||||
const plugin = require("tailwindcss/plugin")
|
||||
|
||||
const stonePalette = {
|
||||
"50": "oklch(0.988281 0.002 75)",
|
||||
"75": "oklch(0.980563 0.002 75)",
|
||||
"100": "oklch(0.964844 0.002 75)",
|
||||
"200": "oklch(0.917969 0.002 75)",
|
||||
"300": "oklch(0.853516 0.002 75)",
|
||||
"400": "oklch(0.789063 0.002 75)",
|
||||
"500": "oklch(0.726563 0.002 75)",
|
||||
"600": "oklch(0.613281 0.002 75)",
|
||||
"700": "oklch(0.523438 0.002 75)",
|
||||
"800": "oklch(0.412109 0.002 75)",
|
||||
"900": "oklch(0.302734 0.002 75)",
|
||||
"925": "oklch(0.220000 0.002 75)",
|
||||
"950": "oklch(0.193359 0.002 75)",
|
||||
}
|
||||
|
||||
const stonePaletteWithAlpha = {...stonePalette};
|
||||
|
||||
Object.keys(stonePalette).forEach(key => {
|
||||
stonePaletteWithAlpha[key] = stonePaletteWithAlpha[key].replace(")", "/ <alpha-value>)")
|
||||
})
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const config = {
|
||||
content: [
|
||||
@@ -14,21 +36,7 @@ const config = {
|
||||
theme: {
|
||||
colors: {
|
||||
...harmonyPalette,
|
||||
stone: {
|
||||
"50": "oklch(0.988281 0.002 75 / <alpha-value>)",
|
||||
"75": "oklch(0.980563 0.002 75 / <alpha-value>)",
|
||||
"100": "oklch(0.964844 0.002 75 / <alpha-value>)",
|
||||
"200": "oklch(0.917969 0.002 75 / <alpha-value>)",
|
||||
"300": "oklch(0.853516 0.002 75 / <alpha-value>)",
|
||||
"400": "oklch(0.789063 0.002 75 / <alpha-value>)",
|
||||
"500": "oklch(0.726563 0.002 75 / <alpha-value>)",
|
||||
"600": "oklch(0.613281 0.002 75 / <alpha-value>)",
|
||||
"700": "oklch(0.523438 0.002 75 / <alpha-value>)",
|
||||
"800": "oklch(0.412109 0.002 75 / <alpha-value>)",
|
||||
"900": "oklch(0.302734 0.002 75 / <alpha-value>)",
|
||||
"925": "oklch(0.220000 0.002 75 / <alpha-value>)",
|
||||
"950": "oklch(0.193359 0.002 75 / <alpha-value>)",
|
||||
},
|
||||
stone: stonePaletteWithAlpha,
|
||||
blue: {
|
||||
...colors.indigo,
|
||||
"500": "#5870F1",
|
||||
@@ -47,7 +55,6 @@ const config = {
|
||||
mono: ["var(--font-commit-mono)"],
|
||||
},
|
||||
fontSize: {
|
||||
|
||||
'2xs': ['0.75rem', { lineHeight: '1.25rem' }],
|
||||
},
|
||||
// shadcn-ui
|
||||
@@ -86,11 +93,6 @@ const config = {
|
||||
foreground: "hsl(var(--card-foreground))",
|
||||
},
|
||||
},
|
||||
borderRadius: {
|
||||
lg: "var(--radius)",
|
||||
md: "calc(var(--radius) - 2px)",
|
||||
sm: "calc(var(--radius) - 4px)",
|
||||
},
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: "0" },
|
||||
@@ -120,12 +122,107 @@ const config = {
|
||||
md: "960px",
|
||||
lg: "1276px",
|
||||
},
|
||||
typography: (theme) => ({
|
||||
DEFAULT: {
|
||||
css: {
|
||||
"--tw-prose-body": stonePalette[700],
|
||||
"--tw-prose-headings": stonePalette[900],
|
||||
"--tw-prose-bold": stonePalette[900],
|
||||
"--tw-prose-invert-bold": theme("colors.white"),
|
||||
"--tw-prose-invert-body": stonePalette[400],
|
||||
"--tw-prose-invert-headings": theme("colors.white"),
|
||||
"--tw-prose-code": stonePalette[900],
|
||||
"--tw-prose-invert-code": stonePalette[50],
|
||||
"--tw-prose-links": theme("colors.blue.DEFAULT"),
|
||||
"--tw-prose-invert-links": theme("colors.blue.500"),
|
||||
maxWidth: null,
|
||||
strong: {
|
||||
color: "var(--tw-prose-bold)",
|
||||
fontWeight: theme("fontWeight.medium"),
|
||||
},
|
||||
b: {
|
||||
color: "var(--tw-prose-bold)",
|
||||
fontWeight: theme("fontWeight.medium"),
|
||||
},
|
||||
a: {
|
||||
fontWeight: theme("fontWeight.normal"),
|
||||
textUnderlineOffset: "4px",
|
||||
},
|
||||
h1: {
|
||||
fontFamily: theme("fontFamily.display"),
|
||||
letterSpacing: theme("letterSpacing.tight"),
|
||||
fontWeight: theme("fontWeight.semibold"),
|
||||
fontSize: theme("fontSize.4xl"),
|
||||
},
|
||||
h2: {
|
||||
fontFamily: theme("fontFamily.display"),
|
||||
letterSpacing: theme("letterSpacing.tight"),
|
||||
fontWeight: theme("fontWeight.semibold"),
|
||||
fontSize: theme("fontSize.3xl"),
|
||||
},
|
||||
h3: {
|
||||
fontFamily: theme("fontFamily.display"),
|
||||
letterSpacing: theme("letterSpacing.tight"),
|
||||
fontWeight: theme("fontWeight.semibold"),
|
||||
fontSize: theme("fontSize.2xl"),
|
||||
},
|
||||
h4: {
|
||||
fontFamily: theme("fontFamily.display"),
|
||||
letterSpacing: theme("letterSpacing.tight"),
|
||||
fontWeight: theme("fontWeight.semibold"),
|
||||
fontSize: theme("fontSize.xl"),
|
||||
},
|
||||
'code::before': {
|
||||
content: 'none',
|
||||
},
|
||||
'code::after': {
|
||||
content: 'none',
|
||||
},
|
||||
code: {
|
||||
backgroundColor: stonePalette[100],
|
||||
padding: "0.15rem 0.25rem",
|
||||
borderRadius: "2px",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
p: {
|
||||
marginBottom: theme("spacing.3"),
|
||||
marginTop: theme("spacing.3"),
|
||||
}
|
||||
}
|
||||
},
|
||||
xl: {
|
||||
css: {
|
||||
p: {
|
||||
marginBottom: theme("spacing.3"),
|
||||
marginTop: theme("spacing.3"),
|
||||
}
|
||||
},
|
||||
}
|
||||
}),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
tailwindCSSAnimate,
|
||||
typography(),
|
||||
plugin(({ addVariant }) => addVariant("label", "& :is(label)")),
|
||||
plugin(({ addUtilities }) => addUtilities({
|
||||
".text-reset, .text-reset:hover, .text-reset:focus": {
|
||||
color: "inherit",
|
||||
textDecoration: "none",
|
||||
},
|
||||
})),
|
||||
plugin(({ addBase }) => addBase({
|
||||
":root": {
|
||||
"--gcmp-border-color": stonePalette[200],
|
||||
"--gcmp-invert-border-color": stonePalette[900],
|
||||
},
|
||||
"*": {
|
||||
borderColor: "var(--gcmp-border-color)",
|
||||
},
|
||||
".dark *": {
|
||||
borderColor: "var(--gcmp-invert-border-color)",
|
||||
},
|
||||
}))
|
||||
],
|
||||
};
|
||||
export default config;
|
||||
|
||||
@@ -65,10 +65,6 @@
|
||||
--ring: 24 5.7% 82.9%;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
.overlay-close {
|
||||
@apply bg-black;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "./globals.css";
|
||||
import type { Metadata } from "next";
|
||||
import { ThemeProvider } from "@/components/themeProvider";
|
||||
import { ThemeProvider } from "gcmp-design-system/src/app/components/molecules/ThemeProvider";
|
||||
|
||||
import { Inter, Manrope } from "next/font/google";
|
||||
import localFont from "next/font/local";
|
||||
@@ -8,6 +8,7 @@ import localFont from "next/font/local";
|
||||
import { SpeedInsights } from "@vercel/speed-insights/next";
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
import { GcmpNav } from "@/components/Nav";
|
||||
import { ThemeToggle } from "gcmp-design-system/src/app/components/molecules/ThemeToggle";
|
||||
|
||||
// If loading a variable font, you don't need to specify the font weight
|
||||
const manrope = Manrope({
|
||||
@@ -76,13 +77,13 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<html lang="en" className="h-full">
|
||||
<body
|
||||
className={[
|
||||
manrope.variable,
|
||||
commitMono.variable,
|
||||
inter.className,
|
||||
"flex flex-col items-center",
|
||||
"min-h-full flex flex-col items-center",
|
||||
"bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-950",
|
||||
].join(" ")}
|
||||
>
|
||||
@@ -95,11 +96,13 @@ export default function RootLayout({
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<GcmpNav />
|
||||
<main className="flex flex-1 flex-col w-full">
|
||||
<main className="flex-1 w-full">
|
||||
{children}
|
||||
</main>
|
||||
<footer className="py-8 md:py-16 text-sm">
|
||||
<footer className="py-8 text-sm flex justify-between gap-3 w-full container mt-12 md:mt-20">
|
||||
<p>©2024 Garden Computing, Inc.</p>
|
||||
|
||||
<ThemeToggle className="hidden md:block"/>
|
||||
</footer>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -53,7 +53,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-900 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 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;
|
||||
@@ -79,7 +79,7 @@ pre.twoslash data-lsp:hover::before {
|
||||
}
|
||||
|
||||
pre .code-container {
|
||||
@apply overflow-auto p-2 pl-0 bg-white dark:bg-stone-925 rounded-b-xl text-xs h-full dark:border-stone-900;
|
||||
@apply overflow-auto p-2 pl-0 bg-white dark:bg-stone-925 rounded-b-xl text-xs h-full;
|
||||
}
|
||||
/* The try button */
|
||||
pre .code-container > a {
|
||||
|
||||
@@ -1,42 +1,187 @@
|
||||
import { H1, H2 } from "gcmp-design-system/src/app/components/atoms/Headings";
|
||||
import { MailIcon } from "lucide-react";
|
||||
import { GlobeIcon, LucideIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { SiGithub, SiTwitter } from "@icons-pack/react-simple-icons";
|
||||
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
|
||||
import {
|
||||
SiBluesky,
|
||||
SiGithub,
|
||||
SiGitlab,
|
||||
SiLinkedin,
|
||||
SiX,
|
||||
} from "@icons-pack/react-simple-icons";
|
||||
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
|
||||
|
||||
export const metadata = {
|
||||
title: "Team",
|
||||
};
|
||||
interface TeamMember {
|
||||
name: string;
|
||||
titles: string[];
|
||||
image?: string;
|
||||
location: string;
|
||||
x?: string;
|
||||
github?: string;
|
||||
gitlab?: string;
|
||||
website?: string;
|
||||
linkedin?: string;
|
||||
bluesky?: string;
|
||||
}
|
||||
|
||||
export default function TeamPage() {
|
||||
const team: Array<TeamMember> = [
|
||||
{
|
||||
name: "Anselm Eickhoff",
|
||||
titles: ["Founder"],
|
||||
image: "anselm.jpg",
|
||||
location: "Canterbury, UK ",
|
||||
x: "anselm_io",
|
||||
github: "aeplay",
|
||||
website: "http://anselm.io",
|
||||
bluesky: "anselm-io",
|
||||
},
|
||||
{
|
||||
name: "Andrei Popa",
|
||||
titles: ["Full-Stack Dev", "Infra"],
|
||||
image: "andrei.jpeg",
|
||||
location: "Bucharest, Romania ",
|
||||
x: "elitepax",
|
||||
github: "pax-k",
|
||||
},
|
||||
{
|
||||
name: "Guido D'Orsi",
|
||||
titles: ["Frontend Dev", "React Performance"],
|
||||
image: "guido.jpeg",
|
||||
location: "Piano di Sorrento, Italy ",
|
||||
github: "gdorsi",
|
||||
},
|
||||
{
|
||||
name: "Trisha Lim",
|
||||
titles: ["Frontend Dev", "Design", "Marketing"],
|
||||
image: "trisha.png",
|
||||
location: "Lisbon, Portugal ",
|
||||
github: "trishalim",
|
||||
website: "https://trishalim.com",
|
||||
},
|
||||
{
|
||||
name: "Benjamin Leveritt",
|
||||
titles: ["Full-Stack Dev"],
|
||||
image: "benjamin.jpg",
|
||||
location: "Portsmouth, UK ",
|
||||
github: "bensleveritt",
|
||||
},
|
||||
{
|
||||
name: "Marina Orlova",
|
||||
titles: ["Full-Stack Dev"],
|
||||
location: "Tarragona, Spain ",
|
||||
gitlab: "marinaorlova",
|
||||
linkedin: "marina-orlova-52a34394",
|
||||
// github: "marinoska",
|
||||
image: "marina.jpeg",
|
||||
},
|
||||
];
|
||||
|
||||
function SocialLink({
|
||||
link,
|
||||
label,
|
||||
icon: Icon,
|
||||
}: {
|
||||
label: string;
|
||||
link: string;
|
||||
icon: LucideIcon;
|
||||
}) {
|
||||
return (
|
||||
<div className="container">
|
||||
<HeroHeader title="Team" slogan="" />
|
||||
<Link href={link} className="p-1 -m-1">
|
||||
<Icon size={16} />
|
||||
<span className="sr-only">{label}</span>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
<div className="grid gap-1">
|
||||
<H2>Anselm Eickhoff (Founder)</H2>
|
||||
<Link
|
||||
href="mailto:anselm@gcmp.io"
|
||||
className="flex gap-1 items-center"
|
||||
aria-label="Email address"
|
||||
>
|
||||
<MailIcon height="1em" /> anselm@gcmp.io
|
||||
</Link>
|
||||
<Link
|
||||
href="https://x.com/anselm_io"
|
||||
className="flex gap-1 items-center"
|
||||
aria-label="Twitter link"
|
||||
>
|
||||
<SiTwitter height="1em" /> anselm_io
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com/aeplay"
|
||||
className="flex gap-1 items-center"
|
||||
aria-label="GitHub link"
|
||||
>
|
||||
<SiGithub height="1em" /> aeplay
|
||||
</Link>
|
||||
function Person({ person }: { person: TeamMember }) {
|
||||
const imageClassName = "size-24 shadow rounded-md bg-stone-100 sm:size-28 ";
|
||||
return (
|
||||
<div className="flex items-center gap-5">
|
||||
{person.image ? (
|
||||
<img src={`/team/${person.image}`} className={imageClassName} />
|
||||
) : (
|
||||
<span className={imageClassName}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="w-full pt-5 h-full text-stone-300 dark:text-stone-700"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M4 22a8 8 0 1 1 16 0zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6s6 2.685 6 6s-2.685 6-6 6"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
)}
|
||||
<div className="flex flex-col gap-2.5">
|
||||
<h3 className="text-lg leading-none font-semibold tracking-tight text-stone-900 dark:text-white">
|
||||
{person.name}
|
||||
</h3>
|
||||
<p className="text-sm leading-none text-gray-600 dark:text-stone-400">
|
||||
{person.titles.join(", ")}
|
||||
</p>
|
||||
<p className="text-sm leading-none text-gray-600 dark:text-stone-400">
|
||||
{person.location}
|
||||
</p>
|
||||
|
||||
<div className="flex gap-2 mt-0.5">
|
||||
{person.website && (
|
||||
<SocialLink
|
||||
link={person.website}
|
||||
icon={GlobeIcon}
|
||||
label="Website"
|
||||
/>
|
||||
)}
|
||||
{person.x && (
|
||||
<SocialLink
|
||||
link={`https://x.com/${person.x}`}
|
||||
icon={SiX}
|
||||
label="X profile"
|
||||
/>
|
||||
)}
|
||||
{person.bluesky && (
|
||||
<SocialLink
|
||||
link={`https://bsky.app/profile/${person.bluesky}.bsky.social`}
|
||||
icon={SiBluesky}
|
||||
label="Bluesky profile"
|
||||
/>
|
||||
)}
|
||||
{person.gitlab && (
|
||||
<SocialLink
|
||||
link={`https://gitlab.com/${person.gitlab}`}
|
||||
icon={SiGitlab}
|
||||
label="Gitlab profile"
|
||||
/>
|
||||
)}
|
||||
{person.linkedin && (
|
||||
<SocialLink
|
||||
link={`https://www.linkedin.com/in/${person.linkedin}`}
|
||||
icon={SiLinkedin}
|
||||
label="Linkedin profile"
|
||||
/>
|
||||
)}
|
||||
{person.github && (
|
||||
<SocialLink
|
||||
link={`https://github.com/${person.github}`}
|
||||
label="Github profile"
|
||||
icon={SiGithub}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TeamPage() {
|
||||
return (
|
||||
<div className="container">
|
||||
<HeroHeader title="Meet the team" slogan="" />
|
||||
|
||||
<div className="grid gap-5 sm:grid-cols-2 lg:grid-cols-3 lg:gap-10">
|
||||
{team.map((person) => (
|
||||
<Person key={person.name} person={person} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||
import { type ThemeProviderProps } from "next-themes/dist/types";
|
||||
|
||||
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as TestPost from "@/components/blog/posts/test.mdx";
|
||||
|
||||
export const posts: typeof TestPost[] = [];
|
||||
export const posts: (typeof TestPost)[] = [];
|
||||
|
||||
export const getPostBySlug = (slug: string) => {
|
||||
return posts.find((post) => post.meta.slug === slug);
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
"mdast-util-mdx": "^3.0.0",
|
||||
"micromark-extension-mdxjs": "^3.0.0",
|
||||
"next": "14.2.15",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"resend": "^4.0.0",
|
||||
|
||||
BIN
homepage/gcmp/public/team/andrei.jpeg
Normal file
BIN
homepage/gcmp/public/team/andrei.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
homepage/gcmp/public/team/anselm.jpg
Normal file
BIN
homepage/gcmp/public/team/anselm.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
homepage/gcmp/public/team/benjamin.jpg
Normal file
BIN
homepage/gcmp/public/team/benjamin.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
homepage/gcmp/public/team/guido.jpeg
Normal file
BIN
homepage/gcmp/public/team/guido.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
BIN
homepage/gcmp/public/team/marina.jpeg
Normal file
BIN
homepage/gcmp/public/team/marina.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
BIN
homepage/gcmp/public/team/trisha.png
Normal file
BIN
homepage/gcmp/public/team/trisha.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 186 KiB |
@@ -1,8 +1,5 @@
|
||||
import { CodeExampleTabs, ResponsiveIframe } from "@/components/forMdx";
|
||||
import {
|
||||
Prose,
|
||||
SmallProse,
|
||||
} from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { SectionHeader } from "gcmp-design-system/src/app/components/molecules/SectionHeader";
|
||||
import { GappedGrid } from "gcmp-design-system/src/app/components/molecules/GappedGrid";
|
||||
import { LabelledFeatureIcon } from "gcmp-design-system/src/app/components/molecules/LabelledFeatureIcon";
|
||||
@@ -46,12 +43,13 @@ import TwoWaySyncDescription from "./toolkit/twoWaySync.mdx";
|
||||
import FileUploadDownloadDescription from "./toolkit/fileUploadDownload.mdx";
|
||||
import VideoPresenceCallsDescription from "./toolkit/videoPresenceCalls.mdx";
|
||||
import CloudIntro from "./cloudIntro.mdx";
|
||||
import { H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
|
||||
import { H2, H3 } from "gcmp-design-system/src/app/components/atoms/Headings";
|
||||
import Link from "next/link";
|
||||
import { SupportedEnvironments } from "@/components/home/SupportedEnvironments";
|
||||
import { Hero } from "@/components/home/Hero";
|
||||
import { HowItWorks } from "@/components/home/HowItWorks";
|
||||
import BeforeAfterJazz from "@/components/home/BeforeAfterJazz";
|
||||
import { P } from "gcmp-design-system/src/app/components/atoms/Paragraph";
|
||||
|
||||
export default function Home() {
|
||||
const localFirst = {
|
||||
@@ -212,7 +210,7 @@ export default function Home() {
|
||||
|
||||
<BeforeAfterJazz />
|
||||
|
||||
<div className="container flex flex-col gap-8 py-8 lg:gap-20 lg:py-20">
|
||||
<div className="container flex flex-col gap-12 mt-12 lg:gap-20 lg:mt-20">
|
||||
<HowItWorks />
|
||||
|
||||
<Testimonial name="Serious Adopter #4" role="Technical Founder">
|
||||
@@ -253,7 +251,7 @@ export default function Home() {
|
||||
slogan="A chat app in 174 lines of code."
|
||||
/>
|
||||
|
||||
<div className="flex flex-col md:grid md:grid-cols-2 md:divide-x border rounded-sm overflow-hidden shadow-sm dark:border-stone-900 dark:divide-stone-900">
|
||||
<div className="flex flex-col md:grid md:grid-cols-2 md:divide-x border rounded-sm overflow-hidden shadow-sm dark:divide-stone-900">
|
||||
<CodeExampleTabs
|
||||
tabs={[
|
||||
{
|
||||
@@ -279,14 +277,14 @@ export default function Home() {
|
||||
]}
|
||||
/>
|
||||
<div className="border-b order-first md:order-last flex flex-col md:border-b-0">
|
||||
<div className="flex border-b overflow-x-auto overflow-y-hidden bg-white dark:border-stone-900 dark:bg-stone-900">
|
||||
<div className="flex border-b overflow-x-auto overflow-y-hidden bg-white dark:bg-stone-900">
|
||||
<p className="items-center -mb-px transition-colors px-3 pb-1.5 pt-2 block text-xs border-b-2 border-blue-700 text-stone-700 dark:bg-stone-925 dark:text-blue-500 dark:border-blue-500">
|
||||
result
|
||||
</p>
|
||||
</div>
|
||||
<ResponsiveIframe
|
||||
src="https://chat.jazz.tools"
|
||||
localSrc="http://localhost:5173"
|
||||
localsrc="http://localhost:5173"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -317,18 +315,18 @@ export default function Home() {
|
||||
<H3>
|
||||
<CodeRef>CoMap</CodeRef>
|
||||
</H3>
|
||||
<SmallProse>
|
||||
<Prose size="sm">
|
||||
<CoMapDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
|
||||
<GridCard>
|
||||
<H3>
|
||||
<CodeRef>CoList</CodeRef>
|
||||
</H3>
|
||||
<SmallProse>
|
||||
<Prose size="sm">
|
||||
<CoListDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
|
||||
<GridCard>
|
||||
@@ -336,18 +334,18 @@ export default function Home() {
|
||||
<CodeRef>CoPlainText</CodeRef> &{" "}
|
||||
<CodeRef>CoRichText</CodeRef> <ComingSoonBadge />
|
||||
</H3>
|
||||
<SmallProse>
|
||||
<Prose size="sm">
|
||||
<CoPlainTextDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
|
||||
<GridCard>
|
||||
<H3>
|
||||
<CodeRef>CoStream</CodeRef>
|
||||
</H3>
|
||||
<SmallProse>
|
||||
<Prose size="sm">
|
||||
<CoStreamDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
</GappedGrid>
|
||||
|
||||
@@ -359,18 +357,18 @@ export default function Home() {
|
||||
<H3>
|
||||
<CodeRef>BinaryCoStream</CodeRef>
|
||||
</H3>
|
||||
<SmallProse>
|
||||
<Prose size="sm">
|
||||
<BinaryCoStreamDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
|
||||
<GridCard>
|
||||
<H3>
|
||||
<CodeRef>ImageDefinition</CodeRef>
|
||||
</H3>
|
||||
<SmallProse>
|
||||
<Prose size="sm">
|
||||
<ImageDefinitionDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
</GappedGrid>
|
||||
|
||||
@@ -382,88 +380,82 @@ export default function Home() {
|
||||
<H3>
|
||||
<CodeRef>Group</CodeRef>
|
||||
</H3>
|
||||
<SmallProse>
|
||||
<Prose size="sm">
|
||||
<GroupDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
<GridCard>
|
||||
<H3>
|
||||
<CodeRef>Account</CodeRef>
|
||||
</H3>
|
||||
<SmallProse>
|
||||
<Prose size="sm">
|
||||
<AccountDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
</GappedGrid>
|
||||
|
||||
<SupportedEnvironments />
|
||||
|
||||
<h2 className="sr-only">More features</h2>
|
||||
|
||||
<GappedGrid>
|
||||
<GridCard>
|
||||
<SectionHeader
|
||||
title="Auto-sub"
|
||||
slogan="Let your UI drive data-syncing."
|
||||
/>
|
||||
<SmallProse>
|
||||
<H3>Auto-sub</H3>
|
||||
<P className="text-lg">
|
||||
Let your UI drive data-syncing.
|
||||
</P>
|
||||
<Prose size="sm">
|
||||
<AutoSubDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
|
||||
<GridCard>
|
||||
<SectionHeader
|
||||
title="Cursors & carets"
|
||||
slogan="Ready-made spatial presence."
|
||||
/>
|
||||
<SmallProse>
|
||||
<H3>Cursors & carets</H3>
|
||||
<P className="text-lg">Ready-made spatial presence.</P>
|
||||
<Prose size="sm">
|
||||
<CursorsAndCaretsDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
|
||||
<GridCard>
|
||||
<SectionHeader
|
||||
title="Two-way sync to your DB"
|
||||
slogan="Add Jazz to an existing app."
|
||||
/>
|
||||
<SmallProse>
|
||||
<H3>Two-way sync to your DB</H3>
|
||||
<P className="text-lg">Add Jazz to an existing app.</P>
|
||||
<Prose size="sm">
|
||||
<TwoWaySyncDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
|
||||
<GridCard>
|
||||
<SectionHeader
|
||||
title="File upload & download"
|
||||
slogan={
|
||||
<>
|
||||
Just use{" "}
|
||||
<CodeRef>{`<input type='file'/>`}</CodeRef>.
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<SmallProse>
|
||||
<H3>File upload & download</H3>
|
||||
<P className="text-lg">
|
||||
Just use <CodeRef>{`<input type='file'/>`}</CodeRef>
|
||||
.
|
||||
</P>
|
||||
<Prose size="sm">
|
||||
<FileUploadDownloadDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
|
||||
<GridCard>
|
||||
<SectionHeader
|
||||
title="Video presence & calls"
|
||||
slogan="Stream and record audio & video."
|
||||
/>
|
||||
<SmallProse>
|
||||
<H3>Video presence & calls</H3>
|
||||
<P className="text-lg">
|
||||
Stream and record audio & video.
|
||||
</P>
|
||||
<Prose size="sm">
|
||||
<VideoPresenceCallsDescription />
|
||||
</SmallProse>
|
||||
</Prose>
|
||||
</GridCard>
|
||||
</GappedGrid>
|
||||
|
||||
<div className="border border-stone-200 dark:border-stone-900 rounded-xl shadow-sm p-4 md:py-16">
|
||||
<div className="flex flex-col lg:max-w-3xl md:text-center mx-auto justify-between gap-6">
|
||||
<div className="border rounded-xl shadow-sm p-4 md:py-16">
|
||||
<div className="lg:max-w-3xl md:text-center mx-auto space-y-6">
|
||||
<p className="uppercase text-blue tracking-widest text-sm font-medium dark:text-stone-400">
|
||||
Become an early adopter
|
||||
</p>
|
||||
<h3 className="font-display md:text-center text-stone-950 dark:text-white text-2xl md:text-3xl font-semibold tracking-tight">
|
||||
<H2>
|
||||
We'll help you build your next app with Jazz
|
||||
</h3>
|
||||
<div className="space-y-2 md:text-balance leading-relaxed">
|
||||
</H2>
|
||||
<Prose className="md:text-balance mx-auto">
|
||||
<p>
|
||||
It's early days, but we work hard every day
|
||||
to make Jazz a great tool for our users.
|
||||
@@ -474,7 +466,7 @@ export default function Home() {
|
||||
We'll prioritize features that you need to
|
||||
succeed.
|
||||
</p>
|
||||
</div>
|
||||
</Prose>
|
||||
<div className="flex md:justify-center gap-3">
|
||||
<Button
|
||||
href="https://discord.gg/utDMjHYg42"
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function Page() {
|
||||
href={`/docs/api-reference/${name}`}
|
||||
key={name}
|
||||
>
|
||||
<Card className="border border-stone-200 dark:border-stone-900 shadow-sm">
|
||||
<Card className="border shadow-sm">
|
||||
<PackageIcon
|
||||
size={25}
|
||||
strokeWidth={1.5}
|
||||
@@ -88,11 +88,17 @@ export default function Page() {
|
||||
</CardHeading>
|
||||
<CardBody>
|
||||
Get help from our{" "}
|
||||
<Link href="https://discord.gg/utDMjHYg42">
|
||||
<Link
|
||||
href="https://discord.gg/utDMjHYg42"
|
||||
className="underline"
|
||||
>
|
||||
Discord
|
||||
</Link>
|
||||
, or open an issue on{" "}
|
||||
<Link href="https://github.com/gardencmp/jazz">
|
||||
<Link
|
||||
href="https://github.com/gardencmp/jazz"
|
||||
className="underline"
|
||||
>
|
||||
GitHub
|
||||
</Link>
|
||||
.
|
||||
9
homepage/homepage/app/docs/(docs)/coming-soon/page.mdx
Normal file
9
homepage/homepage/app/docs/(docs)/coming-soon/page.mdx
Normal file
@@ -0,0 +1,9 @@
|
||||
# The documentation for this feature is not yet available
|
||||
|
||||
Grayed out pages on the sidebar indicate that the documentation is not yet available.
|
||||
|
||||
This feature has already been released, but the documentation is still a work in progress.
|
||||
|
||||
If you don't find what you're looking for, [please ask for help in the Discord](https://discord.gg/utDMjHYg42).
|
||||
We have a whole team of developers who are ready to help you get started.
|
||||
|
||||
13
homepage/homepage/app/docs/(docs)/layout.tsx
Normal file
13
homepage/homepage/app/docs/(docs)/layout.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
|
||||
export default function DocsLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Prose className="container w-full max-w-full col-span-12 md:col-span-8 lg:col-span-9">
|
||||
{children}
|
||||
</Prose>
|
||||
);
|
||||
}
|
||||
63
homepage/homepage/app/docs/(docs)/page.tsx
Normal file
63
homepage/homepage/app/docs/(docs)/page.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Prose>
|
||||
<h1>Welcome to the Jazz documentation.</h1>
|
||||
<p>
|
||||
The Jazz docs are currently heavily work in progress, sorry
|
||||
about that!
|
||||
</p>
|
||||
<p>The best ways to get started are:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Quickstart (incomplete)
|
||||
<ol>
|
||||
<li>
|
||||
<a href="/docs/sync-and-storage">
|
||||
Sync & Storage Setup
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/project-setup/react">
|
||||
React Project Setup
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/docs/schemas/covalues">
|
||||
CoValue Basics & Schema Definition
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<span className="opacity-50">
|
||||
Creating Covalues
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span className="opacity-50">Using Covalues</span>
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
The step-by-step <a href="/docs/guide">Guide</a>{" "}
|
||||
(incomplete)
|
||||
</li>
|
||||
</ul>
|
||||
<p>Also make sure to:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Find an <a href="/docs/examples">example app with code</a>{" "}
|
||||
most similar to what you want to build
|
||||
</li>
|
||||
<li>
|
||||
Check out the{" "}
|
||||
<a href="/docs/api-reference">API Reference</a> (incomplete)
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
And the best way to get help is to join the{" "}
|
||||
<a href="https://discord.gg/utDMjHYg42">Discord</a>!
|
||||
</p>
|
||||
</Prose>
|
||||
);
|
||||
}
|
||||
@@ -236,7 +236,7 @@ export default App; // old
|
||||
<div className="text-xs uppercase text-stone-400 dark:text-stone-600 tracking-wider -mb-3">
|
||||
Preview
|
||||
</div>
|
||||
<div className="p-3 md:-mx-3 rounded border border-stone-100 dark:border-stone-900 bg-white dark:bg-black not-prose">
|
||||
<div className="p-3 md:-mx-3 rounded border border-stone-100 bg-white dark:bg-black not-prose">
|
||||
<div className="grid grid-cols-6 text-sm border-r border-b [&>*]:p-2 [&>*]:border-l [&>*]:border-t">
|
||||
<h2>Buy terrarium</h2>
|
||||
<p className="col-span-3">Make sure it's big enough for 10 snails.</p>
|
||||
@@ -349,7 +349,7 @@ export function IssueComponent({ issue }: { issue: Issue }) { // old
|
||||
<div className="text-xs uppercase text-stone-400 dark:text-stone-600 tracking-wider -mb-3">
|
||||
Preview
|
||||
</div>
|
||||
<div className="p-3 md:-mx-3 rounded border border-stone-100 dark:border-stone-900 bg-white dark:bg-black not-prose">
|
||||
<div className="p-3 md:-mx-3 rounded border border-stone-100 bg-white dark:bg-black not-prose">
|
||||
<div className="grid grid-cols-6 text-sm border-r border-b [&>*]:p-2 [&>*]:border-l [&>*]:border-t">
|
||||
<input type="text" value={"Buy terrarium"} />
|
||||
<input
|
||||
89
homepage/homepage/app/docs/guide/page.tsx
Normal file
89
homepage/homepage/app/docs/guide/page.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import Guide from "./guide.mdx";
|
||||
import { TableOfContents } from "@/components/docs/TableOfContents";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
name: "Project Setup",
|
||||
href: "/docs/guide#project-setup",
|
||||
},
|
||||
{
|
||||
name: "Intro to CoValues",
|
||||
href: "/docs/guide#intro-to-covalues",
|
||||
items: [
|
||||
{
|
||||
name: "Declaration",
|
||||
href: "/docs/guide#declaring-covalues",
|
||||
},
|
||||
{
|
||||
name: "Reading",
|
||||
href: "/docs/guide#reading-covalues",
|
||||
},
|
||||
{
|
||||
name: "Creation",
|
||||
href: "/docs/guide#creating-covalues",
|
||||
},
|
||||
{
|
||||
name: "Editing & Subscription",
|
||||
href: "/docs/guide#editing-and-subscription",
|
||||
},
|
||||
{
|
||||
name: "Persistence",
|
||||
href: "/docs/guide#persistence",
|
||||
},
|
||||
{
|
||||
name: "Remote Sync",
|
||||
href: "/docs/guide#remote-sync",
|
||||
},
|
||||
{
|
||||
name: "Simple Public Sharing",
|
||||
href: "/docs/guide#simple-public-sharing",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Refs & Auto-Subscribe",
|
||||
href: "/docs/guide#refs-and-on-demand-subscribe",
|
||||
items: [
|
||||
{
|
||||
name: "Precise Loading Depths",
|
||||
href: "/docs/guide#loading-depth",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Groups & Permissions",
|
||||
href: "/docs/guide#groups-and-permissions",
|
||||
items: [
|
||||
{
|
||||
name: "Groups/Accounts as Scopes",
|
||||
href: "/docs/guide#groups-accounts-as-scopes",
|
||||
},
|
||||
{
|
||||
name: "Creating Invites",
|
||||
href: "/docs/guide#creating-invites",
|
||||
},
|
||||
{
|
||||
name: "Consuming Invites",
|
||||
href: "/docs/guide#consuming-invites",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"col-span-12 md:col-span-8 lg:col-span-9",
|
||||
"flex justify-center lg:gap-5",
|
||||
)}
|
||||
>
|
||||
<Prose className="overflow-x-hidden lg:flex-1">
|
||||
<Guide />
|
||||
</Prose>
|
||||
<TableOfContents className="w-48 shrink-0" items={navItems} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import { DocNav } from "@/components/docs/nav";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export const metadata = {
|
||||
title: "Docs",
|
||||
@@ -13,17 +11,9 @@ export default function DocsLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="container relative grid grid-cols-12 gap-x-8 py-8">
|
||||
<DocNav
|
||||
className={cn(
|
||||
"md:col-span-4 lg:col-span-3",
|
||||
"sticky align-start top-[4.75rem] h-[calc(100vh-8rem)] overflow-y-auto overflow-x-hidden",
|
||||
"hidden md:block",
|
||||
)}
|
||||
/>
|
||||
<div className="col-span-12 md:col-span-8 lg:col-span-9">
|
||||
<Prose>{children}</Prose>
|
||||
</div>
|
||||
<div className="container relative grid grid-cols-12 gap-5 py-8">
|
||||
<DocNav />
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import Guide from "./guide.mdx";
|
||||
|
||||
export default function Page() {
|
||||
return <Guide />;
|
||||
}
|
||||
75
homepage/homepage/app/docs/project-setup/next/next.mdx
Normal file
75
homepage/homepage/app/docs/project-setup/next/next.mdx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# Next.js
|
||||
|
||||
## <span id="next-csr">Client-side only</span>
|
||||
|
||||
The easiest way to use Jazz with Next.JS is to only use it on the client side. You can ensure this by:
|
||||
|
||||
- marking the `jazz.tsx` file as `"use client"`
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```tsx
|
||||
"use client"
|
||||
import { createJazzReactApp } from "jazz-react";// old
|
||||
|
||||
const Jazz = createJazzReactApp();// old
|
||||
export const { useAccount, useCoState } = Jazz;// old
|
||||
|
||||
import { PasskeyAuthBasicUI, usePasskeyAuth } from "jazz-react";// old
|
||||
|
||||
function JazzAndAuth({ children }: { children: React.ReactNode }) {// old
|
||||
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName });// old
|
||||
|
||||
return (// old
|
||||
<>// old
|
||||
<Jazz.Provider// old
|
||||
auth={passkeyAuth}// old
|
||||
peer="wss://mesh.jazz.tools/?key=you@example.com"// old
|
||||
>// old
|
||||
{children}// old
|
||||
</Jazz.Provider>// old
|
||||
<PasskeyAuthBasicUI state={passKeyState} />// old
|
||||
</>// old
|
||||
);// old
|
||||
}// old
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
- marking any file with components where you use Jazz hooks (such as `useAccount` or `useCoState`) as `"use client"`
|
||||
|
||||
## <span id="next-ssr">SSR use (experimental)</span>
|
||||
|
||||
Pure SSR use of Jazz is basically just using jazz-nodejs (see [Node.JS / Server Workers](/docs/project-setup/server-side)) inside Server Components.
|
||||
|
||||
Instead of using hooks as you would on the client, you await promises returned by `CoValue.load(...)` inside your Server Components.
|
||||
|
||||
TODO: code example
|
||||
|
||||
This should work well for cases like rendering publicly-readable information, since the worker account will be able to load them.
|
||||
|
||||
In the future, it will be possible to use trusted auth methods (such as Clerk, Auth0, etc.) that let you act as the same Jazz user both on the client and on the server, letting you use SSR even for data private to that user.
|
||||
|
||||
## <span id="next-ssr-plus-csr">SSR + client-side (experimental)</span>
|
||||
|
||||
You can combine the two approaches by creating
|
||||
|
||||
1. A pure "rendering" component that renders an already-loaded CoValue (in JSON-ified form)
|
||||
|
||||
TODO: code example
|
||||
|
||||
2. A "hydrating" component (with `"use client"`) that
|
||||
|
||||
- expects a pre-loaded CoValue as a prop (in JSON-ified form)
|
||||
- uses one of the client-side Jazz hooks (such as `useAccount` or `useCoState`) to subscribe to that same CoValue
|
||||
- passing the client-side subscribed state to the "rendering" component, with the pre-loaded CoValue as a fallback until the client receives the first subscribed state
|
||||
|
||||
TODO: code example
|
||||
|
||||
3. A "pre-loading" Server Component that
|
||||
|
||||
- pre-loads the CoValue by awaiting it's `load(...)` method (as described above)
|
||||
- renders the "hydrating" component, passing the pre-loaded CoValue as a prop
|
||||
|
||||
TODO: code example
|
||||
35
homepage/homepage/app/docs/project-setup/next/page.tsx
Normal file
35
homepage/homepage/app/docs/project-setup/next/page.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import NextGuide from "./next.mdx";
|
||||
import { TableOfContents } from "@/components/docs/TableOfContents";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
name: "Client-side only",
|
||||
href: "/docs/project-setup/react#next-csr",
|
||||
},
|
||||
{
|
||||
name: "SSR use 🧪",
|
||||
href: "/docs/project-setup/react#next-ssr",
|
||||
},
|
||||
{
|
||||
name: "SSR + client-side 🧪",
|
||||
href: "/docs/project-setup/react#next-ssr-plus-csr",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"col-span-12 md:col-span-8 lg:col-span-9",
|
||||
"flex justify-center lg:gap-5",
|
||||
)}
|
||||
>
|
||||
<Prose className="overflow-x-hidden lg:flex-1">
|
||||
<NextGuide />
|
||||
</Prose>
|
||||
<TableOfContents className="w-48 shrink-0" items={navItems} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import ReactNativeGuide from "./react-native.mdx";
|
||||
import { TableOfContents } from "@/components/docs/TableOfContents";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
name: "Setup",
|
||||
href: "/docs/project-setup/react#react-native-setup",
|
||||
},
|
||||
{
|
||||
name: "Using Jazz",
|
||||
href: "/docs/project-setup/react#react-native-using-jazz",
|
||||
},
|
||||
];
|
||||
export default function Page() {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"col-span-12 md:col-span-8 lg:col-span-9",
|
||||
"flex justify-center lg:gap-5",
|
||||
)}
|
||||
>
|
||||
<Prose className="overflow-x-hidden lg:flex-1">
|
||||
<ReactNativeGuide />
|
||||
</Prose>
|
||||
<TableOfContents className="w-48 shrink-0" items={navItems} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# <span id="react-native">React Native</span>
|
||||
|
||||
Jazz requires an [Expo development build](https://docs.expo.dev/develop/development-builds/introduction/) using [Expo Prebuild](https://docs.expo.dev/workflow/prebuild/) for native code. It is **not compatible** with Expo Go. Jazz also supports the [New Architecture](https://docs.expo.dev/guides/new-architecture/).
|
||||
|
||||
Tested with:
|
||||
|
||||
<CodeGroup>
|
||||
```json
|
||||
"expo": "~51.0.0",
|
||||
"react-native": "~0.74.5",
|
||||
"react": "^18.2.0",
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## <span id="react-native-setup">Setup</span>
|
||||
|
||||
### Create a New Project
|
||||
|
||||
(skip this step if you already have one)
|
||||
|
||||
<CodeGroup>
|
||||
```bash
|
||||
npx create-expo-app -e with-router-tailwind my-jazz-app
|
||||
cd my-jazz-app
|
||||
npx expo prebuild
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Install dependencies
|
||||
|
||||
<CodeGroup>
|
||||
```bash
|
||||
npx expo install expo-linking expo-secure-store expo-file-system @react-native-community/netinfo @bam.tech/react-native-image-resizer
|
||||
|
||||
npm i -S react-native-polyfill-globals react-native-url-polyfill web-streams-polyfill@3.2.1 base-64 text-encoding react-native-fetch-api react-native-get-random-values buffer
|
||||
|
||||
npm i -D @babel/plugin-transform-class-static-block
|
||||
|
||||
npm i -S jazz-tools jazz-react-native jazz-react-native-media-images
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Fix Incompatible Dependencies
|
||||
|
||||
<CodeGroup>
|
||||
```bash
|
||||
npx expo install --fix
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Install Pods
|
||||
|
||||
<CodeGroup>
|
||||
```bash
|
||||
npx pod-install
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Configure Metro
|
||||
|
||||
#### Regular Repositories
|
||||
|
||||
If you are not working within a monorepo, create a new file metro.config.js in the root of your project with the following content:
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
const { getDefaultConfig } = require("expo/metro-config");
|
||||
const config = getDefaultConfig(projectRoot);
|
||||
config.resolver.unstable_enablePackageExports = true; // important setting
|
||||
config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"];
|
||||
config.resolver.requireCycleIgnorePatterns = [/(^|\/|\\)node_modules($|\/|\\)/];
|
||||
module.exports = config;
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
If you created the project using the command `npx create-expo-app -e with-router-tailwind my-jazz-app`, then `metro.config.js` is already present. In that case, simply add this setting to the existing file:
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
config.resolver.unstable_enablePackageExports = true
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
#### Monorepos
|
||||
|
||||
For monorepos, use the following metro.config.js:
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
const { getDefaultConfig } = require("expo/metro-config");
|
||||
const { FileStore } = require("metro-cache");
|
||||
const path = require("path");
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const projectRoot = __dirname;
|
||||
const workspaceRoot = path.resolve(projectRoot, "../..");
|
||||
|
||||
const config = getDefaultConfig(projectRoot);
|
||||
|
||||
config.watchFolders = [workspaceRoot];
|
||||
config.resolver.nodeModulesPaths = [
|
||||
path.resolve(projectRoot, "node_modules"),
|
||||
path.resolve(workspaceRoot, "node_modules"),
|
||||
];
|
||||
config.resolver.sourceExts = ["mjs", "js", "json", "ts", "tsx"];
|
||||
config.resolver.unstable_enablePackageExports = true;
|
||||
config.resolver.requireCycleIgnorePatterns = [/(^|\/|\\)node_modules($|\/|\\)/];
|
||||
config.cacheStores = [
|
||||
new FileStore({
|
||||
root: path.join(projectRoot, "node_modules", ".cache", "metro"),
|
||||
}),
|
||||
];
|
||||
|
||||
module.exports = config;
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Additional Monorepo Configuration (for pnpm users)
|
||||
|
||||
- Add node-linker=hoisted to the root .npmrc (create this file if it doesn’t exist).
|
||||
- Add the following to the root package.json:
|
||||
|
||||
<CodeGroup>
|
||||
```json
|
||||
"pnpm": {
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": [
|
||||
"@babel/*",
|
||||
"expo-modules-*",
|
||||
"typescript"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
For more information, refer to [this](https://github.com/byCedric/expo-monorepo-example#pnpm-workarounds) Expo monorepo example.
|
||||
|
||||
### Configure Babel
|
||||
|
||||
Add `@babel/plugin-transform-class-static-block` to the array of Babel plugins inside `babel.config.js`:
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ["babel-preset-expo"],
|
||||
plugins: [
|
||||
"nativewind/babel",
|
||||
"@babel/plugin-transform-class-static-block",
|
||||
],
|
||||
};
|
||||
};
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### Add Polyfills
|
||||
|
||||
Create a file `polyfills.js` at the project root with the following content:
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
import "react-native-polyfill-globals/auto";
|
||||
import "@azure/core-asynciterator-polyfill";
|
||||
import { ReadableStream } from "web-streams-polyfill/ponyfill/es6";
|
||||
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions";
|
||||
import { Buffer } from "buffer";
|
||||
|
||||
polyfillGlobal("Buffer", () => Buffer);
|
||||
polyfillGlobal("ReadableStream", () => ReadableStream);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Update `index.js` based on whether you are using expo-router or not:
|
||||
|
||||
#### If using `expo-router`
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
import "./polyfills";
|
||||
import "expo-router/entry";
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
#### Without `expo-router`
|
||||
|
||||
<CodeGroup>
|
||||
```ts
|
||||
import "./polyfills";
|
||||
import { registerRootComponent } from "expo";
|
||||
import App from "./src/App";
|
||||
registerRootComponent(App);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Lastly, ensure that the `"main"` field in your `package.json` points to `index.js`:
|
||||
|
||||
<CodeGroup>
|
||||
```json
|
||||
"main": "index.js",
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## <span id="react-native-using-jazz">Using Jazz</span>
|
||||
|
||||
### `createJazzRNApp()`
|
||||
|
||||
Create a file `jazz.tsx` with the following contents:
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
import { createJazzRNApp } from "jazz-react-native";
|
||||
|
||||
export const Jazz = createJazzRNApp();
|
||||
export const { useAccount, useCoState, useAcceptInvite } = Jazz;
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
You can optionally pass a custom `kvStore` and `AccountSchema` to `createJazzRNApp()`, otherwise, it defaults to `ExpoSecureStoreAdapter` and `Account`.
|
||||
|
||||
### Choosing an Auth Method
|
||||
|
||||
Refer to the Jazz + React Native demo projects for implementing authentication:
|
||||
|
||||
- [DemoAuth Example](https://github.com/gardencmp/jazz/tree/main/examples/chat-rn)
|
||||
- [ClerkAuth Example](https://github.com/gardencmp/jazz/tree/main/examples/chat-rn-clerk)
|
||||
|
||||
In the demos, you'll find details on:
|
||||
|
||||
- Using Jazz.Provider with your chosen authentication method
|
||||
- Defining a Jazz schema
|
||||
- Creating and subscribing to covalues
|
||||
- Handling invites
|
||||
|
||||
### Working with Images
|
||||
|
||||
To work with images in Jazz, import the `createImage` function from [`jazz-react-native-media-images`](https://github.com/gardencmp/jazz/tree/main/packages/jazz-react-native-media-images).
|
||||
|
||||
<CodeGroup>
|
||||
```tsx
|
||||
import { createImage } from "jazz-react-native-media-images";
|
||||
|
||||
const base64ImageDataURI = "data:image/png;base64,...";
|
||||
|
||||
const image = await createImage(base64ImageDataURI, {
|
||||
owner: newPetPost._owner,
|
||||
maxSize: 2048, // optional: specify maximum image size
|
||||
});
|
||||
|
||||
someCovalue.image = image;
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
For a complete implementation, please refer to [this](https://github.com/gardencmp/jazz/blob/main/examples/pets/src/3_NewPetPostForm.tsx) demo.
|
||||
|
||||
### Running Your App
|
||||
|
||||
<CodeGroup>
|
||||
```bash
|
||||
npx expo run:ios
|
||||
npx expo run:android
|
||||
```
|
||||
</CodeGroup>
|
||||
18
homepage/homepage/app/docs/project-setup/react/page.tsx
Normal file
18
homepage/homepage/app/docs/project-setup/react/page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import ReactGuide from "./react.mdx";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"col-span-12 md:col-span-8 lg:col-span-9",
|
||||
"flex justify-center lg:gap-5",
|
||||
)}
|
||||
>
|
||||
<Prose className="overflow-x-hidden lg:flex-1">
|
||||
<ReactGuide />
|
||||
</Prose>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
82
homepage/homepage/app/docs/project-setup/react/react.mdx
Normal file
82
homepage/homepage/app/docs/project-setup/react/react.mdx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# <span id="react">React</span>
|
||||
|
||||
Currently, the recommended pattern to set up a React app with Jazz is to create a separate file (for example, called `jazz.tsx`) in which:
|
||||
|
||||
1. You create a new Jazz React app context, extracting and exporting Jazz hooks.
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```tsx
|
||||
import { createJazzReactApp } from "jazz-react";
|
||||
|
||||
const Jazz = createJazzReactApp();
|
||||
export const { useAccount, useCoState } = Jazz;
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
- You extract hooks here so they can be aware of a custom `AccountSchema` for your app once you start using one (see [Accounts & Migrations](/docs/schemas/accounts-and-migrations)).
|
||||
|
||||
This way, accounts returned by the hooks will be correctly typed throughout your app. Simply import them from `jazz.tsx` wherever you need them.
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```tsx
|
||||
import { createJazzReactApp } from "jazz-react"; // old
|
||||
|
||||
const Jazz = createJazzReactApp({ AccountSchema: MyAppAccount });
|
||||
export const { useAccount, useCoState } = Jazz; // old
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
2. You define a context providing component (typically called `JazzAndAuth`) that uses
|
||||
|
||||
- the context provider of the Jazz React app context you just created
|
||||
|
||||
- the hooks and default/custom UI of one of the [Auth Methods](/docs/auth/auth-methods).
|
||||
|
||||
This is also where you specify the sync & storage server to connect to (see [Sync and storage](/docs/sync-and-storage)).
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```tsx
|
||||
import { createJazzReactApp } from "jazz-react";// old
|
||||
|
||||
const Jazz = createJazzReactApp();// old
|
||||
export const { useAccount, useCoState } = Jazz;// old
|
||||
|
||||
import { PasskeyAuthBasicUI, usePasskeyAuth } from "jazz-react";
|
||||
|
||||
function JazzAndAuth({ children }: { children: React.ReactNode }) {
|
||||
const [passkeyAuth, passKeyState] = usePasskeyAuth({ appName });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Jazz.Provider
|
||||
auth={passkeyAuth}
|
||||
peer="wss://mesh.jazz.tools/?key=you@example.com"
|
||||
>
|
||||
{children}
|
||||
</Jazz.Provider>
|
||||
<PasskeyAuthBasicUI state={passKeyState} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
With `JazzAndAuth` defined, you can wrap your app in it and then use the extracted and exported hooks within your App.
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```tsx
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render( // old
|
||||
<React.StrictMode> // old
|
||||
<JazzAndAuth>
|
||||
<App />
|
||||
</JazzAndAuth>
|
||||
</React.StrictMode>// old
|
||||
);// old
|
||||
```
|
||||
</CodeGroup>
|
||||
@@ -0,0 +1,39 @@
|
||||
import ServerGuide from "./server-side.mdx";
|
||||
import { TableOfContents } from "@/components/docs/TableOfContents";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
name: "Generating Credentials",
|
||||
href: "/docs/project-setup/server-side#generating-credentials",
|
||||
},
|
||||
{
|
||||
name: "Storing and Providing Credentials",
|
||||
href: "/docs/project-setup/server-side#storing-credentials",
|
||||
},
|
||||
{
|
||||
name: "Starting a Server Worker",
|
||||
href: "/docs/project-setup/server-side#starting",
|
||||
},
|
||||
{
|
||||
name: "Using CoValues instead of Requests",
|
||||
href: "/docs/project-setup/server-side#covalues-instead-of-requests",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"col-span-12 md:col-span-8 lg:col-span-9",
|
||||
"flex justify-center lg:gap-5",
|
||||
)}
|
||||
>
|
||||
<Prose className="overflow-x-hidden lg:flex-1">
|
||||
<ServerGuide />
|
||||
</Prose>
|
||||
<TableOfContents className="w-48 shrink-0" items={navItems} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# Node.JS / Server Workers
|
||||
|
||||
The main detail to understand when using Jazz server-side is that Server Workers have Jazz `Accounts`, just like normal users do.
|
||||
|
||||
This lets you share CoValues with Server Workers, having precise access control by adding the Worker to `Groups` with specific roles just like you would with other users.
|
||||
|
||||
## Generating Credentials
|
||||
|
||||
Server Workers typically have static credentials, consisting of a public Account ID and a private Account Secret.
|
||||
|
||||
To generate new credentials for a Server Worker, you can run:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```sh
|
||||
npx jazz-run account create --name "My Server Worker"
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
The name will be put in the public profile of the Server Worker's `Account`, which can be helpful when inspecting metadata of CoValue edits that the Server Worker has done.
|
||||
|
||||
## Storing & Providing Credentials
|
||||
|
||||
Server Worker credentials are typically stored and provided as environmental variables.
|
||||
|
||||
**Take extra care with the Account Secret — handle it like any other secret environment variable such as a DB password.**
|
||||
|
||||
## Starting a Server Worker
|
||||
|
||||
You can use `startWorker` from `jazz-nodejs` to start a Server Worker. Similarly to setting up a client-side Jazz context, it:
|
||||
|
||||
- takes a custom `AccountSchema` if you have one (for example, because the worker needs to store information in it's private account root)
|
||||
- takes a URL for a sync & storage server
|
||||
|
||||
`startWorker` expects credentials in the `JAZZ_WORKER_ACCOUNT` and `JAZZ_WORKER_SECRET` environment variables by default (as printed by `npx account create ...`), but you can also pass them manually as `accountID` and `accountSecret` parameters if you get them from elsewhere.
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
import { startWorker } from 'jazz-nodejs';
|
||||
|
||||
const { worker } = await startWorker({
|
||||
AccountSchema: MyWorkerAccount,
|
||||
syncServer: 'wss://cloud.jazz.tools/?key=you@example.com',
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
`worker` acts like `me` (as returned by `useAccount` on the client) - you can use it to:
|
||||
|
||||
- load/subscribe to CoValues: `MyCoValue.subscribe(id, worker, {...})`
|
||||
- create CoValues & Groups `const val = MyCoValue.create({...}, { owner: worker })`
|
||||
|
||||
## Using CoValues instead of Requests
|
||||
|
||||
Just like traditional backend functions, you can use Server Workers to do useful stuff (computations, calls to third-party APIs etc.) and put the results back into CoValues, which subscribed clients automatically get notified about.
|
||||
|
||||
What's less clear is how you can trigger this work to happen.
|
||||
|
||||
- One option is to define traditional HTTP API handlers that use the Jazz Worker internally. This is helpful if you need to mutate Jazz state in response to HTTP requests such as for webhooks or non-Jazz API clients
|
||||
- The other option is to have the Jazz Worker subscribe to CoValues which they will then collaborate on with clients.
|
||||
- A common pattern is to implement a state machine represented by a CoValue, where the client will do some state transitions (such as `draft -> ready`), which the worker will notice and then do some work in response, feeding the result back in a further state transition (such as `ready -> success & data`, or `ready -> failure & error details`).
|
||||
- This way, client and worker don't have to explicitly know about each other or communicate directly, but can rely on Jazz as a communication mechanism - with computation progressing in a distributed manner wherever and whenever possible.
|
||||
65
homepage/homepage/app/docs/schemas/covalues/covalues.mdx
Normal file
65
homepage/homepage/app/docs/schemas/covalues/covalues.mdx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { CodeGroup } from "@/components/forMdx";
|
||||
|
||||
# CoValues
|
||||
|
||||
**CoValues ("Collaborative Values") are the core abstraction of Jazz.** Think of them as bread-and-butter datastructures that you can use to represent everything you need in your app.
|
||||
|
||||
As their name suggests, they are inherently collaborative, meaning **multiple users and devices can edit them at the same time.**
|
||||
|
||||
- CoValues allow for concurrent edits by always keeping their full edit histories, deriving the "current state" based on the currently locally available history.
|
||||
- **Think of CoValues as "super-fast Git for lots of tiny data".**
|
||||
- (The fact that this happens in an eventually-consistent way makes them [CRDTs](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type))
|
||||
- Having the full history around also means that you often don't need explicit timestamps and author info - you get this for free, just by having different accounts edit a value and then looking at its [edit metadata](/docs/covalues/metadata).
|
||||
|
||||
CoValues mostly model JSON with CoMaps and CoLists, but also offer CoStreams for simple per-user value streams, and also let you represent binary data with BinaryCoStreams.
|
||||
|
||||
## Schemas as Your App's First Step
|
||||
|
||||
Under the hood, CoValues are as dynamic and flexible as JSON itself, but the way you use them in Jazz is by defining fixed schemas to describe the shape of data in your app.
|
||||
|
||||
- This helps correctness and development speed in general, but is particularly important
|
||||
- when you evolve your app and need migrations
|
||||
- when different clients and server workers collaborate on CoValues and need to make compatible changes
|
||||
|
||||
- Luckily, thinking about the shape of your data first is also a really good way to model your app. Even before you know the details of how your app will work, you'll probably know which kinds of objects it will deal with, and how they relate to each other.
|
||||
|
||||
Jazz makes it really quick to define and update schemas, since they are simple TypeScript classes:
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
export class TodoProject extends CoMap {
|
||||
title = co.string;
|
||||
tasks = co.ref(ListOfTasks);
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
Here you can see how we use the `co` definer for declaring collaboratively editable fields, which ensures that schema info is correct at the type level and at runtime.
|
||||
|
||||
Classes might look a bit old-fashioned to some, but one nice property they have is that they are both types and values in TypeScript, so we can use them as both (with a single definition & import).
|
||||
|
||||
<CodeGroup>
|
||||
{/* prettier-ignore */}
|
||||
```ts
|
||||
import { TodoProject } from "./schema";
|
||||
|
||||
const project: TodoProject = TodoProject.create(
|
||||
{
|
||||
title: "New Project",
|
||||
tasks: ListOfTasks.create([], { owner: me }),
|
||||
},
|
||||
{ owner: me }
|
||||
);
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
## CoValue field types
|
||||
|
||||
Before we look at the different types of CoValues, let's learn what we can put *into* them:
|
||||
|
||||
### Primitive fields
|
||||
|
||||
### Refs
|
||||
|
||||
### Computed fields, methods & constructors
|
||||
75
homepage/homepage/app/docs/schemas/covalues/page.tsx
Normal file
75
homepage/homepage/app/docs/schemas/covalues/page.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import CoValuesGuide from "./covalues.mdx";
|
||||
import { TableOfContents } from "@/components/docs/TableOfContents";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
name: "CoValues",
|
||||
href: "/docs/schemas/covalues",
|
||||
},
|
||||
{
|
||||
name: "Schemas as Your App's First Step",
|
||||
href: "/docs/schemas/covalues#schemas-as-first-step",
|
||||
},
|
||||
{
|
||||
name: "CoValue field types",
|
||||
href: "/docs/schemas/covalues#field-types",
|
||||
items: [
|
||||
{
|
||||
name: "Primitive Fields",
|
||||
href: "/docs/schemas/covalues#primitive-fields",
|
||||
},
|
||||
{
|
||||
name: "Refs",
|
||||
href: "/docs/schemas/covalues#refs",
|
||||
},
|
||||
{
|
||||
name: "Computed Fields, Methods & Constructors ",
|
||||
href: "/docs/schemas/covalues#custom-fields",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CoMaps",
|
||||
href: "/docs/schemas/covalues#comaps",
|
||||
items: [
|
||||
{
|
||||
name: "Struct-like CoMaps",
|
||||
href: "/docs/schemas/covalues#comaps-struct-like",
|
||||
},
|
||||
{
|
||||
name: "Dict/Record-like CoMaps",
|
||||
href: "/docs/schemas/covalues#comaps-dict-like",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CoLists",
|
||||
href: "/docs/schemas/covalues#colists",
|
||||
},
|
||||
{
|
||||
name: "CoStreams",
|
||||
href: "/docs/schemas/covalues#costreams",
|
||||
},
|
||||
{
|
||||
name: "BinaryCoStreams",
|
||||
href: "/docs/schemas/covalues#binarycostreams",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"col-span-12 md:col-span-8 lg:col-span-9",
|
||||
"flex justify-center lg:gap-5",
|
||||
)}
|
||||
>
|
||||
<Prose className="overflow-x-hidden lg:flex-1">
|
||||
<CoValuesGuide />
|
||||
</Prose>
|
||||
<TableOfContents className="w-48 shrink-0" items={navItems} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
47
homepage/homepage/app/docs/sync-and-storage/page.tsx
Normal file
47
homepage/homepage/app/docs/sync-and-storage/page.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import SyncAndStorage from "./sync-and-storage.mdx";
|
||||
import { TableOfContents } from "@/components/docs/TableOfContents";
|
||||
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
name: "Using Jazz Cloud",
|
||||
href: "/docs/sync-and-storage#using-jazz-cloud",
|
||||
items: [
|
||||
{
|
||||
name: "Free Public Alpha",
|
||||
href: "/docs/sync-and-storage#free-public-alpha",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Running your own sync server",
|
||||
href: "/docs/sync-and-storage#running-your-own",
|
||||
items: [
|
||||
{
|
||||
name: "Command line options",
|
||||
href: "/docs/sync-and-storage#command-line-options",
|
||||
},
|
||||
{
|
||||
name: "Source code",
|
||||
href: "/docs/sync-and-storage#source-code",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"col-span-12 md:col-span-8 lg:col-span-9",
|
||||
"flex justify-center lg:gap-5",
|
||||
)}
|
||||
>
|
||||
<Prose className="overflow-x-hidden lg:flex-1">
|
||||
<SyncAndStorage />
|
||||
</Prose>
|
||||
<TableOfContents className="w-48 shrink-0" items={navItems} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
# Sync and Storage
|
||||
|
||||
## Using Jazz Cloud
|
||||
|
||||
Simply use `wss://cloud.jazz.tools/?key=...` as the sync server URL.
|
||||
|
||||
Jazz Cloud will
|
||||
- sync CoValues in real-time between users and devices
|
||||
- safely persist CoValues on redundant storage nodes with additional backups
|
||||
- make use of geographically distributed cache nodes for low latency
|
||||
|
||||
### Free Public Alpha <a id="free-public-alpha"/>
|
||||
|
||||
- Jazz Cloud is free during the public alpha, with no strict usage limits
|
||||
- We plan to keep a free tier, so you'll always be able to get started with zero setup
|
||||
- See [Jazz Cloud Pricing](https://jazz.tools/cloud#pricing) for more details
|
||||
- ⚠️ Please use a valid email address as your API key.
|
||||
|
||||
Your full sync server URL should look something like
|
||||
|
||||
```wss://cloud.jazz.tools/?key=you@example.com```
|
||||
|
||||
Once we support per-app API keys, we'll email you an API key you can use instead.
|
||||
|
||||
|
||||
## Running your own sync server <a id="running-your-own"/>
|
||||
|
||||
You can run your own sync server using:
|
||||
|
||||
```sh
|
||||
npx jazz-run sync
|
||||
```
|
||||
|
||||
And then use `ws://localhost:4200` as the sync server URL.
|
||||
|
||||
You can also run this simple sync server behind a proxy that supports WebSockets, for example to provide TLS.
|
||||
In this case, provide the WebSocket endpoint your proxy exposes as the sync server URL.
|
||||
|
||||
### Command line options: <a id="command-line-options"/>
|
||||
|
||||
- `--port` / `-p` - the port to run the sync server on. Defaults to 4200.
|
||||
- `--in-memory` - keep CoValues in-memory only and do sync only, no persistence. Persistence is enabled by default.
|
||||
- `--db` - the path to the file where to store the data (SQLite). Defaults to `sync-db/storage.db`.
|
||||
|
||||
### Source code <a id="source-code"/>
|
||||
|
||||
The implementation of this simple sync server is available open-source [on GitHub](https://github.com/gardencmp/jazz/blob/main/packages/jazz-run/src/startSync.ts).
|
||||
@@ -2,78 +2,6 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 20 14.3% 4.1%;
|
||||
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 20 14.3% 4.1%;
|
||||
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 20 14.3% 4.1%;
|
||||
|
||||
--primary: 24 9.8% 10%;
|
||||
--primary-foreground: 60 9.1% 97.8%;
|
||||
|
||||
--secondary: 60 4.8% 95.9%;
|
||||
--secondary-foreground: 24 9.8% 10%;
|
||||
|
||||
--muted: 60 4.8% 95.9%;
|
||||
--muted-foreground: 25 5.3% 44.7%;
|
||||
|
||||
--accent: 60 4.8% 95.9%;
|
||||
--accent-foreground: 24 9.8% 10%;
|
||||
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 60 9.1% 97.8%;
|
||||
|
||||
--border: 20 5.9% 90%;
|
||||
--input: 20 5.9% 90%;
|
||||
--ring: 20 14.3% 4.1%;
|
||||
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: 20 14.3% 4.1%;
|
||||
--foreground: 60 9.1% 97.8%;
|
||||
|
||||
--card: 20 14.3% 4.1%;
|
||||
--card-foreground: 60 9.1% 97.8%;
|
||||
|
||||
--popover: 20 14.3% 4.1%;
|
||||
--popover-foreground: 60 9.1% 97.8%;
|
||||
|
||||
--primary: 60 9.1% 97.8%;
|
||||
--primary-foreground: 24 9.8% 10%;
|
||||
|
||||
--secondary: 12 6.5% 15.1%;
|
||||
--secondary-foreground: 60 9.1% 97.8%;
|
||||
|
||||
--muted: 12 6.5% 15.1%;
|
||||
--muted-foreground: 24 5.4% 63.9%;
|
||||
|
||||
--accent: 12 6.5% 15.1%;
|
||||
--accent-foreground: 60 9.1% 97.8%;
|
||||
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 60 9.1% 97.8%;
|
||||
|
||||
--border: 12 6.5% 15.1%;
|
||||
--input: 12 6.5% 15.1%;
|
||||
--ring: 24 5.7% 82.9%;
|
||||
}
|
||||
|
||||
* {
|
||||
@apply border-border;
|
||||
}
|
||||
|
||||
.overlay-close {
|
||||
@apply bg-black;
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
@import "shiki.css";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import "./globals.css";
|
||||
import type { Metadata } from "next";
|
||||
import { ThemeProvider } from "@/components/themeProvider";
|
||||
|
||||
import { Inter, Manrope } from "next/font/google";
|
||||
import localFont from "next/font/local";
|
||||
@@ -9,6 +8,7 @@ import { SpeedInsights } from "@vercel/speed-insights/next";
|
||||
import { Analytics } from "@vercel/analytics/react";
|
||||
import { JazzNav } from "@/components/nav";
|
||||
import { JazzFooter } from "@/components/footer";
|
||||
import { ThemeProvider } from "gcmp-design-system/src/app/components/molecules/ThemeProvider";
|
||||
|
||||
// If loading a variable font, you don't need to specify the font weight
|
||||
const manrope = Manrope({
|
||||
@@ -77,13 +77,13 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<html lang="en" className="h-full" suppressHydrationWarning>
|
||||
<body
|
||||
className={[
|
||||
manrope.variable,
|
||||
commitMono.variable,
|
||||
inter.className,
|
||||
"flex flex-col items-center [&_*]:scroll-mt-[5rem]",
|
||||
"min-h-full flex flex-col items-center [&_*]:scroll-mt-[5rem]",
|
||||
"bg-white text-stone-700 dark:text-stone-400 dark:bg-stone-950",
|
||||
].join(" ")}
|
||||
>
|
||||
@@ -96,7 +96,7 @@ export default function RootLayout({
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<JazzNav />
|
||||
<main className="flex flex-col w-full">{children}</main>
|
||||
<main className="flex-1 w-full">{children}</main>
|
||||
<JazzFooter />
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -53,7 +53,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-900 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 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;
|
||||
@@ -79,7 +79,7 @@ pre.twoslash data-lsp:hover::before {
|
||||
}
|
||||
|
||||
pre .code-container {
|
||||
@apply overflow-auto p-2 pl-0 bg-white dark:bg-stone-925 rounded-b-xl text-xs h-full dark:border-stone-900;
|
||||
@apply overflow-auto p-2 pl-0 bg-white dark:bg-stone-925 rounded-b-xl text-xs h-full;
|
||||
}
|
||||
/* The try button */
|
||||
pre .code-container > a {
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function Page() {
|
||||
<Link
|
||||
href={product.url}
|
||||
key={product.url}
|
||||
className="group border bg-stone-50 shadow-sm p-3 flex flex-col gap-3 rounded-lg md:p-4 md:gap-4 dark:border-stone-900 dark:bg-stone-900"
|
||||
className="group border bg-stone-50 shadow-sm p-3 flex flex-col gap-3 rounded-lg md:p-4 md:gap-4 dark:bg-stone-900"
|
||||
>
|
||||
<Image
|
||||
className="rounded-md border dark:border-0"
|
||||
|
||||
@@ -29,7 +29,7 @@ export function CodeExampleTabs({
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className="flex border-b overflow-x-auto overflow-y-hidden dark:border-stone-900 dark:bg-stone-900">
|
||||
<div className="flex border-b overflow-x-auto overflow-y-hidden dark:bg-stone-900">
|
||||
{tabs.map((tab, index) => (
|
||||
<div key={index}>
|
||||
<button
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@ import { useLayoutEffect, useState, useRef, IframeHTMLAttributes } from "react";
|
||||
import { CopyIcon } from "lucide-react";
|
||||
|
||||
export function ResponsiveIframe(
|
||||
props: IframeHTMLAttributes<HTMLIFrameElement> & { localSrc: string },
|
||||
props: IframeHTMLAttributes<HTMLIFrameElement> & { localsrc: string },
|
||||
) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@@ -43,15 +43,15 @@ export function ResponsiveIframe(
|
||||
useLayoutEffect(() => {
|
||||
setSrc(
|
||||
window.location.hostname === "localhost"
|
||||
? props.localSrc
|
||||
? props.localsrc
|
||||
: props.src,
|
||||
);
|
||||
setUrl(
|
||||
window.location.hostname === "localhost"
|
||||
? props.localSrc
|
||||
? props.localsrc
|
||||
: props.src,
|
||||
);
|
||||
}, [props.src, props.localSrc]);
|
||||
}, [props.src, props.localsrc]);
|
||||
|
||||
const copyUrl = () => {
|
||||
if (url) {
|
||||
@@ -61,7 +61,7 @@ export function ResponsiveIframe(
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="bg-white flex gap-3 border-b dark:border-stone-900 text-xs dark:bg-stone-925">
|
||||
<div className="bg-white flex gap-3 border-b text-xs dark:bg-stone-925">
|
||||
<input
|
||||
className="flex-1 font-mono bg-transparent overflow-hidden text-ellipsis py-2 px-3"
|
||||
value={url?.replace("http://", "").replace("https://", "")}
|
||||
@@ -86,7 +86,7 @@ export function ResponsiveIframe(
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 bg-stone-100 flex items-stretch justify-center p-2 sm:p-6 dark:bg-stone-925">
|
||||
<div className="border rounded-lg overflow-hidden shadow-2xl w-[20rem] min-h-[30rem] dark:border-stone-900">
|
||||
<div className="border rounded-lg overflow-hidden shadow-2xl w-[20rem] min-h-[30rem]">
|
||||
<div className="h-full" ref={containerRef}>
|
||||
<iframe
|
||||
{...props}
|
||||
|
||||
77
homepage/homepage/components/SideNav.tsx
Normal file
77
homepage/homepage/components/SideNav.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
import { clsx } from "clsx";
|
||||
import { SideNavHeader } from "@/components/SideNavHeader";
|
||||
import { SideNavItem } from "@/components/SideNavItem";
|
||||
import React from "react";
|
||||
|
||||
interface SideNavItem {
|
||||
name: string;
|
||||
href?: string;
|
||||
done?: number;
|
||||
items?: SideNavItem[];
|
||||
}
|
||||
export function SideNav({
|
||||
items,
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
items: SideNavItem[];
|
||||
className?: string;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className={clsx(className, "text-sm space-y-5")}>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="inline-block size-2 rounded-full bg-yellow-400"></span>{" "}
|
||||
Documentation coming soon
|
||||
</div>
|
||||
|
||||
{items.map(({ name, href, items }) => (
|
||||
<div key={name}>
|
||||
<SideNavHeader href={href}>{name}</SideNavHeader>
|
||||
{items &&
|
||||
items.map(({ name, href, items, done }) => (
|
||||
<ul key={name}>
|
||||
<li>
|
||||
<SideNavItem
|
||||
href={
|
||||
done === 0
|
||||
? "/docs/coming-soon"
|
||||
: href
|
||||
}
|
||||
>
|
||||
{done == 0 && (
|
||||
<span className="mr-1.5 inline-block size-2 rounded-full bg-yellow-400"></span>
|
||||
)}
|
||||
|
||||
<span
|
||||
className={
|
||||
done === 0
|
||||
? "text-stone-400 dark:text-stone-600"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
</SideNavItem>
|
||||
</li>
|
||||
|
||||
{items && items?.length > 0 && (
|
||||
<ul className="pl-4">
|
||||
{items.map(({ name, href }) => (
|
||||
<li key={href}>
|
||||
<SideNavItem href={href}>
|
||||
{name}
|
||||
</SideNavItem>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</ul>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
21
homepage/homepage/components/SideNavHeader.tsx
Normal file
21
homepage/homepage/components/SideNavHeader.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import Link from "next/link";
|
||||
|
||||
export function SideNavHeader({
|
||||
href,
|
||||
children,
|
||||
}: {
|
||||
href?: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const className = "block font-medium text-stone-900 py-1 dark:text-white";
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<Link className={className} href={href}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return <p className={className}>{children}</p>;
|
||||
}
|
||||
35
homepage/homepage/components/SideNavItem.tsx
Normal file
35
homepage/homepage/components/SideNavItem.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ReactNode } from "react";
|
||||
import { clsx } from "clsx";
|
||||
import Link from "next/link";
|
||||
|
||||
export function SideNavItem({
|
||||
href,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
href?: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
const classes = clsx(
|
||||
className,
|
||||
"py-1 flex items-center hover:transition-colors",
|
||||
);
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={clsx(
|
||||
classes,
|
||||
href &&
|
||||
"hover:text-black dark:hover:text-stone-200 transition-colors hover:transition-none",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return <p className={classes}>{children}</p>;
|
||||
}
|
||||
45
homepage/homepage/components/docs/TableOfContents.tsx
Normal file
45
homepage/homepage/components/docs/TableOfContents.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import Link from "next/link";
|
||||
import { clsx } from "clsx";
|
||||
|
||||
interface NavItem {
|
||||
name: string;
|
||||
href: string;
|
||||
items?: NavItem[];
|
||||
}
|
||||
|
||||
export function TableOfContents({
|
||||
className,
|
||||
items,
|
||||
}: {
|
||||
items: NavItem[];
|
||||
className?: string;
|
||||
}) {
|
||||
if (!items.length) return null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"pl-3 sticky align-start top-[4.75rem] h-[calc(100vh-8rem)] overflow-y-auto overflow-x-hidden hidden md:block",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<p className="mb-3">On this page:</p>
|
||||
<ul className="space-y-2 text-sm list-disc pl-4">
|
||||
{items.map(({ name, href, items }) => (
|
||||
<li key={name} className="space-y-2">
|
||||
<Link href={href}>{name}</Link>
|
||||
{items && items?.length > 0 && (
|
||||
<ul className="list-disc pl-4 space-y-2">
|
||||
{items.map(({ name, href }) => (
|
||||
<li key={href}>
|
||||
<Link href={href}>{name}</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,172 +2,43 @@ import { requestProject } from "./requestProject";
|
||||
import { ChevronRight, PackageIcon } from "lucide-react";
|
||||
import { packages } from "@/lib/packages";
|
||||
import Link from "next/link";
|
||||
import { ReactNode } from "react";
|
||||
import { docNavigationItems } from "@/lib/docNavigationItems";
|
||||
import { SideNavItem } from "@/components/SideNavItem";
|
||||
import { SideNavHeader } from "@/components/SideNavHeader";
|
||||
import { SideNav } from "@/components/SideNav";
|
||||
import { clsx } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function DocNav({ className }: { className?: string }) {
|
||||
const comingSoon = [
|
||||
"Auth, Accounts & Migrations",
|
||||
"Edit Metadata & Time Travel",
|
||||
"Backend Workers",
|
||||
];
|
||||
|
||||
const covaluesItems = [
|
||||
{
|
||||
name: "Declaration",
|
||||
href: "/docs#declaring-covalues",
|
||||
},
|
||||
{
|
||||
name: "Reading",
|
||||
href: "/docs#reading-covalues",
|
||||
},
|
||||
{
|
||||
name: "Creation",
|
||||
href: "/docs#creating-covalues",
|
||||
},
|
||||
{
|
||||
name: "Editing & Subscription",
|
||||
href: "/docs#editing-and-subscription",
|
||||
},
|
||||
{
|
||||
name: "Persistence",
|
||||
href: "/docs#persistence",
|
||||
},
|
||||
{
|
||||
name: "Remote Sync",
|
||||
href: "/docs#remote-sync",
|
||||
},
|
||||
{
|
||||
name: "Simple Public Sharing",
|
||||
href: "/docs#simple-public-sharing",
|
||||
},
|
||||
];
|
||||
|
||||
const refsItems = [
|
||||
{
|
||||
name: "Precise Loading Depths",
|
||||
href: "/docs#loading-depth",
|
||||
},
|
||||
];
|
||||
|
||||
const groupsItems = [
|
||||
{
|
||||
name: "Groups/Accounts as Scopes",
|
||||
href: "/docs#groups-accounts-as-scopes",
|
||||
},
|
||||
{
|
||||
name: "Creating Invites",
|
||||
href: "/docs#creating-invites",
|
||||
},
|
||||
{
|
||||
name: "Consuming Invites",
|
||||
href: "/docs#consuming-invites",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={clsx(className, "text-sm space-y-5 pr-3")}>
|
||||
<SideNav
|
||||
items={docNavigationItems}
|
||||
className={clsx(
|
||||
twMerge(
|
||||
"pr-3 md:col-span-4 lg:col-span-3",
|
||||
"sticky align-start top-[4.75rem] h-[calc(100vh-8rem)] overflow-y-auto overflow-x-hidden",
|
||||
"hidden md:block",
|
||||
className,
|
||||
),
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<DocNavHeader href="/docs">Guide</DocNavHeader>
|
||||
<ul>
|
||||
<li>
|
||||
<DocNavLink href="/docs#guide-setup">
|
||||
Project Setup
|
||||
</DocNavLink>
|
||||
</li>
|
||||
<li>
|
||||
<DocNavLink href="/docs#intro-to-covalues">
|
||||
Intro to CoValues
|
||||
</DocNavLink>
|
||||
<ul>
|
||||
{covaluesItems.map((item) => (
|
||||
<li key={item.name}>
|
||||
<DocNavLink
|
||||
className="pl-4"
|
||||
href={item.href}
|
||||
>
|
||||
{item.name}
|
||||
</DocNavLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<DocNavLink href="/docs#refs-and-on-demand-subscribe">
|
||||
Refs & Auto-Subscribe
|
||||
</DocNavLink>
|
||||
<ul>
|
||||
{refsItems.map((item) => (
|
||||
<li key={item.name}>
|
||||
<DocNavLink
|
||||
className="pl-4"
|
||||
href={item.href}
|
||||
>
|
||||
{item.name}
|
||||
</DocNavLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<DocNavLink href="/docs#groups-and-permissions">
|
||||
Groups & Permissions
|
||||
</DocNavLink>
|
||||
<ul>
|
||||
{groupsItems.map((item) => (
|
||||
<li key={item.name}>
|
||||
<DocNavLink
|
||||
className="pl-4"
|
||||
href={item.href}
|
||||
>
|
||||
{item.name}
|
||||
</DocNavLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DocNavHeader>Coming soon</DocNavHeader>
|
||||
<ul>
|
||||
{comingSoon.map((item) => (
|
||||
<li className="py-1" key={item}>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DocNavHeader>Resources</DocNavHeader>
|
||||
<ul>
|
||||
<li>
|
||||
<DocNavLink href="/docs/resources/examples">
|
||||
Example Apps
|
||||
</DocNavLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<DocNavHeader href="/docs/api-reference">
|
||||
<SideNavHeader href="/docs/api-reference">
|
||||
API Reference
|
||||
</DocNavHeader>
|
||||
</SideNavHeader>
|
||||
<ul className="space-y-8">
|
||||
{packages.map(({ name }) => (
|
||||
<li key={name}>
|
||||
<NavPackage package={name} />
|
||||
<PackageNavItem package={name} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</SideNav>
|
||||
);
|
||||
}
|
||||
|
||||
export async function NavPackage({
|
||||
export async function PackageNavItem({
|
||||
package: packageName,
|
||||
}: {
|
||||
package: string;
|
||||
@@ -176,19 +47,19 @@ export async function NavPackage({
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocNavLink
|
||||
<SideNavItem
|
||||
className="mb-1 flex gap-2 items-center"
|
||||
href={`/docs/api-reference/${packageName}`}
|
||||
>
|
||||
<PackageIcon size={15} strokeWidth={1.5} />
|
||||
{packageName}
|
||||
</DocNavLink>
|
||||
</SideNavItem>
|
||||
{project.categories?.map((category) => {
|
||||
return (
|
||||
<details
|
||||
key={category.title}
|
||||
open={category.title !== "Other"}
|
||||
className="group ml-1.5 border-l dark:border-stone-900"
|
||||
className="group ml-1.5 border-l"
|
||||
>
|
||||
<summary className="pl-[13px] py-1 cursor-pointer flex gap-2 items-center justify-between hover:text-stone-800 dark:hover:text-stone-200 [&::-webkit-details-marker]:hidden">
|
||||
{category.title}
|
||||
@@ -217,45 +88,3 @@ export async function NavPackage({
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function DocNavLink({
|
||||
href,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
href: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={clsx(
|
||||
className,
|
||||
"py-1 hover:text-black dark:hover:text-stone-200 block hover:transition-colors",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function DocNavHeader({
|
||||
href,
|
||||
children,
|
||||
}: {
|
||||
href?: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
const className = "block font-medium text-stone-900 py-1 dark:text-white";
|
||||
|
||||
if (href) {
|
||||
return (
|
||||
<Link className={className} href={href}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return <p className={className}>{children}</p>;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user