### Clone and Setup Automerge Tutorial Project
Source: https://automerge.org/docs/tutorial/setup
Clone the automerge-repo-quickstart repository, switch to the 'without-automerge' branch, install dependencies, and start the local development server.
```bash
$ git clone https://github.com/automerge/automerge-repo-quickstart# Cloning into 'automerge-repo-quickstart'...
$ cd automerge-repo-quickstart
$ git checkout without-automerge
$ npm install
# ...installing dependencies...
$ npm run dev
```
--------------------------------
### Initialize Automerge Repo with Storage and Network Adapters
Source: https://automerge.org/docs/tutorial/local-sync
Configure a Repo with IndexedDBStorageAdapter for persistence and BroadcastChannelNetworkAdapter for local sync. This setup allows data to be saved in the browser and synchronized across tabs.
```typescript
import React, { Suspense } from "react";import ReactDOM from "react-dom/client";import App from "./components/App.tsx";import "./index.css";import { initTaskList } from "./components/TaskList.tsx";import {
Repo,
BroadcastChannelNetworkAdapter,
IndexedDBStorageAdapter,
} from "@automerge/react";const repo = new Repo({
network: [new BroadcastChannelNetworkAdapter()],
storage: new IndexedDBStorageAdapter(),
});// Add the repo to the global window object so it can be accessed in the browser console// This is useful for debugging and testing purposes.declare global {
interface Window {
repo: Repo;
}
}
window.repo = repo;
ReactDOM.createRoot(document.getElementById("root")!).render(
Loading a document...}>
,
);
```
--------------------------------
### Initialize Automerge Repo with Storage and Network
Source: https://automerge.org/docs/cookbook/rich-text-prosemirror-vanilla
Sets up the Automerge repository with IndexedDB for local storage and a WebSocket adapter for network synchronization. Ensure the necessary packages are installed.
```javascript
import { DocHandle, Repo, isValidAutomergeUrl } from "@automerge/automerge-repo"
import { IndexedDBStorageAdapter } from "@automerge/automerge-repo-storage-indexeddb"
import { BrowserWebSocketClientAdapter } from "@automerge/automerge-repo-network-websocket"
import { init } from "@automerge/automerge-prosemirror"
const repo = new Repo({
storage: new IndexedDBStorageAdapter("automerge"),
network: [new BrowserWebSocketClientAdapter("wss://sync.automerge.org")],
})
```
--------------------------------
### Create a Repo with WebSocket and File System Adapters
Source: https://automerge.org/docs/reference/repositories
Initializes a Repo instance, configuring it to use a WebSocket server for network communication and the Node.js file system for local storage. This setup is suitable for server-side applications or environments where direct file system access is available.
```javascript
import { Repo } from "@automerge/automerge-repo"
import { WebSocketServer } from "ws"
import { NodeWSServerAdapter } from "@automerge/automerge-repo-network-websocket"
import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs"
const wss = new WebSocketServer({ noServer: true })
const repo = new Repo({
network: [new NodeWSServerAdapter(wss)],
storage: new NodeFSStorageAdapter(dir),
})
```
--------------------------------
### Val.town Automerge Initialization and Document Retrieval
Source: https://automerge.org/docs/reference/library-initialization
A Val.town example demonstrating manual WebAssembly initialization and setting up a Repo to retrieve document contents by ID. It uses the slim variants of Automerge and the repo, and a WebSocket client for network synchronization.
```typescript
import { BrowserWebSocketClientAdapter } from "npm:@automerge/automerge-repo-network-websocket";import { isValidAutomergeUrl, Repo } from "npm:@automerge/automerge-repo/slim";/* set up Automerge's internal wasm guts manually */import { automergeWasmBase64 } from "npm:@automerge/automerge/automerge.wasm.base64.js";import * as automerge from "npm:@automerge/automerge/slim";await automerge.initializeBase64Wasm(automergeWasmBase64);/* This example will return the contents of a documentID passed in as the path as JSON. */export default async function (req: Request): Promise {
const docId = new URL(req.url).pathname.substring(1);
if (!isValidAutomergeUrl("automerge:" + docId)) {
return Response.error();
}
const repo = new Repo({
network: [new BrowserWebSocketClientAdapter("wss://sync.automerge.org")],
});
const handle = await repo.find(docId);
const contents = handle.doc();
return Response.json(contents);
}
```
--------------------------------
### Install @automerge/react
Source: https://automerge.org/docs/tutorial/local-sync
Install the @automerge/react package to your project to begin using Automerge functionalities.
```bash
$ npm install @automerge/react# ...installing dependencies...
```
--------------------------------
### Setup MessageChannel Network Adapters for Two Repos
Source: https://automerge.org/docs/reference/repositories/networking
Configure two Repos to communicate within the same browser using MessageChannel. This involves creating a MessageChannel and wrapping each port with a MessageChannelNetworkAdapter.
```javascript
import { MessageChannelNetworkAdapter } from "@automerge/automerge-repo-network-messagechannel";
import { Repo } from "@automerge/automerge-repo";
const { port1: leftToRight, port2: rightToLeft } = new MessageChannel();
const rightToLeft = new MessageChannelNetworkAdapter(rightToLeft);
const leftToRight = new MessageChannelNetworkAdapter(leftToRight);
const left = new Repo({
network: [leftToRight],
});
const right = new Repo({
network: [rightToLeft],
});
```
--------------------------------
### Hard-code Initial Change for Independent Schema Setup
Source: https://automerge.org/docs/cookbook/modeling-data
Use this method to initialize a document with a pre-defined schema by hard-coding an initial change as a byte array. This allows independent schema setup on different devices while ensuring merge compatibility.
```javascript
// hard-code the initial change here
const initChange = new Uint8Array([133, 111, 74, 131, ...])
let [doc] = Automerge.load(initChange)
```
--------------------------------
### Create a Ref and Get its Value
Source: https://automerge.org/docs/reference/repositories/refs
Create a document, then obtain a reference to a specific field within a list item. You can then retrieve the value at that reference.
```javascript
const handle = repo.create({
todos: [
{ title: "First", done: false },
{ title: "Second", done: true },
]
})
const doneRef = handle.ref("todos", 0, "done")
// Logs false
console.log("first todo done:", doneRef.value())
```
--------------------------------
### Setup RepoContext in React App
Source: https://automerge.org/docs/tutorial/react
Wrap your App component with RepoContext.Provider to make the Automerge repo available to child components via hooks. Ensure Suspense is used for asynchronous document loading.
```tsx
import {
Repo,
BroadcastChannelNetworkAdapter,
IndexedDBStorageAdapter,
RepoContext,
isValidAutomergeUrl,
DocHandle,
} from "@automerge/react";
// ...
ReactDOM.createRoot(document.getElementById("root")!).render(
Loading a document...}>
,
);
```
--------------------------------
### Vite Configuration
Source: https://automerge.org/docs/reference/library-initialization
Vite requires specific plugins, `vite-plugin-wasm` and `vite-plugin-top-level-await`, to handle WebAssembly and top-level await. Install them and add them to your `vite.config.js`.
```javascript
yarn add vite-plugin-wasm vite-plugin-top-level-await
```
```javascript
import { defineConfig } from 'vite'
import wasm from 'vite-plugin-wasm'
import topLevelAwait from 'vite-plugin-top-level-await'
export default defineConfig({
...,
plugins: [wasm(), topLevelAwait()],
...
})
```
--------------------------------
### Example Rich Text Document Spans
Source: https://automerge.org/docs/reference/under-the-hood/rich-text-schema
An example array of spans representing a rich text document, including text, blockquotes, ordered lists, and inline formatting like bold and links.
```typescript
[
{
type: "text", value: "From the automerge docs:"
},
{
type: "block",
value: { parents: ["blockquote"], type: "paragraph" },
},
{
type: "text", value: "The requirements we have for this schema are:"
},
{
type: "block",
value: { parents: ["blockquote", "ordered-list"], type: "paragraph"},
},
{
type: "text": value: "The ability to represent inline text decoration such as bold spans, as well as semantic information like hyperlinks or code spans"
},
{
type: "block",
value: { parents: ["blockquote", "ordered-list"], type: "paragraph"},
},
{
type: "text": value: "A way of representing hierarchical structure which merges well - or, alternatively, which results in patches which are commensurate in size with the editing action the user took (inserting a paragraph is a single user action, we would like it to not result in a large patch which is hard to interpret)"
},
{
type: "block", value: {parents: ["blockquote"], type: "paragraph"}
},
{
type: "text", value: "..."
}
{
type: "block", value: { parents: [], type: "paragraph"}
},
{
type: "text", value: "From: ", marks: { strong: true }
},
{
type: "text", value: "Rich Text Schema", marks: { link: '{\"href\": \"/\", title: \"\"}', em: true}
}
]
```
--------------------------------
### Create and Get Root Document Management Functions
Source: https://automerge.org/docs/tutorial/persist-root-doc
This function checks local storage for an existing root document URL. If found, it returns the URL; otherwise, it creates a new root document, stores its URL in local storage, and then returns the URL. This ensures the root document is persisted across browser sessions.
```typescript
import { AutomergeUrl, Repo } from "@automerge/react";
const ROOT_DOC_URL_KEY = "root-doc-url";
export type RootDocument = {
taskLists: AutomergeUrl[];
};
export const getOrCreateRoot = (repo: Repo): AutomergeUrl => {
// Check if we already have a root document
const existingUrl = localStorage.getItem(ROOT_DOC_URL_KEY);
if (existingUrl) {
return existingUrl as AutomergeUrl;
}
// Otherwise create one and (synchronously) store it
const root = repo.create({ taskLists: [] });
localStorage.setItem(ROOT_DOC_URL_KEY, root.url);
return root.url;
};
```
--------------------------------
### Create and Modify a Document Handle
Source: https://automerge.org/docs/reference/repositories
Demonstrates how to create a new document handle using a Repo instance and make an initial change to it. It also shows how to subscribe to 'change' events to log updates received from other peers.
```javascript
let doc = repo.create()
// Make a change ourselves and send that to everyone else
doc.change((d) => (d.text = "hello world"))
// Listen for changes from other peers
doc.on("change", ({ doc }) => {
console.log("new text is ", doc.text)
})
```
--------------------------------
### Initialize Automerge Repo and Root Document
Source: https://automerge.org/docs/tutorial/persist-root-doc
Sets up the Automerge repository with network and storage adapters, initializes the root document, and makes the repo and handle globally accessible for debugging.
```typescript
import React, { Suspense } from "react";
import ReactDOM from "react-dom/client";
import App from "./components/App.tsx";
import "./index.css";
import {
Repo,
BroadcastChannelNetworkAdapter,
WebSocketClientAdapter,
IndexedDBStorageAdapter,
RepoContext,
DocHandle,
} from "@automerge/react";
import { getOrCreateRoot, RootDocument } from "./rootDoc.ts";
const repo = new Repo({
network: [
new BroadcastChannelNetworkAdapter(),
new WebSocketClientAdapter("wss://sync.automerge.org"),
],
storage: new IndexedDBStorageAdapter(),
});
// Add the repo to the global window object so it can be accessed in the browser console
// This is useful for debugging and testing purposes.
declare global {
interface Window {
repo: Repo;
// We also add the handle to the global window object for debugging
handle: DocHandle;
}
}
window.repo = repo;
const rootDocUrl = getOrCreateRoot(repo);
window.handle = await repo.find(rootDocUrl);
ReactDOM.createRoot(document.getElementById("root")!).render(
Loading a document...}>
,
);
```
--------------------------------
### Initialize WebSocket Client Adapter
Source: https://automerge.org/docs/reference/repositories/networking
Instantiate the WebSocketClientAdapter to connect to a WebSocket server. Ensure the server is running at the specified URL.
```javascript
import { WebSocketClientAdapter } from "@automerge/automerge-repo-network-websocket";
const network = new WebSocketClientAdapter("ws://localhost:3030");
```
--------------------------------
### Initialize and Merge Documents
Source: https://automerge.org/docs/cookbook/modeling-data
Demonstrates initializing a document with a schema and merging changes from another document. Ensure schema initialization is done once before merging.
```javascript
let doc1 = Automerge.change(Automerge.init(), (doc) => {
doc.cards = [];
});
let doc2 = Automerge.merge(Automerge.init(), doc1);
doc1 = Automerge.change(doc1, (doc) => {
doc.cards.push({ title: "card1" });
});
doc2 = Automerge.change(doc2, (doc) => {
doc.cards.push({ title: "card2" });
});
doc1 = Automerge.merge(doc1, doc2);
doc2 = Automerge.merge(doc2, doc1);
```
--------------------------------
### initSyncState
Source: https://automerge.org/automerge/api-docs/js
Initializes a new sync state.
```APIDOC
## initSyncState
### Description
Initializes a new sync state.
### Function Signature
`initSyncState(): SyncState`
```
--------------------------------
### Initialize Root Document
Source: https://automerge.org/docs/tutorial/multiple-task-lists
This code snippet shows how to initialize the root document, creating it if it doesn't exist and setting up an initial task list. It also sets the location hash to the new document's URL.
```typescript
if (isValidAutomergeUrl(locationHash)) { const taskList = await repo.find(locationHash); window.handle = repo.create({ taskLists: [taskList.url] });} else { const taskList = repo.create(initTaskList()); window.handle = repo.create({ taskLists: [taskList.url] }); // Set the location hash to the new document we just made. document.location.hash = taskList.url;}window.handle = repo.create({ taskLists: [] });
```
--------------------------------
### React Component for ProseMirror Editor
Source: https://automerge.org/docs/cookbook/rich-text-prosemirror-react
This React component integrates Automerge with ProseMirror to create a collaborative rich text editor. It handles editor initialization, state management, and cleanup. Ensure Automerge and ProseMirror are installed.
```jsx
import { AutomergeUrl } from "@automerge/automerge-repo"
import { useHandle } from "@automerge/automerge-repo-react-hooks"
import { useEffect, useRef, useState } from "react"
import {EditorState} from "prosemirror-state"
import {EditorView} from "prosemirror-view"
import {exampleSetup} from "prosemirror-example-setup"
import { init } from "@automerge/prosemirror"
import "prosemirror-example-setup/style/style.css"
import "prosemirror-menu/style/menu.css"
import "prosemirror-view/style/prosemirror.css"
import "./App.css"
function App({ docUrl }: { docUrl: AutomergeUrl }) {
const editorRoot = useRef(null)
const handle = useHandle<{text: string}>(docUrl)
const [loaded, setLoaded] = useState(handle && handle.docSync() != null)
useEffect(() => {
if (handle != null) {
handle.whenReady().then(() => {
if (handle.docSync() != null) {
setLoaded(true)
}
})
}
}, [handle])
const [view, setView] = useState(null)
useEffect(() => {
if (editorRoot.current != null && loaded) {
// This is the integration with automerge
const { pmDoc: doc, schema, plugin } = init(handle!, ["text"])
const plugins = exampleSetup({schema})
plugins.push(plugin)
const view = new EditorView(editorRoot.current, {
state: EditorState.create({
schema,
plugins,
doc,
}),
})
setView(view)
}
return () => {
if (view) {
view.destroy()
setView(null)
}
}
}, [editorRoot, loaded])
return
}
export default App
```
--------------------------------
### Apply Bold Mark to Text in Automerge
Source: https://automerge.org/docs/reference/documents/rich-text
Use Automerge.mark to apply formatting to a range of text. Specify the start and end of the range, an expand option, the mark name, and its value. Retrieve active marks using Automerge.marks.
```javascript
import * as Automerge from "@automerge/automerge"
let doc = Automerge.from({text: "hello world"})
Automerge.change(doc, d => {
Automerge.mark(d, ["text"], {start: 0, end: 5, expand: "both"}, "bold", true)
})
console.log(Automerge.marks(doc, ["text"]))
>> [ { name: 'bold', value: true, start: 0, end: 5 } ]
```
--------------------------------
### Creating and Using Refs
Source: https://automerge.org/docs/reference/repositories/refs
Demonstrates how to create a Ref to a specific property within a document and how to read its value.
```APIDOC
## Creating and Using Refs
### Description
This example shows how to create a reference to a specific field within a document and retrieve its current value.
### Method
Implicitly through document handle methods.
### Endpoint
N/A (SDK method)
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Request Example
```javascript
const handle = repo.create({
todos: [
{ title: "First", done: false },
{ title: "Second", done: true },
]
});
const doneRef = handle.ref("todos", 0, "done");
```
### Response
#### Success Response (200)
N/A (SDK method returns a Ref object)
#### Response Example
```javascript
// Logs false
console.log("first todo done:", doneRef.value());
```
```
--------------------------------
### Registering Selected Document in Root Document
Source: https://automerge.org/docs/tutorial/multiple-task-lists
This effect hook in `DocumentList` ensures that if a selected document URL is valid and not already present in the root document's `taskLists`, it gets added. This is crucial for persisting the selection across reloads.
```typescript
import { useEffect } from "react";
export const DocumentList: React.FC<{
docUrl: AutomergeUrl;
selectedDocument: AutomergeUrl | null;
onSelectDocument: (docUrl: AutomergeUrl | null) => void;
}> = ({ docUrl, selectedDocument, onSelectDocument }) => {
const repo = useRepo();
const [doc, changeDoc] = useDocument(docUrl, {
suspense: true,
});
useEffect(() => {
changeDoc((d) => {
if (selectedDocument && !d.taskLists.includes(selectedDocument)) {
// If the selected document is not in the list, add it
d.taskLists.push(selectedDocument);
}
});
}, [selectedDocument, changeDoc]);
const handleNewDocument = () => {
const newTaskList = repo.create(initTaskList());
changeDoc((d) => d.taskLists.push(newTaskList.url));
onSelectDocument(newTaskList.url);
};
return (
// ..
);
};
```
--------------------------------
### Initialize BroadcastChannel Network Adapter
Source: https://automerge.org/docs/reference/repositories/networking
Create a BroadcastChannelNetworkAdapter for inter-process communication within the same browser. Note that this can be less efficient than MessageChannel due to the sync protocol's point-to-point nature.
```javascript
import { BroadcastChannelNetworkAdapter } from "@automerge/automerge-repo-network-broadcastchannel";
const network = new BroadcastChannelNetworkAdapter();
```
--------------------------------
### Creating and Manipulating Refs
Source: https://automerge.org/docs/reference/repositories/refs
Demonstrates how to create a document, obtain references to specific fields or array elements, and perform operations like removal. It also shows how to log the document state after modifications.
```APIDOC
## Create and Manipulate Refs
### Description
This section illustrates the creation of Automerge documents and the subsequent use of the Refs API to target and modify specific data within those documents. It covers referencing fields, array elements, and using pattern matching for selections.
### Method
N/A (SDK Example)
### Endpoint
N/A (SDK Example)
### Parameters
N/A (SDK Example)
### Request Example
```javascript
handle = repo.create({ user: { name: "Alice", age: 30, hobbies: ["biking", "spelunking", "weaving"] }})
const ageRef = handle.ref("user", "age")
ageRef.remove()
// Logs undefined
console.log("age is", handle.doc().user.age)
const nameRef = handle.ref("user", "name", cursor(2,5))
nameRef.remove()
// Logs Al
console.log("name is", handle.doc().user.name)
const bikingHobbyRef = handle.ref("user", "hobbies", 0)
bikingHobbyRef.remove()
// logs ['spelunking', 'weaving']
console.log("hobbies are ", handle.doc().user.hobbies)
```
### Response
N/A (SDK Example)
```
--------------------------------
### Initialize Node.js File System Storage Adapter
Source: https://automerge.org/docs/reference/repositories/storage
Instantiate the NodeFSStorageAdapter to store Automerge documents in a directory on the local file system. This adapter is safe for concurrent use by multiple processes.
```javascript
import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs";
const storage = new NodeFSStorageAdapter();
```
--------------------------------
### Connect to Sync Server via WebSocket
Source: https://automerge.org/docs/tutorial/network-sync
Add WebSocketClientAdapter to your Repo's network subsystem during creation to enable syncing with a remote server. This allows other processes to connect to the server and retrieve your changes.
```typescript
//...import { WebSocketClientAdapter } from "@automerge/react";
const repo = new Repo({
network: [
new BroadcastChannelNetworkAdapter(),
new WebSocketClientAdapter("wss://sync.automerge.org"),
],
storage: new IndexedDBStorageAdapter(),
});
```
--------------------------------
### Create a new Automerge document
Source: https://automerge.org/docs/tutorial/local-sync
Use `repo.create` to make a new document, save it to storage, and announce it to the network. This returns a `DocHandle` for accessing the document and its URL.
```javascript
const handle = window.repo.create({foo: "bar"})
console.log(handle.url)
```
--------------------------------
### Configure Repo with IndexedDBStorageAdapter
Source: https://automerge.org/docs/reference/under-the-hood/storage
Modify the Repo initialization to use IndexedDBStorageAdapter for persistence. This configuration disables live network synchronization but allows for data to be saved and reloaded across browser refreshes or tab closures.
```typescript
const repo = new Repo({
network: [], // This part means that we're not sending live changes anywhere
storage: new IndexedDBStorageAdapter(),
});
```
--------------------------------
### Initialize WebSocketServerAdapter with ws
Source: https://automerge.org/docs/reference/repositories/networking
Instantiate the WebSocketServerAdapter using the 'ws' library. This adapter is used on the server side for WebSocket communication.
```javascript
import { WebSocketServer } from "ws";
import { WebSocketServerAdapter } from "@automerge/automerge-repo-network-websocket";
const wss = new WebSocketServer({ port: 8080 });
const adapter = new WebSocketServerAdapter(wss);
```
--------------------------------
### Initialize Document Structure with Automerge.change()
Source: https://automerge.org/docs/cookbook/modeling-data
Use Automerge.change() to set up the initial schema for a new document. This ensures a consistent structure across devices before users begin making changes.
```javascript
let doc = Automerge.init()
doc = Automerge.change(doc, doc => {
doc.users = []
doc.channels = {}
doc.messages = []
})
```
--------------------------------
### Initialize Root Document in main.tsx
Source: https://automerge.org/docs/tutorial/multiple-task-lists
Sets up the Automerge repository and initializes a root document. It checks the URL for an existing task list or creates a new one, storing its URL in the root document. The root document handle is made available globally for debugging.
```typescript
import { RootDocument } from "./rootDoc.ts"
// ..// Add the repo to the global window object so it can be accessed in the browser console// This is useful for debugging and testing purposes.
declare global {
interface Window {
repo: Repo;
// We also add the handle to the global window object for debugging
handle: DocHandle;
}
}
window.repo = repo;
// Check the URL for a document to load
const locationHash = document.location.hash.substring(1);
// Depending if we have an AutomergeUrl, either find or create the document
if (isValidAutomergeUrl(locationHash)) {
const taskList = await repo.find(locationHash);
window.handle = repo.create({ taskLists: [taskList.url] });
} else {
const taskList = repo.create(initTaskList());
window.handle = repo.create({ taskLists: [taskList.url] });
// Set the location hash to the new document we just made.
document.location.hash = taskList.url;
}
ReactDOM.createRoot(document.getElementById("root")!).render(
Loading a document...}>
,
);
```
--------------------------------
### Unbundled Vanilla JS Initialization
Source: https://automerge.org/docs/reference/library-initialization
For use in unbundled vanilla JavaScript, manually initialize the WASM module by fetching the `.wasm` file and then setting up the Automerge Repo. This approach may require wrapping in an async function if top-level await is not supported.
```javascript
import * as AutomergeRepo from "https://esm.sh/@automerge/[email protected]/slim?bundle-deps";
await AutomergeRepo.initializeWasm(
fetch("https://esm.sh/@automerge/automerge/dist/automerge.wasm")
);
// Then set up an automerge repo (loading with our annoying WASM hack)
const repo = new AutomergeRepo.Repo({
storage: new AutomergeRepo.IndexedDBStorageAdapter(),
network: [
new AutomergeRepo.WebSocketClientAdapter("wss://sync.automerge.org"),
],
});
```
--------------------------------
### Initialize Automerge Repo with Text Document
Source: https://automerge.org/docs/cookbook/rich-text-prosemirror-react
Modify the handle initialization logic in `main.tsx` to create a document with a 'text' field instead of a 'Counter'. This ensures the document is ready to store rich text.
```typescript
import { AutomergeUrl } from "@automerge/automerge-repo"
import { useHandle } from "@automerge/automerge-repo-react-hooks"
import { useEffect, useState } from "react"
function App({ docUrl }: { docUrl: AutomergeUrl }) {
const handle = useHandle<{text: string}>(docUrl)
const [loaded, setLoaded] = useState(handle && handle.docSync() != null)
useEffect(() => {
if (handle != null) {
handle.whenReady().then(() => {
if (handle.docSync() != null) {
setLoaded(true)
}
})
}
}, [handle])
return
}
export default App
```
--------------------------------
### Handle Document URL and Initialize Handle
Source: https://automerge.org/docs/cookbook/rich-text-prosemirror-vanilla
Retrieves the Automerge document URL from the browser's hash. If a valid URL exists, it finds the document; otherwise, it creates a new document and updates the URL hash.
```javascript
// Get the document ID from the URL fragment if it's there. Otherwise, create// a new document and update the URL fragment to match.
const docUrl = window.location.hash.slice(1)
if (docUrl && isValidAutomergeUrl(docUrl)) {
handle = await repo.find(docUrl)
} else {
handle = repo.create({ text: "" })
window.location.hash = handle.url
}
```
--------------------------------
### Importing Automerge (Recommended)
Source: https://automerge.org/docs/guides/migrating-from-automerge-2-to-automerge-3
Change your imports to use the main Automerge module. This is recommended as the `next` module is deprecated.
```javascript
import * as Automerge from "@automerge/automerge"
```
--------------------------------
### wasmInitialized
Source: https://automerge.org/automerge/api-docs/js
Returns true if the WebAssembly module has been initialized.
```APIDOC
## wasmInitialized
### Description
Returns true if the WebAssembly module has been initialized.
### Function Signature
`wasmInitialized(): boolean`
```
--------------------------------
### Initialize IndexedDB Storage Adapter
Source: https://automerge.org/docs/reference/repositories/storage
Instantiate the IndexedDBStorageAdapter to store Automerge documents in the browser's IndexedDB. This adapter is safe for concurrent use across multiple tabs.
```javascript
import { IndexedDBStorageAdapter } from "@automerge/automerge-repo-storage-indexeddb"
const storage = new IndexedDBStorageAdapter()
```
--------------------------------
### load
Source: https://automerge.org/automerge/api-docs/js
Loads an Automerge document from a Uint8Array.
```APIDOC
## load
### Description
Loads an Automerge document from a Uint8Array.
### Function Signature
`load(data: Uint8Array): Doc`
```
--------------------------------
### Update Text to string in Automerge
Source: https://automerge.org/docs/guides/migrating-from-automerge-2-to-automerge-3
Shows how to migrate from using Automerge.Text to plain strings. This involves changing the initialization and update methods.
```javascript
import * as Automerge from "@automerge/automerge"
let doc = Automerge.from({
content: new Automerge.Text()
})
doc = Automerge.change(doc, d => {
d.content.insertAt(0, "Hello ")
})
```
```javascript
import * as Automerge from "@automerge/automerge"
let doc = Automerge.from({
content: ""
})
doc = Automerge.change(doc, d => {
Automerge.splice(d, ["content"], 0, 0, "Hello")
})
```
--------------------------------
### Find an Automerge document by URL
Source: https://automerge.org/docs/tutorial/local-sync
Use `repo.find` to retrieve a document using its URL. It checks local storage and connected peers. This is useful for loading existing documents.
```javascript
const handle = await window.repo.find("")
console.log(handle.doc())
```
--------------------------------
### isWasmInitialized
Source: https://automerge.org/automerge/api-docs/js
Checks if the WebAssembly module has been initialized.
```APIDOC
## isWasmInitialized
### Description
Checks if the WebAssembly module has been initialized.
### Function Signature
`isWasmInitialized(): boolean`
```
--------------------------------
### Webpack Configuration
Source: https://automerge.org/docs/reference/library-initialization
For Webpack projects, enable the `asyncWebAssembly` experiment in your `webpack.config.js` to properly load the WebAssembly module.
```javascript
{
experiments: {
asyncWebAssembly: true;
}
}
```
--------------------------------
### Importing Automerge (Deprecated)
Source: https://automerge.org/docs/guides/migrating-from-automerge-2-to-automerge-3
If you are using the `next` API, it is recommended to switch to the main Automerge import. The `next` module still works but is deprecated.
```javascript
import { next as Automerge } from "@automerge/automerge"
```
--------------------------------
### Debugging Root Document in Browser Console
Source: https://automerge.org/docs/tutorial/persist-root-doc
Demonstrates how to inspect the root document stored in local storage using the browser's developer console. This helps verify that the root document is correctly persisted.
```javascript
const rootDocUrl = localStorage.getItem("root-doc-url")
const root = await window.repo.find(rootDocUrl);
console.log("Root document:", root.doc());
```
--------------------------------
### Load or Create Automerge Document Handle
Source: https://automerge.org/docs/tutorial/load-by-url
Add this code to your main application file to load a document by URL or create a new one. It initializes the Automerge repository and sets up global access to the repository and document handle for debugging.
```typescript
import React, { Suspense } from "react";
import ReactDOM from "react-dom/client";
import App from "./components/App.tsx";
import "./index.css";
import { initTaskList, TaskList } from "./components/TaskList.tsx";
import {
Repo,
BroadcastChannelNetworkAdapter,
IndexedDBStorageAdapter,
isValidAutomergeUrl,
DocHandle,
} from "@automerge/react";
const repo = new Repo({
network: [new BroadcastChannelNetworkAdapter()],
storage: new IndexedDBStorageAdapter(),
});
// Add the repo to the global window object so it can be accessed in the browser console
// This is useful for debugging and testing purposes.
declare global {
interface Window {
repo: Repo;
// We also add the handle to the global window object for debugging
handle: DocHandle;
}
}
window.repo = repo;
// Check the URL for a document to load
const locationHash = document.location.hash.substring(1);
// Depending if we have an AutomergeUrl, either find or create the document
if (isValidAutomergeUrl(locationHash)) {
window.handle = await repo.find(locationHash);
} else {
window.handle = repo.create(initTaskList());
// Set the location hash to the new document we just made.
document.location.hash = window.handle.url;
}
ReactDOM.createRoot(document.getElementById("root")!).render(
Loading a document...}>
,
);
```
--------------------------------
### Integrating SyncControls into App.tsx
Source: https://automerge.org/docs/tutorial/multi-device-root-doc
Demonstrates how to integrate the SyncControls component into the main App component to provide multi-device synchronization capabilities. Ensure necessary imports are present.
```typescript
// ..import { useHash } from "react-use";import { SyncControls } from "./SyncControls";
// ..
```
--------------------------------
### Initialize Automerge WASM with Base64 in Deno
Source: https://automerge.org/docs/reference/library-initialization
Manually initialize the Automerge WebAssembly module in Deno using a base64 encoded string when filesystem access is restricted. Ensure the `automergeWasmBase64` is imported.
```typescript
import { automergeWasmBase64 } from "npm:@automerge/automerge";import * as Am from "npm:@automerge/automerge";await Am.initializeBase64Wasm(automergeWasmBase64);
```
--------------------------------
### Using Refs with Cursors for Strings
Source: https://automerge.org/docs/reference/repositories/refs
Demonstrates how to use Refs with cursors to reference and modify specific substrings within a text document.
```APIDOC
## Using Refs with Cursors for Strings
### Description
This example shows how to use Automerge Refs in conjunction with cursors to pinpoint and manipulate specific ranges within a string value in the document.
### Method
Implicitly through document handle methods and the `change` method of a Ref object.
### Endpoint
N/A (SDK method)
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Request Example
```javascript
// Assuming 'cursor' is imported or available
let handle = repo.create({
message: "Hello world"
});
const rangeRef = handle.ref("message", cursor(0, 5)); // References 'Hello'
// Logs 'Hello'
console.log(rangeRef.value());
rangeRef.change(() => "Hi"); // Replaces the referenced text with 'Hi'
```
### Response
#### Success Response (200)
N/A (SDK method modifies the document in place)
#### Response Example
```javascript
// The text is replaced at the range; logs "Hi world"
console.log(handle.doc().message);
```
```
--------------------------------
### Integrate Automerge with ProseMirror Editor
Source: https://automerge.org/docs/cookbook/rich-text-prosemirror-vanilla
Initializes the ProseMirror editor state and plugins using the Automerge document handle. This sets up the schema, document mirror, and the Automerge plugin for the editor.
```javascript
// This is the integration with automerge.
const { schema, doc, plugin } = init(handle, ["text"])
const editorConfig = {
schema,
plugins: [plugin],
}// This is the prosemirror editor.
const view = new EditorView(document.querySelector("#editor"), {
state: EditorState.create({
doc, // Note that we initialize using the mirror
plugins: exampleSetup({ schema, plugins: [plugin] }),
}),
})
```
--------------------------------
### releaseInfo
Source: https://automerge.org/automerge/api-docs/js
Retrieves information about the Automerge release.
```APIDOC
## releaseInfo
### Description
Retrieves information about the Automerge release.
### Function Signature
`releaseInfo(): JsReleaseInfo`
```
--------------------------------
### HTML Structure for ProseMirror + Automerge App
Source: https://automerge.org/docs/cookbook/rich-text-prosemirror-vanilla
Basic HTML file structure required for the ProseMirror and Automerge integration. Includes a div for the editor and a script tag for the main JavaScript file.
```html
Prosemirror + Automerge
```
--------------------------------
### Remove Properties via Ref
Source: https://automerge.org/docs/reference/repositories/refs
Demonstrates how Refs can be used to remove properties, substrings, or array elements they point to.
```javascript
const firstTodoRef = handle.ref("todos", 0)
firstTodoRef.remove()
// The first todo object is removed from the list
```
--------------------------------
### Pattern Matching with Refs
Source: https://automerge.org/docs/reference/repositories/refs
Shows how to use pattern matching within refs to select elements from an array based on their properties, and then remove the matched element.
```APIDOC
## Pattern Matching with Refs
### Description
This example demonstrates using pattern matching with the `ref` function to select an object within an array based on a specific property value (e.g., `id: "b"`). The selected object can then be manipulated, such as being removed from the document.
### Method
N/A (SDK Example)
### Endpoint
N/A (SDK Example)
### Parameters
N/A (SDK Example)
### Request Example
```javascript
handle = repo.create({
users: [
{ id: "a", name: "Alice" },
{ id: "b", name: "Bob" },
{ id: "c", name: "Charlie" },
]
})
const bobRef = handle.ref("users", { id: "b" })
bobRef.remove()
// Logs the objects for Alice and Charlie
console.log(handle.doc().users)
```
### Response
N/A (SDK Example)
```
--------------------------------
### Listen for changes on an Automerge document
Source: https://automerge.org/docs/tutorial/local-sync
Attach a 'change' event listener to a `DocHandle` to log updates to the document. This allows observing real-time modifications.
```javascript
handle.on("change", evt => console.log(evt.doc))
```
--------------------------------
### save
Source: https://automerge.org/automerge/api-docs/js
Saves the entire Automerge document to a Uint8Array.
```APIDOC
## save
### Description
Saves the entire Automerge document to a Uint8Array.
### Function Signature
`save(doc: Doc): Uint8Array`
```
--------------------------------
### Create and Modify Automerge Document with Various Data Types
Source: https://automerge.org/docs/reference/documents
Demonstrates creating an Automerge document with different data types like maps, lists, text, strings, numbers, booleans, bytes, dates, counters, and null. It also shows how to modify these values and nested structures using the `change` function and specific Automerge methods like `splice` and `increment`.
```javascript
import * as A from "@automerge/automerge";let doc = A.from({
map: {
key: "value",
nested_map: { key: "value" },
nested_list: [1],
},
list: ["a", "b", "c", { nested: "map" }, ["nested list"]],
text: "world",
raw_string: new A.ImmutableString("immutablestring"),
integer: 1,
float: 2.3,
boolean: true,
bytes: new Uint8Array([1, 2, 3]),
date: new Date(),
counter: new A.Counter(1),
none: null,
});
doc = A.change(doc, (d) => {
// Insert 'Hello' at the beginning of the string
A.splice(d, ["text"], 0, 0, "Hello ");
d.counter.increment(20);
d.map.key = "new value";
d.map.nested_map.key = "new nested value";
d.list[0] = "A";
d.list.insertAt(0, "Z");
d.list[4].nested = "MAP";
d.list[5][0] = "NESTED LIST";
});
console.log(doc);
```
--------------------------------
### loadIncremental
Source: https://automerge.org/automerge/api-docs/js
Loads changes incrementally into an existing Automerge document.
```APIDOC
## loadIncremental
### Description
Loads changes incrementally into an existing Automerge document.
### Function Signature
`loadIncremental(doc: Doc, data: Uint8Array): Doc`
```
--------------------------------
### readBundle
Source: https://automerge.org/automerge/api-docs/js
Reads a bundle of changes from a Uint8Array.
```APIDOC
## readBundle
### Description
Reads a bundle of changes from a Uint8Array.
### Function Signature
`readBundle(data: Uint8Array): DecodedChange[]`
```
--------------------------------
### Create DocumentList Component
Source: https://automerge.org/docs/tutorial/multiple-task-lists
A React component that displays a list of task lists. It takes the URL of the root document, fetches it using `useDocument`, and then maps over the `taskLists` array to render each task list's title.
```typescript
import React from "react";
import { useDocument, AutomergeUrl } from "@automerge/react";
import { TaskList } from "./TaskList";
export interface DocumentList {
taskLists: AutomergeUrl[];
}
export const DocumentList: React.FC<{
docUrl: AutomergeUrl;
}> = ({ docUrl }) => {
const [doc, changeDoc] = useDocument(docUrl, {
suspense: true,
});
return (
{doc.taskLists.map((docUrl) => (
))}
);
};
// Component to display document title
const DocumentTitle: React.FC<{ docUrl: AutomergeUrl }> = ({ docUrl }) => {
const [doc] = useDocument(docUrl, { suspense: true });
// Get the first task's title or use a default
const title = doc.title || "Untitled Task List";
return
{title}
;
};
```
--------------------------------
### Initialize Automerge WASM with URL in Vite
Source: https://automerge.org/docs/reference/library-initialization
Initialize the Automerge WebAssembly module in a Vite application using a URL obtained via the `?url` suffix. This method is suitable for environments where direct WASM module imports are not supported.
```javascript
// Note the ?url suffix
import wasmUrl from "@automerge/automerge/automerge.wasm?url";
// Note the `/slim` suffixes
import * as Automerge from "@automerge/automerge/slim";
import { Repo } from "@automerge/automerge-repo/slim";
await Automerge.initializeWasm(wasmUrl)
// Now we can get on with our lives
const repo = new Repo({..})
```
--------------------------------
### App Component for Task List Management
Source: https://automerge.org/docs/tutorial/multiple-task-lists
The main App component orchestrates the display of document lists and task lists. It uses react-use's `useHash` to manage the selected document's URL in the browser's hash, ensuring that the correct task list is displayed.
```typescript
import { type AutomergeUrl, isValidAutomergeUrl } from "@automerge/react";
import { useHash } from "react-use";
function App({ docUrl }: { docUrl: AutomergeUrl }) {
const [hash, setHash] = useHash();
const cleanHash = hash.slice(1); // Remove the leading '#'
const selectedDocUrl =
cleanHash && isValidAutomergeUrl(cleanHash)
? (cleanHash as AutomergeUrl)
: null;
return (
<>
>
);
}
export default App;
```
--------------------------------
### Apply Hard-coded Migration to Upgrade Document
Source: https://automerge.org/docs/cookbook/modeling-data
Safely upgrade a document from one schema version to another by applying a hard-coded migration change. This ensures compatibility when multiple devices might perform the same migration independently.
```javascript
type DocV1 = {
version: 1,
cards: Card[]
}
type DocV2 = {
version: 2,
title: Automerge.Text,
cards: Card[]
}
// This change creates the `title` property required in V2,
// and updates the `version` property from 1 to 2
const migrateV1toV2 = new Uint8Array([133, 111, 74, 131, ...])
let doc = getDocumentFromNetwork()
if (doc.version === 1) {
[doc] = Automerge.applyChange(doc, [migrateV1toV2])
}
```
--------------------------------
### Node.js Import
Source: https://automerge.org/docs/reference/library-initialization
In Node.js, Automerge can be imported directly using standard ES module syntax.
```javascript
import * as A from "@automerge/automerge"
```