Compare commits

...

65 Commits

Author SHA1 Message Date
Guido D'Orsi
ee9f868bb4 chore: use pnpm to run the prepublishOnly hook 2025-01-07 11:59:07 +01:00
Guido D'Orsi
133abd5d8f Merge pull request #1125 from garden-co/changeset-release/main
Version Packages
2025-01-07 11:47:02 +01:00
github-actions[bot]
1a789717f0 Version Packages 2025-01-07 10:15:13 +00:00
Trisha Lim
d4d98c4b48 Create react starter app for create-jazz-app (#1112) 2025-01-07 10:14:02 +00:00
Guido D'Orsi
51d8fb7e77 Merge pull request #1127 from tobiaslins/fix-chat-rn-example
[react native] Fix permissions of messages and fix chat loading
2025-01-03 18:03:16 +01:00
Tobias Lins
315a00bcb5 Update chat.tsx 2025-01-03 14:42:17 +01:00
Tobias Lins
ed4ee686b5 Fix permissions of messages and fix chat loading 2025-01-03 14:38:44 +01:00
Anselm Eickhoff
b4dc466526 Merge pull request #1120 from garden-co/jazz-608-add-version-history-example-app
Add version history example app
2025-01-03 11:27:10 +00:00
Trisha Lim
af9ea3dc4b Change key type to string 2025-01-03 11:21:44 +00:00
Guido D'Orsi
67abc0e437 Merge pull request #1103 from garden-co/jazz-616-simplify-the-create-calls-by-accepting-account-group-as
feat: simplify the .create calls by accepting Account | Group as second param
2025-01-03 11:54:07 +01:00
Guido D'Orsi
2034ef2dcb Merge pull request #1123 from garden-co/changeset-release/main
Version Packages
2025-01-02 22:31:49 +01:00
github-actions[bot]
343b4c0c80 Version Packages 2025-01-02 21:30:25 +00:00
Guido D'Orsi
41e4083f97 Merge pull request #1124 from garden-co/fix-range-error-flatMap
fix: handle circular references in group inheritance
2025-01-02 22:29:16 +01:00
Guido D'Orsi
15f81e889f fix: handle circular references in group inheritance 2025-01-02 22:18:12 +01:00
Guido D'Orsi
8d7fb18a08 Merge pull request #1122 from garden-co/fix-range-error-flatMap
fix(permissions): fix group self-extension
2025-01-02 21:24:40 +01:00
Guido D'Orsi
43378ef2b7 fix(permissions): fix group self-extension 2025-01-02 21:20:35 +01:00
Trisha Lim
1ca1d15639 Update readme 2025-01-02 18:04:34 +00:00
Trisha Lim
bdd67ef34d Add version history example to examples page 2025-01-02 17:55:10 +00:00
Trisha Lim
dc62b9569b Add changeset 2025-01-02 17:55:10 +00:00
Trisha Lim
6fd920face Add verstion history example app 2025-01-02 17:55:10 +00:00
Guido D'Orsi
0689ed3767 Merge pull request #1121 from garden-co/changeset-release/main
Version Packages
2025-01-02 18:35:58 +01:00
github-actions[bot]
a4e1aa7f56 Version Packages 2025-01-02 17:35:18 +00:00
Guido D'Orsi
ca5d7cb2ee Merge pull request #1119 from garden-co/fix-range-error-flatMap
fix(permissions): fixes the transactions collection to avoid RangeError issues
2025-01-02 18:34:10 +01:00
Guido D'Orsi
25dfd90c5a fix(permissions): fixes the transactions collection to avoid RangeError issues 2025-01-02 18:25:53 +01:00
Guido D'Orsi
66badc061e Merge pull request #1099 from garden-co/changeset-release/main
Version Packages
2025-01-02 16:31:02 +01:00
github-actions[bot]
eecdd5215e Version Packages 2025-01-02 13:48:31 +00:00
Guido D'Orsi
99b9605579 Merge pull request #1094 from garden-co/jazz-612-give-the-ability-to-reader-writer-and-writeonly-roles-to-set
feat: give the ability to extend a group to users that have any access to the parent
2025-01-02 14:47:13 +01:00
Guido D'Orsi
830f0c00ca Merge pull request #1116 from garden-co/fix/load-with-empty-schema
fix(CoMap): fix loading of CoMaps with an empty schema
2025-01-02 14:17:41 +01:00
Guido D'Orsi
635e824eb0 fix(CoMap): fix loading of CoMaps with an empty schema 2025-01-02 14:14:25 +01:00
Trisha Lim
c2a55c09e0 Fix indention 2025-01-02 12:59:50 +00:00
Trisha Lim
33b64743ed Make preview interactive in react guide 2025-01-02 12:59:50 +00:00
Trisha Lim
74a419e2df Add comment 2025-01-02 12:59:12 +00:00
Trisha Lim
170e135c79 Remove render-code-samples from turbo.json 2025-01-02 12:59:12 +00:00
Trisha Lim
305917236e Exclude h1 from table of contents 2025-01-02 12:59:12 +00:00
Guido D'Orsi
71e881afcb Merge pull request #1109 from garden-co/fix/missing-titles
Add titles to doc pages
2025-01-02 10:44:17 +01:00
Trisha Lim
f32af158f0 Add titles to doc pages 2024-12-31 15:13:47 +00:00
Anselm Eickhoff
e0d9bb3db3 Merge pull request #1105 from garden-co/jazz-615-remove-demo-and-code-from-examples 2024-12-30 15:24:22 +00:00
Guido D'Orsi
93f73b3b86 Merge pull request #1069 from garden-co/jazz-598-add-form-design-pattern-to-docs
Add form design pattern to docs
2024-12-30 16:10:59 +01:00
Guido D'Orsi
7e96af0602 Merge pull request #1106 from garden-co/jazz-613-add-docs-for-create-jazz-app
Mention create-jazz-app in docs
2024-12-30 16:10:11 +01:00
Trisha Lim
3a5d8d1ff4 Remove example demos 2024-12-30 14:55:19 +00:00
Trisha Lim
d558c085e6 Mention create-jazz-app in docs 2024-12-30 14:53:14 +00:00
Trisha Lim
07e52f50b3 Remove sentence about list of orders 2024-12-30 14:40:19 +00:00
Trisha Lim
9a775c67d5 Link to docs from example readme 2024-12-30 14:35:25 +00:00
Trisha Lim
a64d05f9a5 Simplify form design pattern docs 2024-12-30 14:30:10 +00:00
Trisha Lim
e43a6d4418 Address feedback 2024-12-30 11:56:34 +00:00
Trisha Lim
70182c5b28 Add form design pattern to docs 2024-12-30 11:56:34 +00:00
Guido D'Orsi
1de26f8be9 feat: simplify the .create calls by accepting Account | Group as second param 2024-12-30 12:11:51 +01:00
Anselm Eickhoff
af6c0878c3 Merge pull request #1101 from garden-co/gcmp-hp-fixes
More blog fixes
2024-12-30 11:01:45 +00:00
Anselm
579384a6df Update socials 2024-12-30 10:50:08 +00:00
Anselm
3376a02fde Fix OG pictures 2024-12-30 10:09:33 +00:00
Anselm Eickhoff
8440699193 Merge pull request #1091 from garden-co/jazz-611-missing-inbox-error 2024-12-29 15:12:04 +00:00
Anselm Eickhoff
d7f4ba5b9e Merge pull request #1098 from garden-co/docs/collapse-auth-items 2024-12-29 12:38:03 +00:00
Anselm Eickhoff
58a785db40 Merge pull request #1097 from garden-co/explode-docs-nav 2024-12-29 12:37:51 +00:00
Trisha Lim
7546e8bffc Docs side nav: move examples higher, collapse auth items 2024-12-29 11:03:41 +00:00
Trisha Lim
ad57ac21e0 Fix mock button alignment 2024-12-29 10:54:02 +00:00
Trisha Lim
f8b9854286 Move examples and api nav items to top level 2024-12-29 10:53:27 +00:00
Guido D'Orsi
10ea7332e6 feat: give the ability to extend a group to users that have any access to the parent 2024-12-27 21:16:50 +01:00
Guido D'Orsi
0a859821bc feat(Account): remove the requirement of calling super.migrate when defining the account migration 2024-12-27 11:35:14 +01:00
Anselm
b9408c6119 Fix OG some more 2024-12-26 16:25:10 +00:00
Anselm
c6c26b0ad1 Fix metadata 2024-12-26 16:23:31 +00:00
Anselm Eickhoff
9527c8a019 Merge pull request #1090 from garden-co/gcmp-hp-fixes
Soft-launch first blog post
2024-12-26 16:18:51 +00:00
Anselm
69bdcc0273 Fix type errors 2024-12-26 16:15:33 +00:00
Anselm
62cfd3bce4 Soft-launch first blog post 2024-12-26 16:09:22 +00:00
Anselm
0b8425d9b4 Blog prep 2024-12-24 09:31:10 +00:00
Anselm
94d95ceeb6 Fix garden logo, planned dates 2024-12-23 08:55:17 +00:00
215 changed files with 4816 additions and 1169 deletions

52
.github/workflows/build-starters.yaml vendored Normal file
View File

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

View File

@@ -1,5 +1,41 @@
# jazz-example-book-shelf
## 0.1.39
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser-media-images@0.8.51
- jazz-react@0.8.51
## 0.1.38
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.1.37
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.1.36
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.1.35
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-example-book-shelf",
"version": "0.1.35",
"version": "0.1.39",
"private": true,
"scripts": {
"dev": "next dev",
@@ -11,9 +11,9 @@
},
"dependencies": {
"clsx": "^2.0.0",
"jazz-browser-media-images": "workspace:0.8.45",
"jazz-react": "workspace:0.8.45",
"jazz-tools": "workspace:0.8.45",
"jazz-browser-media-images": "workspace:0.8.51",
"jazz-react": "workspace:0.8.51",
"jazz-tools": "workspace:0.8.51",
"next": "14.2.5",
"react": "^18.3.1",
"react-dom": "^18.3.1"

View File

@@ -31,7 +31,5 @@ export class JazzAccount extends Account {
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
migrate(this: JazzAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
}
migrate() {}
}

View File

@@ -1,5 +1,45 @@
# chat-rn-clerk
## 1.0.41
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react-native@0.8.51
- jazz-react-native-auth-clerk@0.8.51
- jazz-react-native-media-images@0.8.51
## 1.0.40
### Patch Changes
- jazz-react-native@0.8.50
- jazz-react-native-auth-clerk@0.8.50
- jazz-tools@0.8.50
- jazz-react-native-media-images@0.8.50
## 1.0.39
### Patch Changes
- jazz-react-native@0.8.49
- jazz-react-native-auth-clerk@0.8.49
- jazz-tools@0.8.49
- jazz-react-native-media-images@0.8.49
## 1.0.38
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react-native@0.8.48
- jazz-react-native-auth-clerk@0.8.48
- jazz-react-native-media-images@0.8.48
## 1.0.37
### Patch Changes

View File

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

View File

@@ -1,5 +1,37 @@
# chat-rn
## 1.0.38
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react-native@0.8.51
## 1.0.37
### Patch Changes
- jazz-react-native@0.8.50
- jazz-tools@0.8.50
## 1.0.36
### Patch Changes
- jazz-react-native@0.8.49
- jazz-tools@0.8.49
## 1.0.35
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react-native@0.8.48
## 1.0.34
### Patch Changes

View File

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

View File

@@ -19,45 +19,35 @@ import { Chat, Message } from "./schema";
export default function ChatScreen({ navigation }: { navigation: any }) {
const { me, logOut } = useAccount();
const [chat, setChat] = useState<Chat>();
const [chatId, setChatId] = useState<ID<Chat>>();
const loadedChat = useCoState(Chat, chatId, [{}]);
const [message, setMessage] = useState("");
const loadedChat = useCoState(Chat, chat?.id, [{}]);
useEffect(() => {
navigation.setOptions({
headerRight: () => <Button onPress={logOut} title="Logout" />,
headerLeft: () =>
chat ? (
loadedChat ? (
<Button
onPress={() => {
if (chat?.id) {
if (loadedChat?.id) {
Clipboard.setStringAsync(
`https://chat.jazz.tools/#/chat/${chat.id}`,
`https://chat.jazz.tools/#/chat/${loadedChat.id}`,
);
Alert.alert("Copied to clipboard", `Chat ID: ${chat.id}`);
Alert.alert("Copied to clipboard", `Chat ID: ${loadedChat.id}`);
}
}}
title="Share"
/>
) : null,
});
}, [navigation, chat]);
}, [navigation, loadedChat]);
const createChat = () => {
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const chat = Chat.create([], { owner: group });
setChat(chat);
};
const loadChat = async (chatId: ID<Chat>) => {
try {
const chat = await Chat.load(chatId, me, []);
setChat(chat);
} catch (error) {
console.log("Error loading chat", error);
Alert.alert("Error", `Error loading chat: ${error}`);
}
setChatId(chat.id);
};
const joinChat = () => {
@@ -73,7 +63,7 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
text: "Join",
onPress: (chatId) => {
if (chatId) {
loadChat(chatId as ID<Chat>);
setChatId(chatId as ID<Chat>);
} else {
Alert.alert("Error", "Chat ID cannot be empty.");
}
@@ -85,9 +75,11 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
};
const sendMessage = () => {
if (!chat) return;
if (!loadedChat) return;
if (message.trim()) {
chat.push(Message.create({ text: message }, { owner: chat._owner }));
loadedChat.push(
Message.create({ text: message }, { owner: loadedChat?._owner }),
);
setMessage("");
}
};
@@ -137,7 +129,7 @@ export default function ChatScreen({ navigation }: { navigation: any }) {
return (
<View className="flex flex-col h-full">
{!chat ? (
{!loadedChat ? (
<View className="flex flex-col h-full items-center justify-center">
<TouchableOpacity
onPress={createChat}

View File

@@ -1,5 +1,41 @@
# chat-vue
## 0.0.30
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser@0.8.51
- jazz-vue@0.8.51
## 0.0.29
### Patch Changes
- jazz-browser@0.8.50
- jazz-tools@0.8.50
- jazz-vue@0.8.50
## 0.0.28
### Patch Changes
- jazz-browser@0.8.49
- jazz-tools@0.8.49
- jazz-vue@0.8.49
## 0.0.27
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser@0.8.48
- jazz-vue@0.8.48
## 0.0.26
### Patch Changes

View File

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

View File

@@ -1,5 +1,41 @@
# jazz-example-chat
## 0.0.125
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser-media-images@0.8.51
- jazz-react@0.8.51
## 0.0.124
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.123
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.122
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.121
### Patch Changes

View File

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

View File

@@ -1,5 +1,41 @@
# minimal-auth-clerk
## 0.0.24
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react@0.8.51
- jazz-react-auth-clerk@0.8.51
## 0.0.23
### Patch Changes
- jazz-react@0.8.50
- jazz-react-auth-clerk@0.8.50
- jazz-tools@0.8.50
## 0.0.22
### Patch Changes
- jazz-react@0.8.49
- jazz-react-auth-clerk@0.8.49
- jazz-tools@0.8.49
## 0.0.21
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
- jazz-react-auth-clerk@0.8.48
## 0.0.20
### Patch Changes

View File

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

View File

@@ -1,5 +1,37 @@
# file-share-svelte
## 0.0.10
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-svelte@0.8.51
## 0.0.9
### Patch Changes
- jazz-tools@0.8.50
- jazz-svelte@0.8.50
## 0.0.8
### Patch Changes
- jazz-tools@0.8.49
- jazz-svelte@0.8.49
## 0.0.7
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-svelte@0.8.48
## 0.0.6
### Patch Changes

View File

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

View File

@@ -27,9 +27,7 @@ export class FileShareAccount extends Account {
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
async migrate(creationProps?: { name: string }) {
super.migrate(creationProps);
async migrate() {
await this._refs.root?.load();
// Initialize root if it doesn't exist

View File

@@ -1,5 +1,41 @@
# form
## 0.0.20
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser-media-images@0.8.51
- jazz-react@0.8.51
## 0.0.19
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.18
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.17
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.16
### Patch Changes

View File

@@ -15,6 +15,8 @@ which has the same structure as `BubbleTeaOrder`, but with all fields set to `op
When the user is ready to submit the order, we treat `DraftBubbleTeaOrder` as a "real order" by
converting it into a `BubbleTeaOrder`.
[See the full guide here.](https://jazz.tools/docs/react/design-patterns/form)
## Installing & running the example locally
(This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation))

View File

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

View File

@@ -32,9 +32,9 @@ export function CreateOrder() {
// reset the draft
me.root.draft = DraftBubbleTeaOrder.create(
{
addOns: ListOfBubbleTeaAddOns.create([], { owner: me }),
addOns: ListOfBubbleTeaAddOns.create([], me),
},
{ owner: me },
me,
);
router.navigate("/");

View File

@@ -69,20 +69,19 @@ export class AccountRoot extends CoMap {
export class JazzAccount extends Account {
root = co.ref(AccountRoot);
migrate(this: JazzAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
migrate() {
const account = this;
if (!this._refs.root) {
const ownership = { owner: this };
const orders = ListOfBubbleTeaOrders.create([], ownership);
const orders = ListOfBubbleTeaOrders.create([], account);
const draft = DraftBubbleTeaOrder.create(
{
addOns: ListOfBubbleTeaAddOns.create([], ownership),
addOns: ListOfBubbleTeaAddOns.create([], account),
},
ownership,
account,
);
this.root = AccountRoot.create({ draft, orders }, ownership);
this.root = AccountRoot.create({ draft, orders }, account);
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"root":["./src/app.tsx","./src/createorder.tsx","./src/draftindicator.tsx","./src/editorder.tsx","./src/errors.tsx","./src/linktohome.tsx","./src/orderform.tsx","./src/orderthumbnail.tsx","./src/orders.tsx","./src/main.tsx","./src/schema.ts","./src/vite-env.d.ts"],"version":"5.6.3"}

2
examples/form/vite.config.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfig;
export default _default;

View File

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

View File

@@ -1,5 +1,41 @@
# image-upload
## 0.0.22
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser-media-images@0.8.51
- jazz-react@0.8.51
## 0.0.21
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.20
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.19
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.18
### Patch Changes

View File

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

View File

@@ -10,7 +10,5 @@ export class JazzAccount extends Account {
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
migrate(this: JazzAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
}
migrate(this: JazzAccount) {}
}

View File

@@ -1,5 +1,29 @@
# jazz-example-inspector
## 0.0.92
### Patch Changes
- Updated dependencies [43378ef]
- cojson@0.8.50
- cojson-transport-ws@0.8.50
## 0.0.91
### Patch Changes
- Updated dependencies [25dfd90]
- cojson@0.8.49
- cojson-transport-ws@0.8.49
## 0.0.90
### Patch Changes
- Updated dependencies [10ea733]
- cojson@0.8.48
- cojson-transport-ws@0.8.48
## 0.0.89
### Patch Changes

View File

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

View File

@@ -1,5 +1,37 @@
# jazz-example-musicplayer
## 0.0.45
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react@0.8.51
## 0.0.44
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
## 0.0.43
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
## 0.0.42
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
## 0.0.41
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "jazz-example-music-player",
"private": true,
"version": "0.0.41",
"version": "0.0.45",
"type": "module",
"scripts": {
"dev": "vite",
@@ -18,8 +18,8 @@
"@radix-ui/react-toast": "^1.1.4",
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"jazz-react": "workspace:0.8.45",
"jazz-tools": "workspace:0.8.45",
"jazz-react": "workspace:0.8.51",
"jazz-tools": "workspace:0.8.51",
"lucide-react": "^0.274.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",

View File

@@ -85,9 +85,7 @@ export class MusicaAccount extends Account {
* The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
async migrate(creationProps?: { name: string }) {
super.migrate(creationProps);
migrate() {
if (!this._refs.root) {
const ownership = { owner: this };

View File

@@ -29,25 +29,23 @@ export async function uploadMusicTracks(
for (const file of files) {
// The ownership object defines the user that owns the created coValues
// We are creating a group for each CoValue in order to be able to share them via Playlist
const ownership = {
owner: Group.create({ owner: account }),
};
const group = Group.create(account);
const data = await getAudioFileData(file);
// We transform the file blob into a FileStream
// making it a collaborative value that is encrypted, easy
// to share across devices and users and available offline!
const fileStream = await FileStream.createFromBlob(file, ownership);
const fileStream = await FileStream.createFromBlob(file, group);
const musicTrack = MusicTrack.create(
{
file: fileStream,
duration: data.duration,
waveform: MusicTrackWaveform.create({ data: data.waveform }, ownership),
waveform: MusicTrackWaveform.create({ data: data.waveform }, group),
title: file.name,
},
ownership,
group,
);
// The newly created musicTrack can be associated to the
@@ -60,16 +58,14 @@ export async function createNewPlaylist(account: MusicaAccount) {
// Since playlists are meant to be shared we associate them
// to a group which will contain the keys required to get
// access to the "owned" values
const playlistGroup = Group.create({ owner: account });
const ownership = { owner: playlistGroup };
const playlistGroup = Group.create(account);
const playlist = Playlist.create(
{
title: "New Playlist",
tracks: ListOfTracks.create([], ownership),
tracks: ListOfTracks.create([], playlistGroup),
},
ownership,
playlistGroup,
);
// Again, we associate the new playlist to the
@@ -112,7 +108,6 @@ export async function addTrackToPlaylist(
*
* Doing this for backwards compatibility for when the Group inheritance wasn't possible
*/
const ownership = { owner: playlist._owner };
const blob = await FileStream.loadAsBlob(track._refs.file.id, account);
const waveform = await MusicTrackWaveform.load(
track._refs.waveform.id,
@@ -124,13 +119,16 @@ export async function addTrackToPlaylist(
const trackClone = MusicTrack.create(
{
file: await FileStream.createFromBlob(blob, ownership),
file: await FileStream.createFromBlob(blob, playlist._owner),
duration: track.duration,
waveform: MusicTrackWaveform.create({ data: waveform.data }, ownership),
waveform: MusicTrackWaveform.create(
{ data: waveform.data },
playlist._owner,
),
title: track.title,
sourceTrack: track,
},
ownership,
playlist._owner,
);
playlist.tracks?.push(trackClone);

View File

@@ -1,5 +1,41 @@
# jazz-example-onboarding
## 0.0.26
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser-media-images@0.8.51
- jazz-react@0.8.51
## 0.0.25
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.24
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.23
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.22
### Patch Changes

View File

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

View File

@@ -66,9 +66,7 @@ export class HRProfile extends Profile {
export class HRAccount extends Account {
profile = co.ref(HRProfile)!;
migrate(this: HRAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
migrate() {
if (!this.profile._refs.employees) {
this.profile.employees = EmployeeCoList.create([], {
owner: this.profile._owner,

View File

@@ -1,5 +1,37 @@
# organization
## 0.0.18
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react@0.8.51
## 0.0.17
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
## 0.0.16
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
## 0.0.15
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
## 0.0.14
### Patch Changes

View File

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

View File

@@ -38,9 +38,7 @@ export class JazzAccountRoot extends CoMap {
export class JazzAccount extends Account {
root = co.ref(JazzAccountRoot);
async migrate(creationProps?: { name: string }) {
super.migrate(creationProps);
async migrate() {
if (!this._refs.root) {
const draftOrganizationOwnership = {
owner: Group.create({ owner: this }),

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"root":["./src/acceptinvitepage.tsx","./src/homepage.tsx","./src/layout.tsx","./src/organizationpage.tsx","./src/main.tsx","./src/schema.ts","./src/vite-env.d.ts","./src/components/createorganization.tsx","./src/components/createproject.tsx","./src/components/errors.tsx","./src/components/heading.tsx","./src/components/invitelink.tsx","./src/components/organizationform.tsx","./src/components/organizationmembers.tsx","./src/components/organizationselector.tsx"],"version":"5.6.3"}

View File

@@ -0,0 +1,2 @@
declare const _default: import("vite").UserConfig;
export default _default;

View File

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

View File

@@ -1,5 +1,29 @@
# passkey-svelte
## 0.0.14
### Patch Changes
- jazz-svelte@0.8.51
## 0.0.13
### Patch Changes
- jazz-svelte@0.8.50
## 0.0.12
### Patch Changes
- jazz-svelte@0.8.49
## 0.0.11
### Patch Changes
- jazz-svelte@0.8.48
## 0.0.10
### Patch Changes

View File

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

View File

@@ -1,5 +1,37 @@
# minimal-auth-passkey
## 0.0.23
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react@0.8.51
## 0.0.22
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
## 0.0.21
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
## 0.0.20
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
## 0.0.19
### Patch Changes

View File

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

View File

@@ -1,5 +1,37 @@
# jazz-password-manager
## 0.0.44
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react@0.8.51
## 0.0.43
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
## 0.0.42
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
## 0.0.41
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
## 0.0.40
### Patch Changes

View File

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

View File

@@ -28,8 +28,7 @@ export class PasswordManagerAccount extends Account {
profile = co.ref(Profile);
root = co.ref(PasswordManagerAccountRoot);
migrate(this: PasswordManagerAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
migrate() {
if (!this._refs.root) {
const group = Group.create({ owner: this });
const firstFolder = Folder.create(

View File

@@ -1,5 +1,41 @@
# jazz-example-pets
## 0.0.142
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser-media-images@0.8.51
- jazz-react@0.8.51
## 0.0.141
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.140
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.139
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.138
### Patch Changes

View File

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

View File

@@ -44,8 +44,7 @@ export class PetAccount extends Account {
profile = co.ref(Profile);
root = co.ref(PetAccountRoot);
migrate(this: PetAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
migrate() {
if (!this._refs.root) {
this.root = PetAccountRoot.create(
{

View File

@@ -1,5 +1,41 @@
# reactions
## 0.0.22
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser-media-images@0.8.51
- jazz-react@0.8.51
## 0.0.21
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
- jazz-browser-media-images@0.8.50
## 0.0.20
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
- jazz-browser-media-images@0.8.49
## 0.0.19
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser-media-images@0.8.48
- jazz-react@0.8.48
## 0.0.18
### Patch Changes

View File

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

View File

@@ -1,5 +1,41 @@
# todo-vue
## 0.0.28
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-browser@0.8.51
- jazz-vue@0.8.51
## 0.0.27
### Patch Changes
- jazz-browser@0.8.50
- jazz-tools@0.8.50
- jazz-vue@0.8.50
## 0.0.26
### Patch Changes
- jazz-browser@0.8.49
- jazz-tools@0.8.49
- jazz-vue@0.8.49
## 0.0.25
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-browser@0.8.48
- jazz-vue@0.8.48
## 0.0.24
### Patch Changes

View File

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

View File

@@ -22,8 +22,7 @@ export class ToDoAccount extends Account {
profile = co.ref(Profile);
root = co.ref(ToDoAccountRoot);
migrate(this: ToDoAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
migrate() {
if (!this._refs.root) {
const group = Group.create({ owner: this });
const exampleTodo = ToDoItem.create(

View File

@@ -1,5 +1,37 @@
# jazz-example-todo
## 0.0.141
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react@0.8.51
## 0.0.140
### Patch Changes
- jazz-react@0.8.50
- jazz-tools@0.8.50
## 0.0.139
### Patch Changes
- jazz-react@0.8.49
- jazz-tools@0.8.49
## 0.0.138
### Patch Changes
- Updated dependencies [635e824]
- Updated dependencies [0a85982]
- jazz-tools@0.8.48
- jazz-react@0.8.48
## 0.0.137
### Patch Changes

View File

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

View File

@@ -39,8 +39,7 @@ export class TodoAccount extends Account {
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
migrate(this: TodoAccount, creationProps?: { name: string }) {
super.migrate(creationProps);
migrate() {
if (!this._refs.root) {
this.root = TodoAccountRoot.create(
{

24
examples/version-history/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,10 @@
# version-history
## 0.0.19
### Patch Changes
- Updated dependencies [dc62b95]
- Updated dependencies [1de26f8]
- jazz-tools@0.8.51
- jazz-react@0.8.51

View File

@@ -0,0 +1,38 @@
# Jazz Version History Example
A minimal example showing how to use Jazz's built-in version history to show and restore changes.
## Installing & running the example locally
(This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation))
Start by downloading the [jazz repository](https://github.com/garden-co/jazz):
```bash
npx degit gardencmp/jazz jazz
```
Go to the version-history example directory:
```bash
cd jazz/examples/version-history
```
Install and build dependencies:
```bash
pnpm i && npx turbo build
```
Start the dev server:
```bash
pnpm dev
```
## Questions / problems / feedback
If you have feedback, let us know on [Discord](https://discord.gg/utDMjHYg42) or open an issue or PR to fix something that seems wrong.
## Configuration: sync server
By default, the example app uses [Jazz Cloud](https://jazz.tools/cloud) (`wss://cloud.jazz.tools`) - so cross-device use, invites and collaboration should just work.
You can also run a local sync server by running `npx jazz-run sync` and adding the query param `?sync=ws://localhost:4200` to the URL of the example app (for example: `http://localhost:5173/?peer=ws://localhost:4200`), or by setting the `sync` parameter of `JazzProvider` component in [./src/main.tsx](./src/main.tsx).

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

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

View File

@@ -0,0 +1,30 @@
{
"name": "version-history",
"private": true,
"version": "0.0.19",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"format-and-lint": "biome check .",
"format-and-lint:fix": "biome check . --write"
},
"dependencies": {
"@tailwindcss/forms": "^0.5.9",
"jazz-react": "workspace:*",
"jazz-tools": "workspace:*",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwindcss": "^3.4.17"
},
"devDependencies": {
"@biomejs/biome": "1.9.4",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"globals": "^15.11.0",
"typescript": "~5.6.2",
"vite": "^5.4.10"
}
}

View File

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

View File

@@ -0,0 +1,65 @@
import { Group, ID } from "jazz-tools";
import { useState } from "react";
import { IssueComponent } from "./Issue.tsx";
import { IssueVersionHistory } from "./IssueVersionHistory.tsx";
import { useAccount, useCoState } from "./main";
import { Issue } from "./schema";
function App() {
const { me, logOut } = useAccount();
const [issueID, setIssueID] = useState<ID<Issue> | undefined>(
(window.location.search?.replace("?issue=", "") || undefined) as
| ID<Issue>
| undefined,
);
const issue = useCoState(Issue, issueID);
const createIssue = () => {
const group = Group.create({ owner: me });
group.addMember("everyone", "writer");
const newIssue = Issue.create(
{
title: "Buy terrarium",
description: "Make sure it's big enough for 10 snails.",
estimate: 5,
status: "backlog",
},
{ owner: group },
);
setIssueID(newIssue.id);
window.history.pushState({}, "", `?issue=${newIssue.id}`);
};
return (
<>
<header>
<nav className="container flex justify-between items-center py-3">
<span>
You're logged in as <strong>{me?.profile?.name}</strong>
</span>
<button
className="bg-stone-100 py-1.5 px-3 text-sm rounded-md"
onClick={() => logOut()}
>
Log out
</button>
</nav>
</header>
<main className="container my-8 flex flex-col gap-8">
{issue ? (
<>
<h1 className="sr-only">Issue: {issue.title}</h1>
<IssueComponent issue={issue} />
<hr />
<IssueVersionHistory id={issue.id} />
</>
) : (
<button onClick={createIssue}>Create Issue</button>
)}
</main>
</>
);
}
export default App;

View File

@@ -0,0 +1,57 @@
import { Issue } from "./schema";
export function IssueComponent({ issue }: { issue: Issue }) {
return (
<div className="flex flex-col gap-5">
<label className="flex flex-col gap-2">
Title
<input
type="text"
value={issue.title}
onChange={(event) => {
issue.title = event.target.value;
}}
/>
</label>
<label className="flex flex-col gap-2">
Description
<textarea
value={issue.description}
onChange={(event) => {
issue.description = event.target.value;
}}
/>
</label>
<div className="grid grid-cols-2 gap-8">
<label className="flex gap-2 items-center">
Estimate:
<input
type="number"
value={issue.estimate}
onChange={(event) => {
issue.estimate = Number(event.target.value);
}}
/>
</label>
<label className="flex gap-2 items-center">
Status
<select
className="flex-1"
value={issue.status}
onChange={(event) => {
issue.status = event.target.value as
| "backlog"
| "in progress"
| "done";
}}
>
<option value="backlog">Backlog</option>
<option value="in progress">In Progress</option>
<option value="done">Done</option>
</select>
</label>
</div>
</div>
);
}

View File

@@ -0,0 +1,104 @@
import { ID } from "jazz-tools";
import { useEffect, useMemo, useState } from "react";
import { useCoState } from "./main.tsx";
import { Issue } from "./schema.ts";
function DescriptionVersionHistory({ id }: { id: ID<Issue> }) {
const issue = useCoState(Issue, id);
const [version, setVersion] = useState<any | undefined>();
const [isVersionLatest, setIsVersionLatest] = useState(true);
const edits = useMemo(() => {
if (!issue) return [];
return issue._edits.description.all.reverse();
}, [issue?._edits]);
useEffect(() => {
if (!version) {
setVersion(edits[0]);
setIsVersionLatest(true);
}
}, [edits]);
if (!issue) return <div>Loading...</div>;
const selectVersion = (version: any, isLatest: boolean) => {
setVersion(version);
setIsVersionLatest(isLatest);
};
return (
<div>
<h2 className="mb-3">Description version history</h2>
<div className="grid grid-cols-3 border">
{version && (
<div className="col-span-2 border-r p-3 flex flex-col justify-between">
<p>{version.value}</p>
{!isVersionLatest && (
<button
className="bg-black text-white py-1 px-2 rounded"
onClick={() => (issue.description = version.value)}
>
Restore
</button>
)}
</div>
)}
<div className="flex flex-col gap-1 p-2 max-h-96 overflow-y-auto">
{edits.map((edit, i) => (
<button
key={i}
className="text-xs text-left p-2 hover:bg-stone-100"
onClick={() => selectVersion(edit, i === 0)}
>
{i == 0 ? "(Latest)" : ""}
<div className="font-medium">{edit.madeAt.toLocaleString()}</div>
<div className="text-stone-500">{edit.by?.profile?.name}</div>
</button>
))}
</div>
</div>
</div>
);
}
export function IssueVersionHistory({ id }: { id: ID<Issue> }) {
const issue = useCoState(Issue, id);
const edits = useMemo(() => {
if (!issue) return [];
return [
...issue._edits.title.all,
...issue._edits.estimate.all,
...issue._edits.status.all,
].sort((a, b) => (a.madeAt < b.madeAt ? -1 : a.madeAt > b.madeAt ? 1 : 0));
}, [issue?._edits]);
return (
<>
<div className="flex flex-col text-sm gap-2">
<h2 className="sr-only">Edits</h2>
{edits.map((edit, i) => (
<div>
<p className="text-xs text-stone-400">
{edit.madeAt.toLocaleString()}
</p>
<p className="text-stone-600" key={i}>
<span className="font-medium text-stone-800">
{edit.by?.profile?.name}
</span>{" "}
changed{" "}
<span className="font-medium text-stone-800">{edit.key}</span> to{" "}
<span className="font-medium text-stone-800">{edit.value}</span>
</p>
</div>
))}
</div>
<hr />
<DescriptionVersionHistory id={id} />
</>
);
}

View File

@@ -0,0 +1,47 @@
import { createInviteLink } from "jazz-react";
import { ID } from "jazz-tools";
import { IssueComponent } from "./Issue.tsx";
import { useCoState } from "./main.tsx";
import { Issue, Project } from "./schema.ts";
export function ProjectComponent({ projectID }: { projectID: ID<Project> }) {
const project = useCoState(Project, projectID, { issues: [{}] });
if (!project) return;
const invite = (role: "reader" | "writer") => {
const link = createInviteLink(project, role, { valueHint: "project" });
navigator.clipboard.writeText(link);
};
const createAndAddIssue = () => {
project?.issues.push(
Issue.create(
{
title: "",
description: "",
estimate: 0,
status: "backlog",
},
{ owner: project._owner },
),
);
};
return project ? (
<div>
<h1>{project.name}</h1>
{project._owner?.myRole() === "admin" && (
<>
<button onClick={() => invite("reader")}>Invite Guest</button>
<button onClick={() => invite("writer")}>Invite Member</button>
</>
)}
<div className="border-r border-b">
{project.issues.map((issue) => (
<IssueComponent key={issue.id} issue={issue} />
))}
<button onClick={createAndAddIssue}>Create Issue</button>
</div>
</div>
) : (
<div>Loading project...</div>
);
}

View File

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

View File

@@ -0,0 +1,36 @@
import { DemoAuthBasicUI, createJazzReactApp, useDemoAuth } from "jazz-react";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
const Jazz = createJazzReactApp();
export const { useAccount, useCoState } = Jazz;
function JazzAndAuth({ children }: { children: React.ReactNode }) {
const [auth, authState] = useDemoAuth();
return (
<>
<Jazz.Provider
auth={auth}
peer="wss://cloud.jazz.tools/?key=version-history@garden.co"
>
{children}
</Jazz.Provider>
{authState.state !== "signedIn" && (
<DemoAuthBasicUI appName="React + Demo Auth" state={authState} />
)}
</>
);
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<JazzAndAuth>
<App />
</JazzAndAuth>
</StrictMode>,
);

View File

@@ -0,0 +1,20 @@
/**
* Learn about schemas here:
* https://jazz.tools/docs/react/schemas/covalues
*/
import { CoList, CoMap, co } from "jazz-tools";
export class Issue extends CoMap {
title = co.string;
description = co.string;
estimate = co.number;
status? = co.literal("backlog", "in progress", "done");
}
export class ListOfIssues extends CoList.Of(co.ref(Issue)) {}
export class Project extends CoMap {
name = co.string;
issues = co.ref(ListOfIssues);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,8 @@
{
"build": {
"env": {
"APP_NAME": "version-history"
}
},
"ignoreCommand": "node ../../ignore-vercel-build.js"
}

View File

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

View File

@@ -0,0 +1,40 @@
import { clsx } from "clsx";
export function GardenLogo({
monochrome,
className,
}: {
monochrome?: boolean;
className?: string;
}) {
return (
<svg
viewBox="0 0 540 164"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={clsx(className, "text-black dark:text-white")}
>
<g clip-path="url(#clip0_3069_1)">
<path
d="M226.005 55.6V60.935C221.77 56.535 215.885 53.95 208.515 53.95C191.795 53.95 181.4 67.315 181.4 85.245C181.4 103.065 191.74 116.65 208.295 116.65C215.005 116.65 220.45 114.56 224.63 110.875V116.485C224.795 126.715 219.075 131.995 209.285 131.995C204.005 131.995 198.835 129.74 196.25 124.68L185.635 130.07C190.475 138.54 199.605 143.05 209.175 143.05C223.035 143.05 232.935 137.44 235.41 125.395C236.015 122.59 236.18 119.785 236.18 116.595V55.6H226.005ZM209.89 106.255C198.835 106.255 193.61 97.015 193.61 85.245C193.61 73.585 198.835 64.345 210.275 64.345C221.11 64.345 226.005 72.925 226.005 85.245C226.005 97.565 221.22 106.255 209.89 106.255ZM291.829 65.28C288.144 57.47 279.894 53.95 269.279 53.95C255.529 53.95 247.279 60.44 244.419 70.615L255.144 73.915C257.179 67.315 262.899 64.565 269.169 64.565C278.464 64.565 282.204 68.415 282.589 76.61C272.964 77.93 263.174 79.195 256.299 81.23C247.114 84.145 242.274 89.865 242.274 98.83C242.274 108.51 249.204 116.65 262.349 116.65C271.974 116.65 278.849 113.35 283.744 106.365V115H293.919V78.425C293.919 73.475 293.699 69.185 291.829 65.28ZM264.384 107.245C257.014 107.245 253.659 103.34 253.659 98.72C253.659 94.045 257.124 91.515 261.854 89.92C266.859 88.435 273.514 87.5 282.479 86.18C282.424 88.93 282.204 92.725 281.269 95.475C279.949 101.25 274.174 107.245 264.384 107.245ZM317.881 58.955C315.626 60.44 313.756 62.585 312.381 65.06V55.6H302.151V115H313.756V85.025C313.756 78.04 315.626 71.77 321.236 68.195C325.196 65.665 330.366 65.335 334.216 66.38V55.6C328.881 54.61 322.336 55.655 317.881 58.955ZM377.551 35.8V59.67C373.426 56.04 368.036 53.95 361.381 53.95C344.661 53.95 334.266 67.315 334.266 85.245C334.266 103.065 344.606 116.65 361.161 116.65C368.641 116.65 374.581 114.01 378.871 109.555V115H389.101V35.8H377.551ZM362.756 106.255C351.701 106.255 346.476 97.015 346.476 85.245C346.476 73.585 351.701 64.345 363.141 64.345C373.976 64.345 378.871 72.925 378.871 85.245C378.871 97.565 374.086 106.255 362.756 106.255ZM407.459 88.545H451.734C452.944 67.26 442.384 53.95 424.234 53.95C406.854 53.95 395.139 66.325 395.139 85.795C395.139 104.165 407.019 116.65 424.784 116.65C436.279 116.65 446.344 110.545 450.964 99.93L439.689 96.355C436.774 102.46 431.384 105.815 424.234 105.815C414.224 105.815 408.394 99.6 407.459 88.545ZM424.674 64.125C433.804 64.125 438.754 69.075 439.964 79.58H407.734C409.219 69.515 414.884 64.125 424.674 64.125ZM486.278 54.005C477.698 54.005 470.988 57.195 466.643 62.695V55.6H456.358V115H468.018V84.09C468.018 70.01 474.838 64.895 483.088 64.895C495.738 64.895 498.103 76.555 498.103 85.795V115H509.763V82C509.763 72.815 506.738 54.005 486.278 54.005Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M129.988 44.3845C130.172 44.2449 130.355 44.1054 130.539 43.9661L130.658 44.0735C130.434 44.1767 130.211 44.2804 129.988 44.3845ZM39.0525 139.076L45.3628 139.076C49.6432 131.896 54.1741 124.911 58.9404 118.13L57.3623 112.133H52.1219C46.8244 120.321 42.1253 128.999 38.102 138.123L39.0525 139.076Z"
fill={monochrome ? "currentColor" : "#42BB69"}
/>
<path
d="M82.5315 57.3805C79.3982 51.5075 76.846 45.4544 74.8578 39.275C74.5484 39.4079 74.2394 39.5426 73.931 39.679C65.031 43.6161 56.744 48.9482 49.5431 55.3708L49.543 55.3707C49.4471 55.4562 49.3516 55.542 49.2565 55.6281C49.4618 64.8598 51.8486 73.8528 56.4599 81.9511C51.0264 76.0801 46.3449 69.8102 42.4097 63.2312C41.3378 64.7087 40.3643 66.2329 39.4971 67.7939C36.9797 72.3255 35.4076 77.0787 34.8708 81.7821C34.3339 86.4854 34.8428 91.0468 36.3683 95.2058C37.8937 99.3648 40.406 103.04 43.7616 106.022C45.4394 107.512 47.506 108.627 49.8434 109.302C51.024 109.643 52.262 109.868 53.5385 109.977C60.6749 99.2754 68.8435 89.4366 77.8657 80.5635L77.8805 80.5489C93.1057 65.5787 110.761 53.3583 129.988 44.3845C130.172 44.2449 130.355 44.1054 130.539 43.9661L130.658 44.0735C130.434 44.1767 130.211 44.2804 129.988 44.3845C113.587 56.8641 98.2957 70.8841 84.357 86.3032L84.343 86.3187C75.6615 95.9234 67.5049 106.071 59.9322 116.727C60.0716 117.428 60.2633 118.113 60.5069 118.777C61.2696 120.856 62.5258 122.694 64.2036 124.184C67.5592 127.166 71.6924 129.395 76.3672 130.745C81.042 132.095 86.1669 132.54 91.4492 132.053C96.7316 131.566 102.068 130.157 107.154 127.907C110.193 126.563 113.106 124.934 115.837 123.058C104.785 118.874 94.3576 113.318 84.8544 106.375C97.965 112.264 113.15 114.043 128.211 111.832C132.295 107.26 135.812 102.344 138.67 97.2002C139.416 95.8575 140.114 94.5037 140.764 93.141C129.699 91.2029 118.905 88.0557 108.612 83.6429C120.293 86.137 132.678 85.8577 144.642 83.0796C145.66 79.6346 146.372 76.17 146.766 72.7208C147.146 69.3887 147.227 66.0974 147.01 62.877C139.63 61.9724 132.319 60.5435 125.138 58.5674C132.194 59.1483 139.343 58.7882 146.374 57.5667C145.876 54.7067 145.132 51.9198 144.145 49.2292C143.791 48.2641 143.407 47.3138 142.993 46.3792L142.272 44.3173L141.717 36.8508L133.329 36.371L131.011 35.7334C129.961 35.3664 128.892 35.026 127.807 34.7127C123.483 33.4639 118.939 32.6579 114.25 32.3015C113.594 37.0369 113.498 41.8046 113.999 46.5239C112.338 41.7743 111.017 36.9598 110.025 32.1028C107.192 32.0504 104.317 32.1572 101.414 32.4248C94.593 33.0536 87.7207 34.5604 81.0112 36.8888C80.2968 43.7957 80.7762 50.7136 82.5315 57.3805Z"
fill={monochrome ? "currentColor" : "#42BB69"}
/>
</g>
<defs>
<clipPath id="clip0_3069_1">
<rect width="540" height="164" fill="white" />
</clipPath>
</defs>
</svg>
);
}

View File

@@ -327,7 +327,12 @@ export function Nav(props: NavProps) {
/>
))}
<SocialLinks {...props.socials} />
<SocialLinks
{...props.socials}
className={
!items.find((item) => item.firstOnRight) ? "ml-auto" : ""
}
/>
{cta}
</PopoverGroup>

View File

@@ -1,10 +1,16 @@
import { SiDiscord, SiGithub, SiX } from "@icons-pack/react-simple-icons";
import {
SiBluesky,
SiDiscord,
SiGithub,
SiX,
} from "@icons-pack/react-simple-icons";
import { clsx } from "clsx";
export interface SocialLinksProps {
github?: string;
x?: string;
discord?: string;
bluesky?: string;
}
const socials = [
@@ -20,6 +26,12 @@ const socials = [
key: "discord",
size: 23,
},
{
name: "BlueSky",
icon: SiBluesky,
key: "bluesky",
size: 20,
},
{
name: "X",
icon: SiX,

View File

@@ -1,8 +1,10 @@
import { FormattedDate } from "@/components/FormattedDate";
import { NewsletterCard } from "@/components/blog/NewsletterCard";
import PostCoverImage from "@/components/blog/PostCoverImage";
import { PostJsonLd } from "@/components/blog/PostJsonLd";
import { BigGrass } from "@/components/blog/Swishes";
import { getPostBySlug, posts } from "@/lib/posts";
import { H1 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { H1, H2 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { Metadata } from "next";
import Image from "next/image";
@@ -15,22 +17,26 @@ export default async function Post({ params }: Params) {
return notFound();
}
const { title, coverImage, date, author, excerpt } = post.meta;
const { title, subtitle, coverImage, date, author } = post.meta;
const content = post.default({});
return (
<>
<PostJsonLd
title={title}
image={coverImage}
subtitle={subtitle}
image={coverImage.replace(".svg", ".png")}
author={author.name}
datePublished={date}
description={excerpt}
/>
<article className="container max-w-3xl flex flex-col gap-8 py-8 lg:py-16 lg:gap-12">
<div className="flex flex-col gap-2">
<H1>{title}</H1>
<div>
<H1 className="mb-2">{title}</H1>
<H2>{subtitle}</H2>
</div>
<PostCoverImage src={coverImage} title={title} className="rounded-lg" />
<div className="flex flex-col gap-2">
<div className="flex items-center gap-3">
<Image
width={100}
@@ -48,9 +54,12 @@ export default async function Post({ params }: Params) {
</div>
</div>
<PostCoverImage src={coverImage} title={title} />
<Prose size="md" className="text-stone-900 dark:text-stone-50">
{content}
<BigGrass />
</Prose>
<Prose>{content}</Prose>
<NewsletterCard />
</article>
</>
);
@@ -69,14 +78,18 @@ export function generateMetadata({ params }: Params): Metadata {
return notFound();
}
const { title, excerpt, coverImage } = post.meta;
const { title, subtitle, coverImage } = post.meta;
return {
title,
description: excerpt,
title: title,
description: subtitle,
openGraph: {
title,
images: [coverImage],
title: title,
images: [coverImage.replace(".svg", ".png")],
},
twitter: {
title: title + " " + subtitle,
images: [coverImage.replace(".svg", ".png")],
},
};
}

View File

@@ -1,8 +1,6 @@
import { NewsletterCard } from "@/components/blog/NewsletterCard";
import { Posts } from "@/components/blog/Posts";
import { HeroHeader } from "gcmp-design-system/src/app/components/molecules/HeroHeader";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { NewsletterForm } from "gcmp-design-system/src/app/components/organisms/NewsletterForm";
import Link from "next/link";
export const metadata = {
title: "Blog",
@@ -10,30 +8,12 @@ export const metadata = {
export default function NewsPage() {
return (
<div className="container">
<div className="container flex flex-col gap-10">
<HeroHeader title="Blog" slogan="" />
<Prose>
<p>Wow! You caught us a bit early.</p>
<p>You can subscribe to our newsletter below.</p>
<NewsletterForm />
<p>
Follow us on <Link href="https://x.com/gcmp_io">@garden.co</Link> or{" "}
<Link href="https://x.com/jazz_tools">@jazz_tools</Link>.
</p>
<p>
And if you want to build something with Jazz, you should definitely{" "}
<Link href="https://discord.gg/utDMjHYg42">
join the Jazz Discord
</Link>
!
</p>
</Prose>
<Posts />
<NewsletterCard />
</div>
);
}

View File

@@ -4,11 +4,7 @@ import { GcmpLogo } from "gcmp-design-system/src/app/components/atoms/logos/Gcmp
import { Nav } from "gcmp-design-system/src/app/components/organisms/Nav";
export function GcmpNav() {
const cta = (
<Button
variant="secondary"
className="ml-auto"
href="mailto:hello@garden.co"
>
<Button variant="secondary" className="ml-3" href="mailto:hello@garden.co">
Contact us
</Button>
);
@@ -22,6 +18,11 @@ export function GcmpNav() {
]}
cta={cta}
themeToggle={ThemeToggle}
socials={{
bluesky: "https://bsky.app/profile/garden.co",
x: "https://x.com/gardendotco",
github: "https://github.com/garden-co",
}}
></Nav>
);
}

View File

@@ -0,0 +1,53 @@
import { SiBluesky, SiX } from "@icons-pack/react-simple-icons";
import { Card } from "gcmp-design-system/src/app/components/atoms/Card";
import { H2 } from "gcmp-design-system/src/app/components/atoms/Headings";
import { Prose } from "gcmp-design-system/src/app/components/molecules/Prose";
import { NewsletterForm } from "gcmp-design-system/src/app/components/organisms/NewsletterForm";
import Link from "next/link";
export function NewsletterCard() {
return (
<Card className="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">
Stay up to date
</p>
<H2>Subscribe to our newsletter</H2>
<div className="flex justify-center">
<NewsletterForm />
</div>
<p>
<SiBluesky className="inline-block w-3.5 h-3.5 relative -top-px" />{" "}
follow{" "}
<Link className="underline" href="https://bsky.app/profile/garden.co">
@garden.co
</Link>{" "}
&{" "}
<Link
className="underline"
href="https://bsky.app/profile/jazz.tools"
>
@jazz.tools
</Link>
<br />
<SiX className="inline-block w-3 h-3 relative -top-px" /> follow{" "}
<Link className="underline" href="https://x.com/gardendotco">
@gardendotco
</Link>{" "}
&{" "}
<Link className="underline" href="https://x.com/jazz_tools">
@jazz_tools
</Link>
</p>
<p>
Want to build something with Jazz?{" "}
<Link className="underline" href="https://discord.gg/utDMjHYg42">
Join the Jazz Discord!
</Link>
</p>
</div>
</Card>
);
}

View File

@@ -1,20 +1,22 @@
export function PostJsonLd({
title,
subtitle,
image,
author,
datePublished,
description,
}: {
title: string;
subtitle: string;
image: string;
author: string;
datePublished: string;
description: string;
description?: string;
}) {
const jsonLd = {
"@context": "https://schema.org",
"@type": "Article",
headline: title,
headline: title + " " + subtitle,
image,
author: {
"@type": "Person",
@@ -25,7 +27,7 @@ export function PostJsonLd({
name: "Garden Computing",
},
datePublished,
description,
description: description || subtitle,
};
return (

View File

@@ -5,7 +5,7 @@ import Link from "next/link";
export function Posts() {
return (
<div className="grid grid-cols-3 gap-8">
<div className="grid md:grid-cols-3 gap-8">
{posts.map((post) => (
<div className="flex flex-col gap-2" key={post.meta.slug}>
<PostCoverImage
@@ -16,13 +16,13 @@ export function Posts() {
/>
<Link
href={`/news/${post.meta.slug}`}
className="text-lg font-medium text-display text-stone-900 dark:text-white"
className="text-stone-900 dark:text-white"
>
{post.meta.title}
<h1 className="text-2xl font-display font-semibold tracking-tight">
{post.meta.title}
</h1>
<h2 className="text-lg">{post.meta.subtitle}</h2>
</Link>
<p className="line-clamp-3 leading-relaxed text-ellipsis text-sm text-stone-900 dark:text-stone-400">
{post.meta.excerpt}
</p>
<div className="flex text-sm items-center">
{post.meta.author.name} <FormattedDate date={post.meta.date} />
</div>

View File

@@ -0,0 +1,35 @@
export function SmallGrass() {
return (
<svg
className="h-3 mx-auto my-8 opacity-50"
viewBox="0 0 42 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M9.32788 7.4541C7.85475 7.4541 6.43963 7.70242 5.12207 8.15948C11.5143 11.5696 16.6856 16.9676 19.8108 23.5282C20.0298 24.3677 20.3316 25.1737 20.7071 25.937C20.73 25.8774 20.753 25.8179 20.7762 25.7585C20.7994 25.8178 20.8224 25.8771 20.8452 25.9366C21.2204 25.1739 21.522 24.3686 21.7409 23.5298C24.8659 16.9686 30.0376 11.57 36.4302 8.15955C35.1126 7.70244 33.6974 7.4541 32.2242 7.4541C27.2242 7.4541 22.8924 10.3147 20.776 14.4886C18.6597 10.3147 14.3279 7.4541 9.32788 7.4541Z"
fill="currentColor"
/>
</svg>
);
}
export function BigGrass() {
return (
<svg
className="h-3 mx-auto my-8 opacity-50"
viewBox="0 0 67 36"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M3.55082 0.726562H0.100586C0.100586 3.93055 0.100586 5.53254 0.218747 6.88313C1.53212 21.8951 13.4342 33.7972 28.4462 35.1106C28.5789 35.1222 28.7139 35.1327 28.8527 35.1421C28.8526 35.171 28.8526 35.1999 28.8526 35.2288H32.3028C32.7112 35.2288 33.0936 35.2288 33.4528 35.2285C33.812 35.2288 34.1944 35.2288 34.6028 35.2288H38.053C38.053 35.1999 38.053 35.171 38.0529 35.1421C38.1917 35.1327 38.3267 35.1222 38.4594 35.1106C53.4714 33.7972 65.3735 21.8951 66.6869 6.88313C66.805 5.53254 66.805 3.93055 66.805 0.726562H63.3548C50.573 0.726562 39.4146 7.67702 33.4528 18.0046C27.4911 7.67702 16.3326 0.726562 3.55082 0.726562Z"
fill="currentColor"
/>
</svg>
);
}

View File

@@ -0,0 +1,108 @@
import { SmallGrass } from "@/components/blog/Swishes";
export const meta = {
slug: "hello-world",
title: "Hello World!",
subtitle: "(A love letter to computers)",
date: "2024-12-26",
coverImage: "/posts/post1.svg",
author: {
name: "Anselm Eickhoff",
image: "/team/anselm.jpg",
}
};
Doing anything successfully consists of two parts: *doing it,* and *talking about doing it.* This is true whether you do something by yourself, or as an organisation (such as a company).
Im very used to talking about what I personally am doing. You should try it! Lets [normalise sharing scrappy fiddles](https://www.todepond.com/sky/normalise-dont-share-lol/).
But this is the first time I am really talking *as my company,* Garden Computing.
<SmallGrass />
I thought speaking *as my company* would be hard, until I realised that Im simply creating Garden Computing in my own image. Or, more humbly, I am trying to make it the company Id love to exist. One that Id love working for.
So what do we do?
- Computer research
- Devtools & infrastructure
- End-user products
- Art
If you pause for a second, youll realise this covers pretty much everything you can do with computers. And thats important.
<SmallGrass />
Computers are special not in any one narrow sense but in the very fact that you can do so much with them. They are the most “you can just do stuff” object since paper.
But over their short history, computers have grown extremely complex. Hardware is somewhat unavoidably complex — making stuff tiny is *hard.* Software, however, doesnt really have an excuse. The success of computers is *despite* our haphazard attempts at organising ourselves around them. [The computer revolution still hasnt happened yet.](https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://www.youtube.com/watch%3Fv%3DoKg1hTOQXoY&ved=2ahUKEwiYh7XA-v-JAxVuRvEDHTBbGagQwqsBegQIDxAG&usg=AOvVaw1EWUQISqL3Fpi6dLOnvj5y)
We came up with a way *to bring abstractions to life and make them useful just by typing* and we still managed to make things ever more complicated. Granted, the problem domains we apply abstractions to can be inherently complex, but that is far overshadowed by the gigantic incidental complexity in software.
Its a skill issue on a societal level. Already early on, systems programmers were rightfully called the [“high priests of a low cult”](https://en.wikipedia.org/wiki/Robert_S._Barton#cite_note-6). The problem is that this has been a [vicious cycle](https://x.com/anselm_io/status/1297151607541506048) — incidental complexity leads to ~~nerds~~ specialists who lead to more incidental complexity and isolation of niches. Their whole identity relies on it. If youre reading this theres a high chance you are calling yourself a “React frontend developer” or something equally narrow.
But, again, whats special about the computer and the abstractions of software is their *universality.* Any time things get needlessly complex, weve lost sight of that.
And the only way to fix it is *to take responsibility for everything you can do with computers*.
Thats what I started Garden Computing for.
This sounds like an impossible task, but the good news is that the situation is so dire that there are very low-hanging fruits if you take a step back. And you only need to start in one place to see dramatic improvements.
<SmallGrass />
Of course, this means that you need to change how some things are done. Whenever you do that its important to really embrace that [everyone will not just](https://www.tumblr.com/squareallworthy/163790039847/everyone-will-not-just) [sic].
You cant fix computers just by thinking about computers. You have to think about psychology, incentives, academia, business and the everyday person. The past, the current and the future. Accidents and influencers. And you have to be extremely pragmatic.
Pragmatic doesnt have to mean mediocre. You can do bold things, but you need to be in touch with the mainstream as much as you are with your favourite fringes.
Thats where Ive always found myself situated and where Im trying to embed Garden Computing in this world: a vertical slab that integrates humans and computers everywhere.
<SmallGrass />
The other ingredient you need to change how things are done is something *actually new.* Im particularly fond of new *tall abstractions* that let you reach the same (or greater) heights while using much less material — like a beautiful arched bridge. For computers, the material you want to spend as little as possible of is complexity. And whenever you can replace dense stacks of shallow abstractions with a single tall abstraction, you know youve got a winner.
I found (or at least assembled) my first *tall abstraction* four years ago: local-first state (CRDTs) combined with local-first auth & permissions (public-key cryptography).
Over the years, this rough idea has solidified into three things:
- CoJSON (a soon-to-be-open protocol for local-first state & permissions, sync & persistence)
- Jazz (an open-source framework for building apps and server workers based on CoJSON)
- Jazz Cloud (hosted realtime sync & storage infrastructure for apps based on Jazz/CoJSON)
Together, they can completely replace *what traditional backends and databases do* with something simpler, more powerful and better suited to our beloved internet, which is a lot more distributed than wed like to admit.
They form a *tall abstraction* because you can use it for everything, it makes everything it touches easier and you can suddenly focus on a lot more not-computer problems than you could before. And even more excitingly, it makes building apps *more accessible*
Well cover CoJSON, Jazz and Jazz Cloud, as well as endeavours *building on them* in more detail in future blog posts, so stay tuned.
<SmallGrass />
While I would consider myself very lucky if Garden Computing became successful just based on Jazz and CoJSON, my main concern is how to foster a spirit that lets us *keep finding new things* and be the ones to make them useful.
And to understand how to foster that, Id like to reflect on the journey so far: The only reason I got as far as I have and was able to connect some unusual dots is my unique *broad* home in the computer world.
I am…
- a designer (by nurture — mom is a graphic designer)
- a mainstream web developer (by trade — the first thing that ever made me feel useful)
- a hacker (at heart — my parents are both serious tinkerers)
- an entrepreneur (against my upbringing — everyone around me was very risk-averse)
- and a failed “computer scientist” (who still reads papers — both attracted to and repelled by the rigour of academia)
There are very few people like me, and I dont mean that in a boasting way, I mean it in a sad way.
I know many people that are much better at any one thing than I am. And I know many generalists whose genius is severely under-appreciated by a world of niches.
What we need is generalists with *agency* — surrounded by a couple specialists who can tough out the hardest point problems, but still have enough broad context.
And guess what, I think I managed to create that in a small way.
<SmallGrass />
After four years of intense bootstrapped research and product development, I recently have found *generalists with agency* as investors and supporters, who believed in me and saw what I saw. And I have found an extra special handful of such people to make Garden Computing an actual *company,* not just a hollow corporate vessel for my hopes and dreams. And I couldnt be more proud of [our little team](https://garden.co/team), even though we still have so much to prove.
But give us a chance and well keep making computers everything they *can be*, by shipping software that is delightful, bold and new — and yet strangely familiar, humble and immediately *useful*.
Meet Garden Computing.

View File

@@ -0,0 +1,11 @@
export const meta = {
slug: "what-is-jazz",
title: "What is Jazz?",
subtitle: "(A framework for local-first state & permissions)",
date: "2024-12-27",
coverImage: "/posts/post2.svg",
author: {
name: "Anselm Eickhoff",
image: "/team/anselm.jpg",
},
};

View File

@@ -0,0 +1,11 @@
export const meta = {
slug: "what-we-shipped-since-summer",
title: "What we shipped since summer",
subtitle: "(The first six months of Garden Computing & Jazz)",
date: "2024-12-28",
coverImage: "/posts/post3.svg",
author: {
name: "Anselm Eickhoff",
image: "/team/anselm.jpg",
}
};

View File

@@ -7,8 +7,8 @@ export const meta = {
name: "Anselm Eickhoff",
picture: "/social-image.png",
},
excerpt:
"Jazz is a framework for local-first data/permissions. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut gravida vel urna sit amet lacinia. Morbi euismod mi ac lacus feugiat, vel sollicitudin urna faucibus. ",
subtitle:
"Jazz is a framework for local-first data/permissions. ",
};
Sed viverra in justo sit amet imperdiet. Proin posuere euismod ante, eu bibendum arcu lobortis non. Ut vitae placerat tellus. Phasellus quis tortor sollicitudin nisi scelerisque bibendum nec ac massa. Donec hendrerit felis non est elementum, at rutrum lorem sodales. Nam ligula urna, varius eu sem non, mollis dictum leo. Duis facilisis id sapien et feugiat.

View File

@@ -8,6 +8,14 @@ export default function Approach() {
<div className="grid gap-5">
<H2>Approach</H2>
<GappedGrid>
<GridCard>
<H3>Find tall abstractions</H3>
<P>
In a world drowning in complexity, we need to invent tools that let
us do more with less.
</P>
</GridCard>
<GridCard>
<H3>Grow open ecosystems</H3>

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