Compare commits

..

3 Commits

Author SHA1 Message Date
Trisha Lim
87b41fefad add multicursors example to cofeeds docs 2025-04-07 18:56:35 +07:00
Trisha Lim
265a4e8cc5 update readme for multi-cursors example 2025-04-07 18:53:03 +07:00
Trisha Lim
623467503f add multicursors example to examples page 2025-04-07 18:33:50 +07:00
6 changed files with 123 additions and 12 deletions

View File

@@ -1,3 +1,64 @@
# Multi-cursor example
# Jazz Multi-Cursors Example
An example app of using Jazz for showing multiple-cursors on a simple canvas.
Track user presence on a canvas with multiple cursors and out of bounds indicators.
## Getting started
You can either
1. Clone the jazz repository, and run the app within the monorepo.
2. Or create a new Jazz project using this example as a template.
### Using the example as a template
Create a new Jazz project, and use this example as a template.
```bash
npx create-jazz-app@latest multi-cursors-app --example multi-cursors
```
Go to the new project directory.
```bash
cd multi-cursors-app
```
Run the dev server.
```bash
npm run dev
```
### Using the monorepo
This requires `pnpm` to be installed, see [https://pnpm.io/installation](https://pnpm.io/installation).
Clone the jazz repository.
```bash
git clone https://github.com/garden-co/jazz.git
```
Install and build dependencies.
```bash
pnpm i && npx turbo build
```
Go to the example directory.
```bash
cd jazz/examples/multi-cursors/
```
Start the dev server.
```bash
pnpm dev
```
Open [http://localhost:5173](http://localhost:5173) with your browser to see the result.
## 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).

View File

@@ -1,5 +1,4 @@
import {
AlertCircleIcon,
AlertTriangleIcon,
ArrowDownIcon,
ArrowRightIcon,
@@ -27,6 +26,7 @@ import {
MessageCircleQuestionIcon,
MonitorSmartphoneIcon,
MoonIcon,
MousePointer2Icon,
MousePointerSquareDashedIcon,
ScanFace,
ScrollIcon,
@@ -50,6 +50,7 @@ const icons = {
close: XIcon,
code: CodeIcon,
copy: ClipboardIcon,
cursor: MousePointer2Icon,
darkTheme: MoonIcon,
delete: TrashIcon,
devices: MonitorSmartphoneIcon,

View File

@@ -8,7 +8,9 @@ CoFeeds are append-only data structures that track entries from different user s
Each account can have multiple sessions (different browser tabs, devices, or app instances), making CoFeeds ideal for building features like activity logs, presence indicators, and notification systems.
The [Reactions example](https://github.com/garden-co/jazz/tree/main/examples/reactions) demonstrates a practical use of CoFeeds.
The following examples demonstrate a practical use of CoFeeds:
- [Multi-cursors](https://github.com/garden-co/jazz/tree/main/examples/multi-cursors) - track user presence on a canvas with multiple cursors and out of bounds indicators
- [Reactions](https://github.com/garden-co/jazz/tree/main/examples/reactions) - store per-user emoji reaction using a CoFeed
## Creating CoFeeds

View File

@@ -253,6 +253,38 @@ const ReactionsIllustration = () => (
</div>
);
const MultiCursorIllustration = () => (
<div className="flex bg-stone-100 h-full flex-col items-center justify-center dark:bg-transparent p-4">
<div className=" bg-white md:aspect-[3/2] flex flex-col rounded-md shadow-xl shadow-stone-400/20 dark:shadow-none">
<div className="w-full py-2 flex items-center gap-1.5 px-2 border-b dark:border-b-stone-200">
<span className="rounded-full size-2 bg-stone-200"></span>
<span className="rounded-full size-2 bg-stone-200"></span>
<span className="rounded-full size-2 bg-stone-200"></span>
</div>
<div className="h-full mx-auto flex flex-col justify-center p-12 sm:p-16">
<div className="inline-block relative px-1 ring-1 ring-blue-400">
<div className="absolute size-2 bg-white border border-blue-400 -left-1 -top-1"></div>
<div className="absolute size-2 bg-white border border-blue-400 -right-1 -top-1"></div>
<div className="absolute size-2 bg-white border border-blue-400 -left-1 -bottom-1"></div>
<div className="absolute size-2 bg-white border border-blue-400 -right-1 -bottom-1"></div>
<span className="text-lg font-semibold md:text-2xl md:font-bold text-stone-800 ">
Hello, world!
</span>
<div className="absolute -top-10 right-4 text-rose-600 flex items-end gap-1">
<Icon name="cursor"></Icon> <span className="text-xs">Mia</span>
</div>
<div className="absolute -bottom-10 left-4 text-green-600 flex items-end gap-1">
<Icon name="cursor"></Icon>{" "}
<span className="text-xs">Sebastian</span>
</div>
</div>
</div>
</div>
</div>
);
const PetIllustration = () => (
<div className="h-full p-4 bg-[url('/dog.jpg')] bg-cover bg-center p-4 flex items-end">
<div className="inline-flex justify-center gap-1 mx-auto">
@@ -395,6 +427,16 @@ const reactExamples: Example[] = [
demoUrl: "https://reactions-demo.jazz.tools",
illustration: <ReactionsIllustration />,
},
{
name: "Cursor presence",
slug: "multi-cursors",
description:
"Track user presence on a canvas with multiple cursors and out of bounds indicators.",
tech: [tech.react],
features: [features.coFeed],
demoUrl: "https://jazz-multi-cursors.vercel.app",
illustration: <MultiCursorIllustration />,
},
{
name: "Rate my pet",
slug: "pets",
@@ -484,7 +526,8 @@ const rnExamples: Example[] = [
{
name: "Chat",
slug: "chat-rn",
description: "A simple React Native app that creates a chat room with a shareable link.",
description:
"A simple React Native app that creates a chat room with a shareable link.",
tech: [tech.reactNative],
illustration: <ChatIllustration />,
},
@@ -492,7 +535,8 @@ const rnExamples: Example[] = [
{
name: "Chat",
slug: "chat-rn-expo",
description: "A simple Expo app that creates a chat room with a shareable link.",
description:
"A simple Expo app that creates a chat room with a shareable link.",
tech: [tech.reactNative, tech.expo],
illustration: <ChatIllustration />,
},
@@ -500,7 +544,8 @@ const rnExamples: Example[] = [
{
name: "Chat",
slug: "chat-rn-expo-clerk",
description: "Exactly like the React Native Expo chat app, with Clerk for auth.",
description:
"Exactly like the React Native Expo chat app, with Clerk for auth.",
tech: [tech.reactNative, tech.expo],
features: [features.clerk],
illustration: <ClerkIllustration />,

View File

@@ -56,8 +56,8 @@ export const docNavigationItems = [
"react-native": 100,
"react-native-expo": 100,
},
}
]
},
],
},
{
name: "Tools",
@@ -82,7 +82,7 @@ export const docNavigationItems = [
{
name: "0.13.0 - React Native Split",
href: "/docs/upgrade/0-13-0",
done: 100
done: 100,
},
{
// upgrade guides

View File

@@ -26,11 +26,13 @@ export function InterpolateInCode(replace: { [key: string]: string }) {
}: { highlightedCode: string }) => {
const newHighlightedCode = Object.entries(replace).reduce(
(acc, [key, value]) => {
return acc.replaceAll(key, value);
return acc.replaceAll(
key.replaceAll("$", "&#36;").replaceAll("_", "&#95;"),
value,
);
},
highlightedCode,
);
return <div dangerouslySetInnerHTML={{ __html: newHighlightedCode }} />;
},
};