[{"name":"Crdt","comment":" **Describe your collaborative document, and edit it type-safely.** By document we generally\nmean the part of your application model you want to sync/collaborate on.\n\n**Describe its shape as a schema.** A `Schema` value says both what Elm type a\npiece of data reads as and how concurrent edits to it merge. Think of it as the collaborative\nanalogue of a JSON decoder.\n\n**Edit through refs.** The record and custom builders hand you back typed `Ref`s\nalongside the schema — one per field or payload. A `Ref` is a pointer to one editable\nspot, and you edit by passing it to a function like `set` or `increment`.\n\n_Why not just edit the plain record and hand it back?_ Because you can't recover an\nedit's _intent_ from before/after values, and a CRDT needs that intent to merge. If a\nlist goes from `[ a, b ]` to `[ a, c, b ]`, did you insert `c`, or replace `b` with `c`\nand append `b`? Concurrent replicas resolve those two stories differently, so a value\ndiff would guess and you might end up with odd results. If a counter reads `5`\nboth before and after, was there no edit, or a `+3` and a `-3` that a teammate's\nconcurrent `+1` must still be added to? Only the _operation_ (`insert c after a`,\n`increment by 3`) carries what merge needs. A `Ref` names the spot and the edit function\nnames the operation, so the library records intent directly instead of reverse-engineering\nit, which is also why there is no `a -> Doc a` \"save\" function.\n\nThreading edits through refs also makes them safe: each ref carries the kind of data\nit points at, so the compiler rejects a nonsensical edit, such as an `increment` on text,\nand a field name, written once in the schema, can never be mistyped.\n\n    import Crdt\n    import Crdt.Edit as Edit\n    import Crdt.Id\n\n    type alias Board =\n        { title : String, votes : Int }\n\n    -- one flat bundle: a Ref per field plus a reserved `schema`\n    type alias BoardDoc =\n        { title : Crdt.Ref Board Crdt.Settable String\n        , votes : Crdt.Ref Board Crdt.Counter Int\n        , schema : Crdt.Schema Crdt.Nested Board\n        }\n\n    board : BoardDoc\n    board =\n        Crdt.record Board BoardDoc\n            |> Crdt.field \"title\" .title Crdt.text\n            |> Crdt.field \"votes\" .votes Crdt.counter\n            |> Crdt.build\n\n    -- `board.schema` creates the document\n    -- `board.title` / `board.votes` are your edit handles (each edit returns a\n    -- `Result`, since a ref can fail to resolve against the current value):\n    doc =\n        Crdt.init (Crdt.Id.replica \"alice\") board.schema\n\n    doc1 =\n        Edit.set board.title \"Trip plan\" doc\n            |> Result.withDefault doc\n\n    doc2 =\n        Edit.increment board.votes 1 doc1\n            |> Result.withDefault doc1\n\n    -- Edit.increment board.title  =>  a compile error\n\n\n# Schema\n\nA difference between this schema and a typical codec library is that you have\nto decide the correct merge semantics for your data types. The basic `int`, `string`,\n`bool` are _last-write-wins (LWW) registers_, meaning that if there are concurrent edits,\nthe last edit wins (and the previous one is overwritten, although it is still accessible\nthrough history). A `counter` on the other hand _sums_ concurrent modifications.\n\nFor example if you are modelling a position in a graphic editor, a LWW register is probably\nwhat you want: if I move a box to x=40px, and you move it to x=20px at the same time,\neither 40px or 20px are reasonable outcomes, but 60px is not. However, if you have a shared\nshopping cart and I want 2 tacos and you want 3 tacos, we do want to end up with 5 tacos\nin total.\n\nThe same applies for `string` vs `text` - both are represented as a simple string, but\n`string` is a LWW register, vs `text` merges edits. `string` is useful for things like\nIDs or URLs, where having both just breaks, `text` is much better for human readable text.\n\nEvery builder below hands back a **bundle** — a `Crdt`. It always carries the `.schema`\nitself, plus whatever handles that kind of data offers: a container's element composer, or\n(for a record / custom type) one `Ref` per field. That's the one type you'll see throughout:\n\n@docs Crdt, Schema\n\n\n## Simple values\n\n@docs int, float, string, bool, counter, register\n\n\n## Collaborative text\n\n@docs text, richText\n\n\n## Collections\n\n@docs list, movableList, dict, tree\n\n\n## Defining your own CRDT\n\n@docs opSet\n\n\n## Building a record\n\n@docs record, field, build\n\n\n## Pairing two schemas\n\n@docs tuple\n\n\n## Building a custom type\n\n@docs custom, variant0, variant1, variant2, variant3, buildCustom\n\n\n## Giving a field your own type\n\n@docs map\n\n\n## Evolving a schema over time\n\nOnce you have a schema, releases will change it.\n\nIf you are writing an application with a centralized server, incrementing a protocol version\nfield and forcing clients to reload may well be the easiest option.\n\nHowever, if you want true offline capability or decentralization, documents\nwritten by past versions are still out there, and peers on old and new schemas\nmay edit the same document at once. So a schema change shouldn't be a one-shot migration: a newer\nschema should read older documents, an older schema must survive newer ones, and both must\nconverge. These combinators make a read _tolerant_ of a shape it didn't expect, rather\nthan failing.\n\nThe safe, non-breaking changes and the tool for each:\n\n  - **Add a field** — wrap it in `optional` (reads `Nothing` when missing) or\n    `withDefault` (reads a default when missing), so old documents that lack it still\n    read. A plain required field would error on them.\n  - **Rename a field** — `aliasedField` reads the old name too, so peers on either name\n    share one document.\n  - **Add a variant to a custom type** — declare a `catchAll` so an older peer reads a\n    newer variant's tag as a fallback (preserving it) instead of erroring.\n  - **Change a value's representation** (rename an enum, `Int` ↔ `String`) — `map` between\n    the old stored form and the new one.\n\nRemoving a field or variant, or changing a field's merge semantics (e.g. `counter` →\nregister), are _not_ safe to do in place. Treat those as a new field alongside the old one.\n\n@docs optional, withDefault\n@docs aliasedField, catchAll\n\n\n# Creating a document\n\n`init` turns a schema into a live document for one replica. Reading the value back out\n(`Crdt.Doc.read` / `readAt`) and everything else you do with a document — syncing, history,\nundo — lives in `Crdt.Doc`; changing it lives in `Crdt.Edit`.\n\n@docs init\n\n\n# Pointing into a document\n\nA `Ref` names a spot inside a document — the title field, a list element, a tree node's\npayload. The builders hand you a flat bundle: your field refs, a reserved `.schema`, and —\nfor containers — a composer (`.index` / `.key` / `.node`) that points at an element with\nits schema already captured. You compose deeper with `at`, then pass the ref to a\n`Crdt.Edit` function. A ref carries a phantom `kind` marker, so the compiler rejects a\nnonsensical edit (`increment` on text, `move` on a plain list) before it runs.\n\n    board.todos |> todoList.index 0 |> Crdt.at todo.done\n\n@docs Ref\n\n@docs at\n\n\n## Edit-capability markers\n\nEach schema is tagged with a marker describing how its value may be **edited** (a counter\ncan be `increment`ed, text typed into, and so on). You rarely write these yourself — they\nappear in the `Ref` types the builders infer — but they are what lets the compiler reject\na nonsensical edit. They never affect how data reads or merges.\n\n@docs Settable, Counter, Nested, Variants, ListK, Fixed, Movable, DictK, TreeK, RichK, OpSetK\n@docs VariantSeed\n\n","unions":[],"aliases":[{"name":"Counter","comment":" Kind marker: a PN-counter; supports `increment`.\n","args":[],"type":"Crdt.Schema.Internal.Counter"},{"name":"Crdt","comment":" Everything a builder hands back: a **bundle**. It always carries `.schema` (the piece of\ncollaborative data itself), plus whatever extra handles that kind of data offers —\ncollected in the `setters` row:\n\n  - a **primitive** has no extras: `Crdt Settable Int {}` — just `.schema`.\n  - a **container** adds a composer that points at an element with the element schema\n    already captured: a list's `{ index : … }`, a dict's `{ key : … }`, a tree's\n    `{ node : … }`.\n  - a **record** or **custom type** you build adds one `Ref` per field or payload, so its\n    bundle is your own flat alias (`{ title = …, votes = …, schema = … }`).\n\nBecause the type is an extensible record, anything that just needs a schema accepts _any_\nbundle: `field`, the containers, `optional`/`withDefault`/`map` all take `Crdt kind a s`\nand ignore the extras. That's why you never pass a schema separately.\n\n","args":["kind","a","setters"],"type":"{ setters | schema : Crdt.Schema kind a }"},{"name":"DictK","comment":" Kind marker: a `dict` of `a` with value kind `vk`.\n","args":["vk","a"],"type":"Crdt.Schema.Internal.DictK vk a"},{"name":"Fixed","comment":" Movability marker for a plain `list` (no reordering).\n","args":[],"type":"Crdt.Schema.Internal.Fixed"},{"name":"ListK","comment":" Kind marker: a list of `a` with element kind `ek` and movability `mv`.\n","args":["mv","ek","a"],"type":"Crdt.Schema.Internal.ListK mv ek a"},{"name":"Movable","comment":" Movability marker for a `movableList` (supports `move`).\n","args":[],"type":"Crdt.Schema.Internal.Movable"},{"name":"Nested","comment":" Kind marker: a record or map; edited by descending into fields/keys.\n","args":[],"type":"Crdt.Schema.Internal.Nested"},{"name":"OpSetK","comment":" Kind marker: a user-defined **op-set** CRDT whose contributions have kind `ck` and type\n`c` (see [`opSet`](#opSet)); edited only via `contribute`/`retract`. Carrying the\ncontribution's kind is what makes `contribute` reject a differently-merging schema of the\nsame type (a `counter` where an `int` register was declared).\n","args":["ck","c"],"type":"Crdt.Schema.Internal.OpSetK ck c"},{"name":"Ref","comment":" A typed pointer to one editable spot inside a document — a field, a list element, a\ndictionary value, a variant's payload. You get refs from the `record` and `custom`\nbuilders (as `board.title`) and pass them to the edit functions (`set`, `increment`,\n…) to say _where_ to edit.\n\nThe three type parameters are what make edits safe, and you rarely write them by hand:\n\n  - `r` is the type at the **root** the ref was built from (e.g. your `Board` record), so\n    refs from different documents can't be mixed up;\n  - `kind` is **how the spot may be edited** — `Settable`, `Counter`, `RichK`, and so on\n    — which is what lets the compiler accept `increment` only on a `Counter` ref;\n  - `a` is the value the spot **reads as**.\n\nOpaque: build refs with the builders, compose them with `at` (and a container bundle's\n`.index`/`.key`/`.node`), and edit through them.\n\n","args":["r","kind","a"],"type":"Crdt.Ref.Internal.Ref r kind a"},{"name":"RichK","comment":" Kind marker: rich (formatted) text; supports text edits + `mark`/`unmark`.\n","args":[],"type":"Crdt.Schema.Internal.RichK"},{"name":"Schema","comment":" A description of a piece of collaborative data — what Elm type it reads as, and how\nconcurrent edits to it merge. Compose small `Schema` values (leaves and containers) into\nthe shape of your whole document. The `kind`\nphantom tag records how the value may be edited (see [Edit-capability markers](#Settable)); `a` is\nthe Elm type it reads as.\n","args":["kind","a"],"type":"Crdt.Schema.Internal.Crdt kind a"},{"name":"Settable","comment":" Kind marker: an LWW register leaf; supports `set`.\n","args":[],"type":"Crdt.Schema.Internal.Settable"},{"name":"TreeK","comment":" Kind marker: a movable `tree` of `a` with node kind `ek`.\n","args":["ek","a"],"type":"Crdt.Schema.Internal.TreeK ek a"},{"name":"VariantSeed","comment":" The opaque \"make a fresh variant payload\" value the choice-type builders\n(`variant0`/`variant1`/… and `catchAll`) produce and thread for you. You never build one\nby hand; it only shows up in the inferred types of those builders.\n","args":[],"type":"Crdt.Schema.Internal.VariantSeed"},{"name":"Variants","comment":" Kind marker: a sum type over `a`; supports variant switching + payload refs.\n","args":["a"],"type":"Crdt.Schema.Internal.Variants a"}],"values":[{"name":"aliasedField","comment":" Add a field that also reads from older names (`aliases`) when `name` is absent —\na schema-evolution rename. Reads prefer `name`, then each alias in order;\nwrites and the field's `Ref` always target `name`, so once written the old key becomes an\ninert extra key. Peers on the old and new schema converge on the same document and each\nreads it through its own names.\n\n    record Todo TodoRefs\n        |> aliasedField \"done\" [ \"complete\" ] .done Crdt.bool\n        |> ...\n\n","type":"String.String -> List.List String.String -> (full -> f) -> Crdt.Crdt kind f s -> Crdt.RecordRefsBuilder r full (f -> b) (Crdt.Ref r kind f -> rest) -> Crdt.RecordRefsBuilder r full b rest"},{"name":"at","comment":" Compose refs: descend from a spot of type `sub` into a spot inside it. Reads as\nnavigation in a pipe:\n\n    board.status |> at status.archivedReason\n\n","type":"Crdt.Ref sub innerKind a -> Crdt.Ref r outerKind sub -> Crdt.Ref r innerKind a"},{"name":"bool","comment":" A boolean LWW register.\n","type":"Crdt.Crdt Crdt.Settable Basics.Bool {}"},{"name":"build","comment":" Finish a `record` schema. Feeds the built schema as the **last** argument to the refs\nassembler, producing one **flat** bundle: your `Ref`s plus a reserved `.schema` field. So\n`build` returns `{ …fieldRefs, schema }` — there is no separate `.refs` wrapper.\n","type":"Crdt.RecordRefsBuilder r a a (Crdt.Schema Crdt.Nested a -> unified) -> unified"},{"name":"buildCustom","comment":" Finish a `custom` schema. Like `build`, feeds the built schema as the **last** argument\nto the refs assembler, producing one **flat** bundle: the payload `Ref`s plus a reserved\n`.schema` field.\n","type":"Crdt.CustomRefsBuilder value (value -> Crdt.VariantSeed) (Crdt.Schema (Crdt.Variants value) value -> unified) -> unified"},{"name":"catchAll","comment":" A catch-all variant that keeps the unknown tag so you can render it. When a\ncustom type's stored `$tag` names a variant this schema doesn't know — one a newer peer\nadded — it decodes to `ctor tag`, carrying the raw tag string, instead of failing the\nread.\n\nGive it the constructor of a variant that holds a `String`. Because it keeps the tag, your\n`view` can tell a genuine value from a from-the-future one and show, say, a greyed-out\n\"unsupported — please update\" placeholder rather than pretending it's something else:\n\n    type Status\n        = Active\n        | Done String\n        | Unsupported String -- carries the unknown tag\n\n    Crdt.custom\n        (\\active done unsupported v ->\n            case v of\n                Active ->\n                    active\n\n                Done note ->\n                    done note\n\n                Unsupported tag ->\n                    unsupported tag\n        )\n        Refs\n        |> Crdt.variant0 \"active\" Active\n        |> Crdt.variant1 \"done\" Done Crdt.text\n        |> Crdt.catchAll Unsupported\n        |> Crdt.buildCustom\n\nThe unknown value is _not corrupted_ by merely holding it: sync ships the original\noperations, so an old peer that reads, holds, and re-syncs it forwards the untouched\n`$tag` along, and a peer still on the new schema reads the real variant. It is only lost\nif the old peer `switch`es it to a variant it _does_ know — so don't normalize an\n`Unsupported` value back to a known one in your update logic.\n\nIt is a real variant of your type, so your `custom` matcher must handle it, but it carries\nno editable payload, so it threads no ref.\n\nIf you'd rather **not** surface the unknown value at all and just collapse it to a\nsensible existing one, skip the catch-all and wrap the schema in `withDefault` instead\n(`withDefault Active statusSchema`) — simpler, but the tag is then lost the moment the\nvalue is re-saved. And if your clients are versioned in lockstep (e.g. a server pins\neveryone to one build), an unknown tag is a _bug_, not evolution — use neither, and let\nthe read error surface it.\n\n","type":"(String.String -> value) -> Crdt.CustomRefsBuilder value ((String.String -> Crdt.VariantSeed) -> b) refs -> Crdt.CustomRefsBuilder value b refs"},{"name":"counter","comment":" A counter — an integer edited only with `increment` (add or subtract a delta), read\nas its running total.\n\nReach for it instead of `int` when the value is a running tally that several people\nchange at once: a vote count, a stock level, a like button. Concurrent increments\nadd up: if two people each `increment` by 1 at the same time, the total goes up by 2.\nAn `int` register is last-write-wins, so under the same race one of the two `+1`s would be\nsilently lost.\n\nImplemented as a [PN-counter](https://www.cs.utexas.edu/~rossbach/cs380p/papers/Counters.html#pn-counter---increment-and-decrement-counter).\n\n","type":"Crdt.Crdt Crdt.Counter Basics.Int {}"},{"name":"custom","comment":" Start building a schema for a **custom type** (a union / \"sum\" type — one of several\nvariants, some carrying data). Like `record`, it takes two things: a **matcher** that\nsays which variant a value is, and a refs constructor for the payload refs.\n\nThe matcher is given one handler per variant (in declaration order) plus the value, and\nreturns the matching handler — it is just a `case` turned inside out. Use\n`variant0`/`variant1`/`variant2`/`variant3` for a variant carrying that many arguments,\neach argument getting its own schema (and its own payload ref):\n\n    type Status\n        = Active\n        | Archived String\n        | Snooze String Time.Posix\n\n    -- flat bundle: a payload ref per data-carrying variant arg, then `schema` last\n    type alias StatusDoc =\n        { archivedReason : Crdt.Ref Status Crdt.Settable String\n        , snoozeReason : Crdt.Ref Status Crdt.Settable String\n        , snoozeUntil : Crdt.Ref Status Crdt.Settable Time.Posix\n        , schema : Crdt.Schema (Crdt.Variants Status) Status\n        }\n\n    status : StatusDoc\n    status =\n        Crdt.custom\n            (\\active archived snooze value ->\n                case value of\n                    Active ->\n                        active\n\n                    Archived reason ->\n                        archived reason\n\n                    Snooze reason until ->\n                        snooze reason until\n            )\n            StatusDoc\n            |> Crdt.variant0 \"active\" Active\n            |> Crdt.variant1 \"archived\" Archived Crdt.text\n            |> Crdt.variant2 \"snooze\" Snooze Crdt.text time\n            |> Crdt.buildCustom\n\n    time : Crdt.Crdt Crdt.Settable Time.Posix {}\n    time =\n        Crdt.map Time.millisToPosix\n            Time.posixToMillis\n            Crdt.int\n\nEach `variant*` step contributes exactly its arguments' refs to the `StatusDoc`\nconstructor, in order — so `variant2 \"snooze\" Snooze …` supplies `snoozeReason` then\n`snoozeUntil`. (The `Snooze` payload also shows `map` in action: `time` stores an instant\nas a plain `int` but reads it as a `Time.Posix`.) Use `switch` to change which variant a\nvalue is, and the payload refs (via `at`) to edit inside the active variant. Which variant\nis active is last-write-wins.\n\n","type":"match -> refsAssembler -> Crdt.CustomRefsBuilder value match refsAssembler"},{"name":"dict","comment":" A dictionary from string keys to values of `a`, read as an Elm `Dict`. Set and remove\nkeys with `setKey` / `removeKey`; each value is described by a sub-schema and merges by its\nown rules, so a dict of `text` merges each entry character-wise.\n\nKey **presence** is last-write-wins by timestamp, which makes the set-vs-remove race\nwell-defined: concurrent `setKey` and `removeKey` on the same key resolve by stamp, so the\nlater of the two wins. Editing _inside_ a value is not a presence write, so it does not\nresurrect a key a peer removed concurrently — the removal stands, though the value it held\nis kept, so `setKey`ing the key again brings back whatever that value has since merged to\n(with the new value written over it, by the same rules as any other write).\n\n`removeKey` on a key this replica has never seen does nothing: there is no value to\ntombstone, and a delete races only against writes it has actually observed, so a concurrent\n`setKey` of an unseen key wins.\n\n","type":"Crdt.Crdt vk a s -> Crdt.Crdt (Crdt.DictK vk a) (Dict.Dict String.String a) { key : String.String -> Crdt.Ref r (Crdt.DictK vk a) (Dict.Dict String.String a) -> Crdt.Ref r vk a }"},{"name":"field","comment":" Add a field to a `record` schema: its name, a getter, and the field's own schema.\nThreads a typed `Ref` for the field into your refs record.\n","type":"String.String -> (full -> f) -> Crdt.Crdt kind f s -> Crdt.RecordRefsBuilder r full (f -> b) (Crdt.Ref r kind f -> rest) -> Crdt.RecordRefsBuilder r full b rest"},{"name":"float","comment":" A float LWW register.\n","type":"Crdt.Crdt Crdt.Settable Basics.Float {}"},{"name":"init","comment":" Create a fresh, empty document from a schema, owned by a replica. Give each\nparticipant (each browser tab, device, or server) a **distinct** `Crdt.Id.replica` so\ntheir edits never collide.\n\n    doc =\n        Crdt.init (Crdt.Id.replica \"alice-laptop\") board.schema\n\n","type":"Crdt.Id.ReplicaId -> Crdt.Schema kind a -> Crdt.Doc.Doc a"},{"name":"int","comment":" An integer LWW register.\n","type":"Crdt.Crdt Crdt.Settable Basics.Int {}"},{"name":"list","comment":" An ordered, growable list of `a` (each element is itself described by a sub-schema). Elements keep a stable identity,\nso a nested edit or a cursor into one survives other people's concurrent insertions and\ndeletions elsewhere in the list.\n\nOrdering (and concurrent-insert convergence) is [Fugue](https://arxiv.org/abs/2305.00583),\nthe same sequence CRDT `text` uses: two people appending at the same spot get both\nelements, in a deterministic order, with each contiguous run kept intact. Elements can't be\nreordered either, that's what `movableList` is for.\n\n","type":"Crdt.Crdt ek a s -> Crdt.Crdt (Crdt.ListK Crdt.Fixed ek a) (List.List a) { index : Basics.Int -> Crdt.Ref r (Crdt.ListK mv ek a) (List.List a) -> Crdt.Ref r ek a }"},{"name":"map","comment":" Transform the value a schema reads and seeds, in **both** directions (`to` on read,\n`from` on seed). It's the general tool for raising one of the built-in representations\ninto a domain type of your own (and lowering it back to store), so you can model a field\nas the type your app actually uses while it merges as a primitive underneath.\n\n    -- store an instant as milliseconds (a plain LWW `int`),\n    -- read it as a `Time.Posix`:\n    time : Crdt.Crdt Crdt.Settable Time.Posix {}\n    time =\n        Crdt.map Time.millisToPosix\n            Time.posixToMillis\n            Crdt.int\n\nThe merge semantics are the underlying schema's: mapping `int` gives a last-write-wins\ninstant; mapping `counter` would give something that still sums. Both directions are\nrequired so a seeded value round-trips (`from` then `to` must be identity).\n\n`map` also serves schema evolution re-spelling an enum, or moving a field from `Int`\nto `String` while the stored bytes stay put by mapping between the old representation and\nthe new domain type.\n\n","type":"(a -> b) -> (b -> a) -> Crdt.Crdt kind a s -> Crdt.Crdt kind b {}"},{"name":"movableList","comment":" Like `list`, but elements can also be reordered with `move` while keeping their\nidentity. A nested edit, or a cursor, anchored to an item follows it to its new position.\nUse it for a hand-orderable list (a Kanban column, a playlist, a to-do list you drag to\nrank); use plain `list` when order is fixed by insertion and you never move items\n(`movableList` consumes roughly twice the per item in-memory bookkeeping of `list`,\nso if you don't need it it is more efficient to just use `list`).\n\nMoves converge without the cycles a naive \"re-point the previous element\" scheme would\ncreate: an element's position is a stable value, and the latest move wins by\nlast-write-wins. Concurrent moves of the same element resolve to one deterministic\nposition.\n\n","type":"Crdt.Crdt ek a s -> Crdt.Crdt (Crdt.ListK Crdt.Movable ek a) (List.List a) { index : Basics.Int -> Crdt.Ref r (Crdt.ListK mv ek a) (List.List a) -> Crdt.Ref r ek a }"},{"name":"opSet","comment":" Define your own CRDT type when none of the built-ins fit. It's an\noperation-based set: each `contribute` writes one contribution (tagged with its own\nop-id), merge simply **unions** the contributions, and a read `fold`s the whole set into\nyour value. Because union is commutative, associative, and idempotent, convergence is free\n— you only supply the `fold`, which must be a pure, order-independent function of the set\n(it sees the contributions in no particular order, possibly with duplicates already\ndeduped).\n\nThis generalizes the built-in accretive CRDTs: `counter` is `opSet` with `fold = List.sum`;\na max-register, a grow-only set, or an add-wins tally are all a few lines. It cannot express\nthings that need coordinated _removal_ or a custom _sequence_ order — those aren't a plain\nunion-of-contributions.\n\n    -- a grow-only max register\n    maxRegister : Crdt.Crdt (Crdt.OpSetK Crdt.Settable Int) Int {}\n    maxRegister =\n        Crdt.opSet\n            { contribution = Crdt.int\n            , fold = List.maximum >> Maybe.withDefault 0\n            }\n\n","type":"{ contribution : Crdt.Crdt ck c s, fold : List.List c -> a } -> Crdt.Crdt (Crdt.OpSetK ck c) a {}"},{"name":"optional","comment":" Make a field's schema optional for schema evolution: it reads `Nothing` when the\nfield is absent (a document written before the field existed) instead of failing, and\n`Just v` when present. Seeding `Nothing` writes no value.\n\n    |> Crdt.field \"priority\" .priority (Crdt.optional prioritySchema)\n\n","type":"Crdt.Crdt kind a s -> Crdt.Crdt kind (Maybe.Maybe a) {}"},{"name":"record","comment":" Start building a record schema. Describe a record type field by field, and get back one\n**flat bundle**: a typed `Ref` per field plus a reserved `.schema`.\n\n`record` takes **two** constructors: your record's own constructor (which reassembles the\ndecoded value), and a second one for the bundle — a type alias whose fields are the `Ref`s\nin field order, followed by a `schema` field. Each `field` you pipe in feeds the field's\nvalue to the first and its ref to the second, and `build` supplies the finished schema as\nthe bundle's last field.\n\n\n    type alias Board =\n        { title : String, votes : Int }\n\n    -- one Ref per field, in the same order, then `schema` last:\n    type alias BoardDoc =\n        { title : Crdt.Ref Board Crdt.Settable String\n        , votes : Crdt.Ref Board Crdt.Counter Int\n        , schema : Crdt.Schema Crdt.Nested Board\n        }\n\n    board : BoardDoc\n    board =\n        Crdt.record Board BoardDoc\n            |> Crdt.field \"title\" .title Crdt.text\n            |> Crdt.field \"votes\" .votes Crdt.counter\n            |> Crdt.build\n\n    -- now: `board.schema` for `Crdt.init`,\n    -- `board.title` to edit the title.\n\nRecords nest: a field's schema can itself be another record's `.schema`, and you reach\ninto it with [`at`](#at).\n\n","type":"(a -> b) -> refsAssembler -> Crdt.RecordRefsBuilder r full (a -> b) refsAssembler"},{"name":"register","comment":" A last-write-wins register holding a value of any type, stored as JSON. Give a\n`default` (what a fresh document reads before anyone writes the field), a JSON encoder,\nand a decoder:\n\n    import Json.Decode as D\n    import Json.Encode as E\n\n    type Priority\n        = Low\n        | High\n\n    priority : Crdt.Crdt Crdt.Settable Priority {}\n    priority =\n        Crdt.register Low encodePriority priorityDecoder\n\nThe whole value is one cell: two people editing it concurrently don't merge field by\nfield. Use it for a field whose type isn't a\nbuilt-in primitive, or that you're happy to treat as one indivisible unit.\n\nThis can be used as a way to start slowly making an existing application collaborative.\nStart from the top of the document, and make a record there, with all values registers.\nThen you can keep doing this conversion until your whole app can do granular merges.\n\n","type":"a -> (a -> Json.Encode.Value) -> Json.Decode.Decoder a -> Crdt.Crdt Crdt.Settable a {}"},{"name":"richText","comment":" Collaborative rich (formatted) text: a character sequence (merged character-wise,\nlike `text`) plus a set of formatting **marks** such as bold, italic, headings, list\nitems layered over character ranges. Reads as a list of `Crdt.RichText.Span`s (runs of\ntext sharing the same formatting); edit the text with ordinary text edits and the\nformatting with `mark` / `unmark`.\n\nMarks are [Peritext](https://www.inkandswitch.com/peritext/) ranges: each mark is anchored\nto the character identities at its ends, not to numeric offsets, so a mark stays\nattached to the right span as text is inserted or deleted around it — and a bold applied\nconcurrently with an insertion at its boundary merges the way a human would expect.\n\n","type":"Crdt.Crdt Crdt.RichK (List.List Crdt.RichText.Span) {}"},{"name":"string","comment":" A string LWW register. For collaborative editing use `text`.\n","type":"Crdt.Crdt Crdt.Settable String.String {}"},{"name":"text","comment":" Collaborative plain text, read as a `String`. Every character is tracked\nindividually, so two people typing at once merge character by character rather than\none overwriting the other — the thing a `string` register (last-write-wins) can't do.\nYou edit it like any other field, with `set` to the new whole string; the library diffs\nold→new into the minimal insert/delete operations, so a `set` from `\"cat\"` to `\"cart\"`\nis understood as \"insert `r`\", not a wholesale replace.\n\nOrdering is [Fugue](https://arxiv.org/abs/2305.00583) (Weidner & Kleppmann, 2023),\nwhich guarantees maximal non-interleaving: when two people type a run of characters at\nthe same spot concurrently, each run stays a contiguous block after merging. You get\n`\"helloworld\"`, never `\"hwoelrllod\"`. (Fugue refines the classic\n[RGA](https://doi.org/10.1016/j.jpdc.2010.12.006) sequence CRDT, whose single anchor per\nelement permits that interleaving.)\n\n","type":"Crdt.Crdt Crdt.Settable String.String {}"},{"name":"tree","comment":" A movable tree of `a`: hierarchical, re-parentable, sibling-ordered data (an\noutline, a file tree, threaded comments). Reads as a `Crdt.Tree.Forest a`.\n\nRe-parenting is the hard part — two people could concurrently move A under B and B under A,\nwhich a naive merge would turn into a detached cycle. The tree uses Kleppmann et al.'s\n[replicated-tree move operation](https://martin.kleppmann.com/2021/10/07/crdt-tree-move-operation.html):\nmoves are applied in a deterministic global order and any move that would create a cycle is\nskipped identically on every replica, so all replicas derive the same tree. Sibling order\nis a [fractional index](https://www.figma.com/blog/realtime-editing-of-ordered-sequences/)\n(a position value, not an \"after node X\" pointer), which keeps concurrent reorders from\ncycling.\n\n","type":"Crdt.Crdt ek a s -> Crdt.Crdt (Crdt.TreeK ek a) (Crdt.Tree.Forest a) { node : Crdt.Id.OpId -> Crdt.Ref r (Crdt.TreeK ek a) (Crdt.Tree.Forest a) -> Crdt.Ref r ek a }"},{"name":"tuple","comment":" Combine two schema bundles into a pair `( a, b )` where each half keeps its own merge\nsemantics.\n\nReturns a flat bundle `{ first, second, schema }` — a `Ref` for each component and the\nschema to place in a document.\n\nPrefer a named `record` when the two components have meaningful names.\n\n","type":"Crdt.Crdt k1 a s1 -> Crdt.Crdt k2 b s2 -> { first : Crdt.Ref ( a, b ) k1 a, second : Crdt.Ref ( a, b ) k2 b, schema : Crdt.Schema Crdt.Nested ( a, b ) }"},{"name":"variant0","comment":" A nullary variant — no payload, so it contributes no ref.\n","type":"String.String -> value -> Crdt.CustomRefsBuilder value (Crdt.VariantSeed -> b) refs -> Crdt.CustomRefsBuilder value b refs"},{"name":"variant1","comment":" A one-argument variant. Threads a payload ref (rooted at the sum type) into the\nrefs assembler; `set`/`over` through it edit iff this variant is active.\n","type":"String.String -> (t1 -> value) -> Crdt.Crdt k1 t1 s1 -> Crdt.CustomRefsBuilder value ((t1 -> Crdt.VariantSeed) -> b) (Crdt.Ref value k1 t1 -> rest) -> Crdt.CustomRefsBuilder value b rest"},{"name":"variant2","comment":" A two-argument variant. Threads two payload refs.\n","type":"String.String -> (t1 -> t2 -> value) -> Crdt.Crdt k1 t1 s1 -> Crdt.Crdt k2 t2 s2 -> Crdt.CustomRefsBuilder value ((t1 -> t2 -> Crdt.VariantSeed) -> b) (Crdt.Ref value k1 t1 -> Crdt.Ref value k2 t2 -> rest) -> Crdt.CustomRefsBuilder value b rest"},{"name":"variant3","comment":" A three-argument variant. Threads three payload refs.\n","type":"String.String -> (t1 -> t2 -> t3 -> value) -> Crdt.Crdt k1 t1 s1 -> Crdt.Crdt k2 t2 s2 -> Crdt.Crdt k3 t3 s3 -> Crdt.CustomRefsBuilder value ((t1 -> t2 -> t3 -> Crdt.VariantSeed) -> b) (Crdt.Ref value k1 t1 -> Crdt.Ref value k2 t2 -> Crdt.Ref value k3 t3 -> rest) -> Crdt.CustomRefsBuilder value b rest"},{"name":"withDefault","comment":" Give a field's schema a default read when the field is absent: it reads `default`\ninstead of failing, without changing the value type. The real value is minted on first\nwrite.\n\n    |> Crdt.field \"priority\" .priority (Crdt.withDefault Medium prioritySchema)\n\nIt also absorbs the other \"the shape evolved\" case: if `default` wraps a custom type\nand the stored value is a variant this schema doesn't know (a newer peer added it), the\nread is `default` rather than an error. (`catchAll` is the alternative that keeps the unknown tag instead.\nNote that re-saving a collapsed value with `switch` loses the original tag, whereas simply\nholding and syncing it preserves it.) Genuinely malformed data still errors.\n\n","type":"a -> Crdt.Crdt kind a s -> Crdt.Crdt kind a {}"}],"binops":[]},{"name":"Crdt.Cursor","comment":" A stable position in a collaborative document — where someone's caret or selection\nsits, so you can show remote collaborators' cursors.\n\nA `Cursor` remembers a position by the **identity** of the characters around it, not by\na numeric offset. That is what makes it survive concurrent editing: if a collaborator's\ncaret is between characters 3 and 4 and someone else inserts text earlier in the line,\nthe caret still sits between the same two characters after the edits merge — it does not\ndrift. You create one with `cursorAt` (from a text ref and an offset) and turn it\nback into a current offset, against your own converged document, with `cursorOffset`.\n\nCursors are **presence data, not part of the document**. You broadcast them on the\n`Crdt.Presence` channel and every viewer resolves them locally; there is nothing to\nmerge. A typical setup carries each peer's caret as an optional presence field:\n\n    peerCodec =\n        Presence.codec Peer\n            |> Presence.optional \"caret\" .caret Presence.cursor\n            |> Presence.buildCodec\n\nA `Range` is a text selection: the pair of cursors marking its two ends — the **anchor**\nwhere the selection started and the **focus** where the caret now reaches (see\n[`Range`](#Range) for why the two ends aren't interchangeable). Each end resolves\nindependently, so a\nselection stretches and shifts correctly as the text changes underneath it.\n\n\n# Cursors\n\n@docs Cursor, cursorAt, cursorAtRich, cursorOffset\n@docs element, sameContainer\n\n\n# Selections\n\n@docs Range, range, rangeAnchor, rangeFocus\n\n\n# Sending cursors over the wire\n\n@docs encode, decoder, encodeRange, rangeDecoder\n\n","unions":[],"aliases":[{"name":"Cursor","comment":" A stable position within a text field. Build one with `cursorAt` and resolve it\nback to a current offset with `cursorOffset`. Opaque.\n","args":[],"type":"Crdt.Cursor.Internal.Cursor"},{"name":"Range","comment":" A text selection — the pair of cursors marking its two ends.\n\nThink of selecting text by dragging: the point where you first pressed down stays put,\nand the other end follows your pointer. Those are the two ends of a `Range`:\n\n  - the **anchor** is the end that stays put — where the selection started;\n  - the **focus** is the end that moves — where it reaches to right now (where the\n    caret is).\n\nKeeping them apart, rather than just storing \"leftmost\" and \"rightmost\", matters for two\nreasons. It records the selection's **direction**: dragging left-to-right puts the focus\n_after_ the anchor, dragging right-to-left puts it _before_ — a \"backwards\" selection is\nsimply one whose focus sits before its anchor. And it is what lets you **grow or shrink**\na selection from the moving end (shift-clicking, or holding shift and pressing an arrow\nkey) while the anchor holds fast.\n\nIf all you want is the selected span itself, resolve both ends to offsets with\n`cursorOffset` and take the smaller and larger of the two.\n\n","args":[],"type":"Crdt.Cursor.Internal.Range"}],"values":[{"name":"cursorAt","comment":" Make a stable cursor at character `offset` in a text field. Because the cursor is\nanchored to the characters around it (not to the number), it keeps pointing at the same\nspot as the text is edited concurrently. `Nothing` if the ref doesn't resolve to a text\nfield in this document.\n\nMaking a cursor doesn't change the document — it's a read against the current text — so\nbroadcast it on the [`Crdt.Presence`](Crdt-Presence) channel to show collaborators' carets.\n\n","type":"Crdt.Ref.Internal.Ref r Crdt.Schema.Internal.Settable String.String -> Basics.Int -> Crdt.Doc.Doc doc -> Maybe.Maybe Crdt.Cursor.Cursor"},{"name":"cursorAtRich","comment":" Like `cursorAt`, but for a **rich-text** field (a `Crdt.richText` ref). The `offset`\nis a document-wide character offset over the flattened text (block boundaries don't count\nas characters), matching the offsets `Crdt.Edit.mark` and the span stream use. The\nresulting cursor resolves and merges exactly like a plain-text one.\n","type":"Crdt.Ref.Internal.Ref r Crdt.Schema.Internal.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> Crdt.Doc.Doc doc -> Maybe.Maybe Crdt.Cursor.Cursor"},{"name":"cursorOffset","comment":" Resolve a cursor back to its current character offset in this document, or `Nothing`\nif it no longer points anywhere. The inverse of `cursorAt`.\n\nDeleting the character a cursor sits after does **not** unresolve it — the caret lands at\nthe nearest live spot, because the tombstone still marks the position. `Nothing` means this\ndocument genuinely cannot place the anchor: the cursor's text field is gone, or the anchor\ncharacter is unknown here because `Crdt.Doc.compact` dropped its tombstone or the insert\ncarrying it has not arrived yet. Render no caret in that case; cursors are ephemeral, so\nthe peer's next broadcast resolves normally.\n\n","type":"Crdt.Cursor.Cursor -> Crdt.Doc.Doc doc -> Maybe.Maybe Basics.Int"},{"name":"decoder","comment":" Decode a cursor received from a peer.\n","type":"Json.Decode.Decoder Crdt.Cursor.Cursor"},{"name":"element","comment":" The identity of the character a cursor sits just after, if any (`Nothing` when it is\nat the very start). Because it is an identity rather than an offset, it keeps pointing at\nthe same character as the text is edited around it.\n","type":"Crdt.Cursor.Cursor -> Maybe.Maybe Crdt.Id.OpId"},{"name":"encode","comment":" Serialize a cursor to JSON, to send on the presence channel.\n","type":"Crdt.Cursor.Cursor -> Json.Encode.Value"},{"name":"encodeRange","comment":" Serialize a selection to JSON.\n","type":"Crdt.Cursor.Range -> Json.Encode.Value"},{"name":"range","comment":" Build a `Range` from its two ends: the `anchor` (the end that stays put, where the\nselection started) and the `focus` (the moving end, where the caret now is).\n","type":"Crdt.Cursor.Cursor -> Crdt.Cursor.Cursor -> Crdt.Cursor.Range"},{"name":"rangeAnchor","comment":" The end of the selection that stays put — where it started.\n","type":"Crdt.Cursor.Range -> Crdt.Cursor.Cursor"},{"name":"rangeDecoder","comment":" Decode a selection received from a peer.\n","type":"Json.Decode.Decoder Crdt.Cursor.Range"},{"name":"rangeFocus","comment":" The moving end of the selection — where the caret currently is.\n","type":"Crdt.Cursor.Range -> Crdt.Cursor.Cursor"},{"name":"sameContainer","comment":" Do two cursors point into the **same container** (the same text field or list),\nregardless of where within it? Useful when rendering per-field carets: keep only the\npeers whose cursor is in the field you are currently drawing.\n","type":"Crdt.Cursor.Cursor -> Crdt.Cursor.Cursor -> Basics.Bool"}],"binops":[]},{"name":"Crdt.Doc","comment":" A **document** — the live, collaborative value your application holds and edits.\n\nA `Doc` is a JSON-like document (records, lists, dictionaries, text, counters, and\nyour own types — whatever your `Crdt` schema describes) that several people can edit at\nthe same time, on different devices, even while offline, and always end up agreeing on\nthe result. You never reconcile conflicts by hand: concurrent edits **merge**\nautomatically and every replica converges to the same value.\n\nYou **create** a document with `Crdt.init` (in the `Crdt` module, where the schema lives)\nand **edit** it through that module's typed refs, via `Crdt.Edit`. Everything else you do\nwith a document once you have one is here:\n\n1.  **read** its current typed value (`read`), or its value at a past `Version` (`readAt`);\n\n2.  **sync** it with other replicas by shipping bytes over any transport you like\n    (`encode` / `decodeInto`, or the smaller `encodeSince` delta), and merging what\n    arrives — the library guarantees convergence, the network is entirely yours;\n\n3.  **react** to what a merge changed (`mergeWithDiff` + `Doc.touched`);\n\n4.  travel through **history** (`version` / `restoreTo`), **undo/redo**, name\n    **checkpoints**, and **compact** old history away.\n\n    -- sync: send `Doc.encode doc` to a peer; on receipt:\n    doc2 =\n    Doc.decodeInto incomingBytes doc\n    |> Result.withDefault doc\n\n\n# The document\n\nYou create a document with `Crdt.init` (in the `Crdt` module, where the schema lives), then\neverything you do with it once you have one is here — starting with reading its value.\n\n@docs Doc\n\n\n# Reading the value\n\n`read` decodes the document through its schema to the typed value your app renders; `readAt`\ndoes the same for a past `Version` (time-travel). Both `Err` only on a genuinely corrupt or\nschema-incompatible document — a `ReadError`, not a merge conflict.\n\n@docs read, readAt, ReadError, readErrorToString\n\n\n# Syncing with other replicas\n\nConvergence is the library's job; moving bytes is yours. `encode` a document (or\n`encodeSince` a small delta) to a peer over any channel — WebSocket, HTTP, a file — and\n`decodeInto` merges what arrives. Edits from any replica, in any order, converge.\n\n@docs merge, encode, decodeInto, encodeSince, encodeFrom\n\n\n# Reacting to what changed\n\nMerging is automatic, but sometimes you want to _react_ to an incoming change rather than\njust re-render the new state (your `view` already reflects that for free). Knowing **what**\na merge touched and **who** did it lets you, for example, flash a highlight on a field a\ncollaborator just edited, show a \"Bob is editing\" marker, scroll a remote change into\nview, or fire a notification. `mergeWithDiff` / `decodeWithDiff` hand back a `Diff`\nalongside the new document; query it with the same typed refs you edit through — `touched`\ntells you whether (and by whom) a given spot changed, and `origins` lists every replica\nthat contributed.\n\n@docs Diff, Origin, isLocal, originReplica, mergeWithDiff, decodeWithDiff, diffSince, diffBetween\n@docs touched, origins\n\n\n# History and time travel\n\nA document remembers how it got to its current state, so you can look at any past point,\nscrub through the timeline, and restore an earlier version — and because a restore is\nitself just more edits, it syncs to everyone else too.\n\n@docs Version, version, historyLength, versionAt, restoreTo\n@docs encodeVersion, decodeVersion\n\n\n# Branching\n\nBecause history is a shared op DAG (not a linear snapshot), you can **fork** a document\ninto an independent branch, edit it in isolation, and later **merge** it back — the merge\nis just an ordinary document merge (op-union converges). Fork from the current state\n(`fork`) or from any past `version` (`forkAt`), then edit the branch freely; the original\nis untouched. `divergence` tells you how far a branch and the mainline have drifted apart\n(and hence whether a merge-back is fast-forward, already-merged, or truly divergent).\n\nA branch must edit under its **own** `ReplicaId` (you pass one to `fork`/`forkAt`), so its\nedits are concurrent with the mainline's and both survive the merge — see [`fork`](#fork).\n\n    -- try a change on a branch without disturbing the live doc:\n    branch =\n        Doc.fork (Crdt.Id.replica \"experiment\") model.doc\n            |> applySomeEdits\n\n    -- decide later: keep it (merge back) or just drop `branch` on the floor\n    merged =\n        Doc.merge model.doc branch\n\n@docs fork, forkAt, Divergence, divergence\n\n\n# Undo and redo\n\nUndo here is **per-user**, and different from the time-travel above. Time-travel\n(`readAt` / `restoreTo`) works on the _whole shared timeline_ — every replica's edits\ntogether — and `restoreTo` rewinds the document for everyone. Undo instead walks back\nonly **your own** recent actions, and leaves concurrent edits from other people alone: if\nyou type a word while a collaborator deletes a paragraph elsewhere, your undo removes your\nword and nothing else.\n\nIt works by emitting the **inverse** of your action as fresh edits, so an undo is itself\na normal, syncing change — it converges like any other, and a peer sees your undo the way\nthey saw the original. Mark where each undoable step begins by capturing a `version`\nbefore an action and passing it to `recordEdit` after; that lets a burst of keystrokes or\na whole drag collapse into one undo step.\n\n    -- when a logical action starts (focus a field, begin a drag), remember the version:\n    startVersion =\n        Doc.version model.doc\n\n    -- …after the action's edits are applied, close the step:\n    doc =\n        Doc.recordEdit startVersion editedDoc\n\n    -- later, wire buttons to it:\n    onUndo model =\n        if Doc.canUndo model.doc then\n            { model | doc = Doc.undo model.doc }\n\n        else\n            model\n\n@docs recordEdit, undo, redo, canUndo, canRedo\n\n\n# Named checkpoints\n\n@docs Checkpoint, checkpoint, checkpoints, checkpointMessage, checkpointAuthor, checkpointVersion\n\n\n# Compacting history\n\nOld history accumulates — every edit is an op the store keeps so you can time-travel and so\na lagging peer can still merge. **Compaction** is the one operation that reclaims it: fold\nhistory up to a `Version` into a base and drop the ops below it. The materialized value is\nunchanged; what you lose is the fine-grained history (time-travel and per-op provenance)\nbelow the cut.\n\nCompaction comes in two forms — **shrink yourself** or **export a shallow copy** — sharing\nthe same underlying transform:\n\n  - `compact` mutates _your_ document: it reclaims local space, at the cost of your own\n    ability to time-travel below the cut.\n  - `encodeFrom` leaves you untouched and produces a shallow snapshot to _send_: your live\n    doc keeps every op while a peer receives only the state at the cut plus the tail.\n\nEither way the key question is **which cut**. Below the cut, the history is gone — so it must\nbe a point you (or your peers) will never need to merge from again. Three safe choices:\n\n  - **Local save / single replica** — compact up to your own `version`; there is no\n    concurrent peer for that store.\n\n  - **Multiple live replicas** — `stableFrontier` computes the safe cut from the connected\n    peers' versions (the point _all_ of them have seen); a peer that's offline is caught up\n    by an automatic snapshot when it reconnects.\n\n        -- shrink my own doc up to the point every peer has seen:\n        safe =\n            Doc.stableFrontier (myVersion :: peerVersions) model.doc\n\n        doc =\n            Doc.compact safe model.doc\n\n        -- OR keep my full history but hand peers a compacted copy:\n        payloadForPeers =\n            Doc.encodeFrom safe model.doc\n\n`encodeFrom` drops the _operations_ below the cut, so a peer receives no per-edit record of\nwho changed what and when. It is **not** a redaction tool, though: the snapshot it folds\nthose ops into still carries deleted text (as tombstones that keep their content), removed\ndictionary values, and replica-identifying ids — see [`encodeFrom`](#encodeFrom).\n\n@docs compact, stableFrontier, opCount\n\n","unions":[],"aliases":[{"name":"Checkpoint","comment":" A named point in history — like a git tag. Capture the shared `Version` plus a message\nand author, so collaborators can jump back to \"before the big reorg\" together.\n","args":[],"type":"Crdt.Doc.Internal.Checkpoint"},{"name":"Diff","comment":" What a merge changed, as an opaque value you query with your typed refs\n(`Doc.touched` / `Doc.origins`). Produced by `mergeWithDiff` /\n`decodeWithDiff`.\n","args":[],"type":"Crdt.Doc.Internal.Diff"},{"name":"Divergence","comment":" How far a branch and the mainline have drifted: `ahead` = edits on the branch the\nmainline lacks, `behind` = edits on the mainline the branch lacks. `ahead == 0` ⇒ nothing\nto merge back; `behind == 0` ⇒ a fast-forward; both `> 0` ⇒ genuinely divergent (the merge\nintegrates concurrent work from both sides).\n","args":[],"type":"{ ahead : Basics.Int, behind : Basics.Int }"},{"name":"Doc","comment":" A collaborative document holding a value of type `a` (the type your schema decodes\nto). Opaque: create it with `Crdt.init`, read it with `read`, edit it through `Crdt.Edit`.\n","args":["a"],"type":"Crdt.Doc.Internal.Doc a"},{"name":"Origin","comment":" Who authored a change: this replica, or a specific remote one. Inspect it with\n`isLocal` and `originReplica`.\n","args":[],"type":"Crdt.Doc.Internal.Origin"},{"name":"ReadError","comment":" Why reading a document through its schema failed — returned by `read` and `readAt`.\nUnlike an edit error this is not a race: it means the stored data doesn't match the schema\n(a genuinely corrupt or incompatible document). Opaque; render with `readErrorToString`.\n","args":[],"type":"Crdt.Schema.Internal.Error"},{"name":"Version","comment":" A point in the document's shared history — the state at some moment. Two replicas\nholding the same edits agree on it, so a `Version` can be stored, sent, and revisited\nlater. Capture the current one with `version`.\n\nIt describes **everything the document has**, including history that `compact` has folded\naway — which is what lets a peer answer `encodeSince` with a real delta rather than\nre-sending ops you have already garbage-collected.\n\n","args":[],"type":"Crdt.Doc.Internal.Version"}],"values":[{"name":"canRedo","comment":" Whether there is a step available to `redo`.\n","type":"Crdt.Doc.Doc a -> Basics.Bool"},{"name":"canUndo","comment":" Whether there is a step available to `undo`.\n","type":"Crdt.Doc.Doc a -> Basics.Bool"},{"name":"checkpoint","comment":" Record a checkpoint at the current version with a message (the author is the\ndocument's replica).\n","type":"String.String -> Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"checkpointAuthor","comment":" The replica that recorded a checkpoint.\n","type":"Crdt.Doc.Checkpoint -> Crdt.Id.ReplicaId"},{"name":"checkpointMessage","comment":" A checkpoint's message.\n","type":"Crdt.Doc.Checkpoint -> String.String"},{"name":"checkpointVersion","comment":" The `Version` a checkpoint points at — pass to `readAt` or `restoreTo`.\n","type":"Crdt.Doc.Checkpoint -> Crdt.Doc.Version"},{"name":"checkpoints","comment":" All checkpoints recorded in this document, newest first.\n","type":"Crdt.Doc.Doc a -> List.List Crdt.Doc.Checkpoint"},{"name":"compact","comment":" Compact history up to a `Version`, dropping the fine-grained steps before it to\nreclaim space, while keeping the current value and everything after that point intact.\nUse it once old history is no longer needed for undo or time-travel.\n","type":"Crdt.Doc.Version -> Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"decodeInto","comment":" Merge a serialized document (from `encode` / `encodeSince`) that arrived from a peer\ninto this one. This is the normal sync path. `Err` if the payload is malformed.\n","type":"Json.Encode.Value -> Crdt.Doc.Doc a -> Result.Result String.String (Crdt.Doc.Doc a)"},{"name":"decodeVersion","comment":" Decode a `Version` from `encodeVersion`. `Err` on malformed input.\n","type":"Json.Encode.Value -> Result.Result String.String Crdt.Doc.Version"},{"name":"decodeWithDiff","comment":" Like `decodeInto`, but also return a `Diff` of what the incoming changes touched — the\nnetwork counterpart of `mergeWithDiff`.\n","type":"Json.Encode.Value -> Crdt.Doc.Doc a -> Result.Result String.String ( Crdt.Doc.Doc a, Crdt.Doc.Diff )"},{"name":"diffBetween","comment":" The `Diff` of what changed **between** two versions — the ops at `later` that weren't\nyet at `earlier`. Pass two adjacent scrubber steps (`versionAt (n-1)` and `versionAt n`) to\nget the single edit that produced step `n`, then `touched` it with a ref to find where it\nlanded and `originReplica` to attribute it. `earlier` should be an ancestor of `later`.\n","type":"Crdt.Doc.Version -> Crdt.Doc.Version -> Crdt.Doc.Doc a -> Crdt.Doc.Diff"},{"name":"diffSince","comment":" The `Diff` of everything that changed since a `Version` — works for your own edits too\n(capture a version before an edit, then compare), so a UI can refresh only touched slices\nhowever the change arrived.\n","type":"Crdt.Doc.Version -> Crdt.Doc.Doc a -> Crdt.Doc.Diff"},{"name":"divergence","comment":" Compare a branch against the mainline — see [`Divergence`](#Divergence). Pass them by\nname to keep the direction unambiguous:\n\n    Doc.divergence { branch = experiment, mainline = model.doc }\n\n","type":"{ branch : Crdt.Doc.Doc a, mainline : Crdt.Doc.Doc a } -> Crdt.Doc.Divergence"},{"name":"encode","comment":" Serialize the whole document for transport or storage (as JSON). Send it to a peer;\nthey `decodeInto` it. For steady-state syncing prefer `encodeSince`, which sends only\nwhat changed.\n","type":"Crdt.Doc.Doc a -> Json.Encode.Value"},{"name":"encodeFrom","comment":" Serialize a **shallow** copy of the document, keeping the history that comes after the\ngiven `Version` but discarding the history before it.\n\n`encode` serializes the whole document — every operation, back to the first edit.\n`encodeFrom v` serializes the document's current value plus only the operations made after\n`v`; the earlier operations are collapsed into a starting snapshot. The result is smaller,\nand decodes to a document with the **same value** but a shorter history.\n\nThe use for it: **archive the full history, share a shallow copy.** Persist `encode doc` for\nyourself so you keep undo and time-travel, but send peers `encodeFrom v doc` so they get a\nsmall document without your entire edit log. A good `v` is\n[`stableFrontier`](#stableFrontier) over the connected peers (the point they've all already\nreached).\n\n**It is not a redaction tool.** It drops the _operations_ below `v` — so the fine-grained\n\"who typed what, when\" is gone — but the snapshot it folds them into carries more than the\ndocument's visible value:\n\n  - a deleted list or text element survives as a **tombstone that still holds its content**,\n    along with the anchors giving its position, so deleted text is reconstructible;\n  - a removed dictionary key keeps its **value node**;\n  - every element `OpId` names the **replica** that minted it.\n\n(Tombstones can't be dropped here the way [`compact`](#compact) drops them when it folds the\nwhole store, because an operation above `v` may still anchor after one.) So do not reach for\n`encodeFrom` to strip sensitive content that was typed and later deleted — it won't. See\n[`compact`](#compact) and the module's **Compacting history** section.\n\n","type":"Crdt.Doc.Version -> Crdt.Doc.Doc a -> Json.Encode.Value"},{"name":"encodeSince","comment":" Serialize only the changes since a `Version` — a small **delta** to send a peer who\nis already up to that point. Track the version you last sent each peer and ship\n`encodeSince` from it; far cheaper than `encode` for ongoing collaboration.\n","type":"Crdt.Doc.Version -> Crdt.Doc.Doc a -> Json.Encode.Value"},{"name":"encodeVersion","comment":" Serialize a `Version` to JSON — a tiny payload (a causal frontier is a handful of op\nids), for broadcasting your position to peers. The counterpart to `decodeVersion`. This is\nhow a stable-frontier compaction policy gathers everyone's version: each peer sends\n`encodeVersion (version doc)`, and `stableFrontier` turns the collected versions into a\nsafe compaction cut.\n","type":"Crdt.Doc.Version -> Json.Encode.Value"},{"name":"fork","comment":" Fork an **independent branch** from the current state, editing under a fresh\n`ReplicaId`. The branch shares this document's history but diverges from now on: edit it\nwithout touching the original, then `merge` the two back together.\n\nGive the branch its **own** replica id (distinct from this document's and from any peer's).\nA `Doc` is immutable, so without a new id the branch would keep minting `OpId`s from the\nsame `(replica, counter)` sequence as the original — a branch edit and a mainline edit\nwould mint the _same_ id and collide (one silently wins on merge). A distinct replica makes\nthe two sides genuinely concurrent, so both survive the merge-back.\n\n","type":"Crdt.Id.ReplicaId -> Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"forkAt","comment":" Fork a branch from a **past** `version` instead of the current state — diverge from\n\"the state as of then\". The branch keeps the history up to the fork point and drops\neverything after it; the original document is untouched. Contrast `restoreTo`, which\nrewinds the _live_ document onto its own timeline. Edits under the given `ReplicaId` (see\n`fork` for why that matters); merge back with `merge`.\n","type":"Crdt.Id.ReplicaId -> Crdt.Doc.Version -> Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"historyLength","comment":" How many edit steps the document's history holds — the length of the timeline you can\nscrub through with `versionAt`.\n","type":"Crdt.Doc.Doc a -> Basics.Int"},{"name":"isLocal","comment":" Was this change made by the document's own replica (rather than a peer)?\n","type":"Crdt.Doc.Origin -> Basics.Bool"},{"name":"merge","comment":" Merge another replica's document into this one. Commutative, associative and\nidempotent: merging in any order, more than once, always converges to the same value —\nso you can gossip documents around a network of peers without coordination.\n\nUsually you merge over the wire with `decodeInto` rather than holding two live documents;\n`merge` is the in-memory form. It handles a `compact`ed argument the same way\n`decodeInto` handles a compacted peer's snapshot: history the incoming document folded\ninto its base is adopted rather than lost.\n\nThe exception, for both paths, is two documents that were compacted **independently** at\ncuts where neither covers the other — each base then holds folded history the other\nlacks, and one result can only carry one base, so the local one wins and the other's\nfolded history is dropped. That only arises by compacting at a cut a future merge partner\nhad not reached, which `compact` already documents as unsafe.\n\n","type":"Crdt.Doc.Doc a -> Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"mergeWithDiff","comment":" Like `merge`, but also return a `Diff` of what changed — so you can react to and\nattribute a collaborator's changes (highlight them, notify, scroll into view). See\n`Doc.touched`.\n","type":"Crdt.Doc.Doc a -> Crdt.Doc.Doc a -> ( Crdt.Doc.Doc a, Crdt.Doc.Diff )"},{"name":"opCount","comment":" How many operations the document holds — a rough size/complexity gauge (e.g. to decide\nwhen to `compact`).\n","type":"Crdt.Doc.Doc a -> Basics.Int"},{"name":"originReplica","comment":" The remote replica that authored a change, or `Nothing` if it was local. Use it to\nattribute or highlight a collaborator's edits.\n","type":"Crdt.Doc.Origin -> Maybe.Maybe Crdt.Id.ReplicaId"},{"name":"origins","comment":" Every `Origin` that contributed a change to `diff` — a quick \"was there any remote\nedit, and whose?\" without threading refs.\n","type":"Crdt.Doc.Diff -> List.List Crdt.Doc.Origin"},{"name":"read","comment":" Read the document's current value through its schema. `Err` only if the stored data\ncan't be interpreted by the schema (a genuinely corrupt document); render the error with\n`readErrorToString`.\n","type":"Crdt.Doc.Doc a -> Result.Result Crdt.Doc.ReadError a"},{"name":"readAt","comment":" Read the value as it stood at a past `Version` — true time-travel, without disturbing\nthe live document. Great for previews and history views.\n","type":"Crdt.Doc.Version -> Crdt.Doc.Doc a -> Result.Result Crdt.Doc.ReadError a"},{"name":"readErrorToString","comment":" A human-readable description of a `ReadError`.\n","type":"Crdt.Doc.ReadError -> String.String"},{"name":"recordEdit","comment":" Mark an **undo boundary**: record that the edits between the given past `Version` and\nnow form one undoable step. Call it after a logical action (a keystroke burst, a\ndrag-reorder) so `undo` collapses that whole action into a single step.\n","type":"Crdt.Doc.Version -> Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"redo","comment":" Redo the step most recently undone.\n","type":"Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"restoreTo","comment":" Rewind the document to a past `Version` — but **as new edits**, so the revert itself\nsyncs to every peer and converges (it doesn't silently rewind only your copy, which a\nlater merge would undo). Survivors keep their identity; only things deleted since are\nre-created fresh.\n","type":"Crdt.Doc.Version -> Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"stableFrontier","comment":" The **multi-replica-safe** cut for `compact`: given the `Version`s of the replicas you\nmight still merge with (typically every connected peer, plus your own `version`), returns\nthe causal point all of them have already delivered past. Compacting below it drops no op\nany listed peer still needs — an op even one of them is missing is kept, and concurrent\nwork nobody has shipped yet is never included.\n\nThe library computes the frontier; **you** decide who is in the list. A peer left out\n(disconnected, or lagging) isn't protected by this cut, but that's safe: when it\nreconnects, being behind the compacted base, it is caught up by an automatic full-state\nsnapshot rather than a delta. So the worst case of an aggressive cut is a heavier catch-up\nfor a straggler, never lost data or divergence. An empty list compacts nothing.\n\n","type":"List.List Crdt.Doc.Version -> Crdt.Doc.Doc a -> Crdt.Doc.Version"},{"name":"touched","comment":" Did the spot a `ref` points at — or anything under it, or a container it lives in —\nchange in `diff`, and if so who? You ask with the same typed refs you edit through (from\nyour schema bundle); the `Origin` in a `Just` says whether the change was `Local` or\nfrom a particular remote replica. `doc` is the post-merge document.\n\nUse it to _react_ to an incoming change (your `view` already reflects the new state) — e.g.\nto flash a highlight on a field a collaborator just touched:\n\n    ( doc1, diff ) =\n        Doc.mergeWithDiff model.doc incoming\n\n    highlightTitle =\n        case Doc.touched board.title doc1 diff of\n            Just origin ->\n                not (Doc.isLocal origin)\n\n            Nothing ->\n                False\n\n","type":"Crdt.Ref.Internal.Ref r kind a -> Crdt.Doc.Doc doc -> Crdt.Doc.Diff -> Maybe.Maybe Crdt.Doc.Origin"},{"name":"undo","comment":" Undo your own most recent recorded step — by emitting the **inverse** as fresh edits,\nso a peer's concurrent edit survives your undo and the undo itself syncs. Local to this\nreplica (you undo your actions, not everyone's).\n","type":"Crdt.Doc.Doc a -> Crdt.Doc.Doc a"},{"name":"version","comment":" The document's current version — capture it now to return to \"the state as of now\"\nlater (for undo boundaries, checkpoints, or a `diffSince`).\n","type":"Crdt.Doc.Doc a -> Crdt.Doc.Version"},{"name":"versionAt","comment":" The `Version` after the first `n` edits — a scrubber handle into linear history.\n`readAt (versionAt n doc) doc` shows the document after its `n`th edit. `n` is clamped to\n`[0, historyLength]`.\n","type":"Basics.Int -> Crdt.Doc.Doc a -> Crdt.Doc.Version"}],"binops":[]},{"name":"Crdt.Edit","comment":" **Edit a document through the typed refs your schema hands you.** You describe the\ndocument and get its refs in the `Crdt` module; you _change_ it here.\n\nEvery edit takes a `Ref` (naming the spot), whatever the operation needs, and the `Doc`,\nand returns `Result EditError (Doc doc)` (`contribute` also hands back the contribution's\nkey, and the `read…` functions here return the value read). The ref's `kind` makes edits\n**safe**: the compiler rejects a nonsensical operation — `increment` on text, `move` on a\nnon-movable list, `set` with the wrong value type — so most mistakes are compile errors,\nnot runtime surprises. What's left, an `EditError`, is a target that didn't resolve\nagainst the current value; you can usually `Result.withDefault doc` past it.\n\n    import Crdt\n    import Crdt.Edit as Edit\n\n    doc1 =\n        Edit.set board.title \"Trip plan\" doc\n            |> Result.withDefault doc\n\n    doc2 =\n        Edit.increment board.votes 1 doc1\n            |> Result.withDefault doc1\n\n    -- Edit.increment board.title  =>  a compile error\n\n\n# Errors\n\n@docs EditError, editErrorToString\n\n\n# Simple values\n\n@docs set, over, increment, switch\n\n\n# Lists\n\n`append` adds to the end, `insert` at any index (`0` prepends), `remove` deletes by index;\nall work on any list. `move` (reorder) is reserved for a `movableList`.\n\n@docs append, insert, remove, move\n\n\n# Dictionaries\n\n@docs setKey, removeKey\n\n\n# Trees\n\nNode ids come from reading the tree (`Crdt.Tree.itemId`); pass them to these.\n\n@docs addChild, moveInto, moveBefore, moveAfter, removeNode\n\n\n# Rich text\n\n@docs setRich, mark, unmark\n@docs readBlocks, readBlocksAt, setBlockText, splitBlock, mergeBlock, setBlockType, indentBlock, outdentBlock\n\n\n# Your own CRDT types\n\n@docs contribute, retract\n\n\n# Reading a ref at a past version\n\n@docs readAt\n\n","unions":[{"name":"EditError","comment":" Why an edit couldn't be applied — the error type the editing functions return.\n\nThe compile-time checks already rule out the _kind_ mistakes (you can't `increment` text,\nor `move` a non-movable list — those don't compile). What's left are the two runtime ways\na target can fail to resolve against the document's **current** value, both of which you\ncan branch on:\n\n  - `PathNotFound where_` — nothing lives at the spot the ref points to right now. Most\n    often a list index or dictionary key that isn't there (perhaps a peer removed it), or\n    a tree node id that has since been deleted. The `String` names the spot.\n  - `WrongNodeType detail` — something is there, but it isn't the kind of value the edit\n    expects (for instance a block edit aimed at a field that turned out not to be rich\n    text). This points at corrupt or mismatched data rather than a normal race, and the\n    `String` describes what was expected.\n\nIn practice most call sites treat either as \"the edit didn't apply, keep the document as\nit was\" and `Result.withDefault doc` past it. Branch on the variants when you want to tell\nthe two apart — e.g. ignore a `PathNotFound` (a benign race) but log a `WrongNodeType` (a\nreal bug). Render either with `editErrorToString`.\n\n","args":[],"cases":[["PathNotFound",["String.String"]],["WrongNodeType",["String.String"]]]}],"aliases":[],"values":[{"name":"addChild","comment":" Add a new node (seeded from `value`) as the last child of `parent` (`Nothing` =\na new root) in a tree ref.\n\nTakes the **node bundle** (the same one you gave `Crdt.tree`) for the payload schema. Unlike\n`append`/`setKey` — whose element schema the library recovers from the container ref — a\ntree seeds an empty forest, so the node schema can't be recovered and is passed here.\n\n","type":"Crdt.Ref.Internal.Ref r (Crdt.TreeK ek a) forest -> Crdt.Crdt ek a s -> a -> Maybe.Maybe Crdt.Id.OpId -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"append","comment":" Append a value to the end of a list ref. The element schema is recovered from the list\nref itself (no schema argument needed). Compiles for any list (`ListK _ ek e`).\n","type":"Crdt.Ref.Internal.Ref r (Crdt.ListK mv ek e) (List.List e) -> e -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"contribute","comment":" Add a **contribution** to a user-defined op-set CRDT (`Crdt.opSet`). Only compiles for\nan `OpSetK` ref. Pass the same `contribution` schema you gave `opSet` and a contribution\nvalue; it is written under a fresh op-id, so concurrent contributions from any replicas\nall survive and the op-set's `fold` sees them all. Returns the contribution's key (its\nop-id string), which you can keep to `retract` exactly that contribution later.\n\n    case Edit.contribute board.highScore Crdt.int 42 doc of\n        Ok ( key, doc1 ) ->\n            -- keep `key` if you'll want to `retract` this contribution later\n            ...\n\n        Err _ ->\n            ...\n\n","type":"Crdt.Ref.Internal.Ref r (Crdt.OpSetK ck c) a -> Crdt.Crdt ck c s -> c -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError ( String.String, Crdt.Doc.Doc doc )"},{"name":"editErrorToString","comment":" A human-readable description of an `EditError`, for logging or a message. Branch on\nthe variants themselves if you need to react differently to each.\n","type":"Crdt.Edit.EditError -> String.String"},{"name":"increment","comment":" Add `delta` to a counter ref. Only compiles for a `Counter` ref, so\n`increment` on a register or text is a **type error**.\n","type":"Crdt.Ref.Internal.Ref r Crdt.Counter Basics.Int -> Basics.Int -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"indentBlock","comment":" Indent (raise depth by one) block `blockIndex`.\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"insert","comment":" Insert a value at **visible index `i`** of a list ref (`0` prepends; an `i` at or past\nthe end is the same as `append`), seeded through the element schema. Compiles for any list\n(`ListK _ ek e`) — you don't need a `movableList` just to insert somewhere other than the\nend (e.g. a \"newest first\" feed that prepends). Concurrent inserts at the same position\nconverge deterministically and keep each contiguous run intact ([Fugue](https://arxiv.org/abs/2305.00583)).\n\n    -- prepend a new item (newest-first list):\n    Edit.insert todosRef 0 todo doc\n\n","type":"Crdt.Ref.Internal.Ref r (Crdt.ListK mv ek e) (List.List e) -> Basics.Int -> e -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"mark","comment":" Apply a formatting mark of kind `type_` (e.g. `\"bold\"`, `\"link\"`) with `value`\nover the visible character range `[from, to)` of a rich-text ref. Use\n`RichText.Flag` for boolean marks (bold/italic/…) and `RichText.Value href` for value\nmarks (link/color). Only compiles for a `RichK` ref.\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> Basics.Int -> String.String -> Crdt.RichText.MarkValue -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"mergeBlock","comment":" Merge block `blockIndex` into the previous block (tombstones its marker). No-op on\nblock 0.\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"move","comment":" Move the element at visible index `from` to index `to`. Compiles **only** for a\n`Movable` list (`ListK Movable …`) — calling it on a plain `list` is a type error.\nThe moved item keeps its identity (nested edits and cursors follow it).\n","type":"Crdt.Ref.Internal.Ref r (Crdt.ListK Crdt.Movable ek e) (List.List e) -> Basics.Int -> Basics.Int -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"moveAfter","comment":" Move `child` to sit immediately after `sibling` (under the sibling's parent).\n","type":"Crdt.Ref.Internal.Ref r (Crdt.TreeK ek a) forest -> Crdt.Id.OpId -> Crdt.Id.OpId -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"moveBefore","comment":" Move `child` to sit immediately before `sibling` (under the sibling's parent).\n","type":"Crdt.Ref.Internal.Ref r (Crdt.TreeK ek a) forest -> Crdt.Id.OpId -> Crdt.Id.OpId -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"moveInto","comment":" Re-parent `child` to be the last child of `parent` (`Nothing` = a root).\nCycle-forming moves are skipped (the node stays put), so this always converges.\n","type":"Crdt.Ref.Internal.Ref r (Crdt.TreeK ek a) forest -> Crdt.Id.OpId -> Maybe.Maybe Crdt.Id.OpId -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"outdentBlock","comment":" Outdent (lower depth by one, min 0) block `blockIndex`.\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"over","comment":" Modify the value at a ref: read it, apply `f`, write it back. No-op if the spot\ndoesn't currently resolve (e.g. an inactive variant payload).\n","type":"Crdt.Ref.Internal.Ref r kind a -> (a -> a) -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"readAt","comment":" Read the value a **single ref** pointed at as of a past `Crdt.Doc.Version` — the\nref-scoped counterpart of `Crdt.Doc.readAt` (which reads the whole document). Use it to\ninspect one field's history without decoding the entire document — for example to find\nwhere a past edit landed, or to preview a single field on a timeline.\n\n`Err` if the ref doesn't resolve at that version (its container didn't exist yet) or the\nstored data doesn't match the schema — the same conditions as an ordinary edit.\n\n","type":"Crdt.Doc.Version -> Crdt.Ref.Internal.Ref r kind a -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError a"},{"name":"readBlocks","comment":" Read a rich-text ref as **blocks** (type + depth + spans + marker id), rather\nthan the flat `List Span` the schema decodes to. Used to resolve a block index to its\nmarker `OpId` for the block edits. See [`Crdt.RichText.Block`](Crdt-RichText#Block).\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (List.List Crdt.RichText.Block)"},{"name":"readBlocksAt","comment":" Read a rich-text ref as **blocks as of a past `Crdt.Doc.Version`** — the time-travel\ncounterpart of `readBlocks`, mirroring `Crdt.Doc.readAt`. A UI previewing history uses this\nto feed the editor the blocks the document had at that version; the live document is\nunchanged.\n","type":"Crdt.Doc.Version -> Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (List.List Crdt.RichText.Block)"},{"name":"remove","comment":" Remove the element at visible index `i` from a list ref.\n","type":"Crdt.Ref.Internal.Ref r (Crdt.ListK mv ek e) (List.List e) -> Basics.Int -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"removeKey","comment":" Remove a dict key (LWW tombstone).\n\nRemoving a key this replica has not observed does nothing: a delete races only against\nwrites it has actually seen, so a concurrent `setKey` of an unseen key wins.\n\n","type":"Crdt.Ref.Internal.Ref r (Crdt.DictK vk v) dictType -> String.String -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"removeNode","comment":" Remove a node and its subtree from a tree ref.\n","type":"Crdt.Ref.Internal.Ref r (Crdt.TreeK ek a) forest -> Crdt.Id.OpId -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"retract","comment":" Take back **one specific contribution** you previously added to an op-set, so it no\nlonger counts toward the folded value.\n\nAn op-set is a bag of individual contributions, each written under its own id (see\n`Crdt.opSet` / `contribute`). `retract` removes one by that id — the `key` string that\n`contribute` handed back — not by its value. So you must **keep the key** from the\n`contribute` you want to be able to undo; retracting isn't \"remove a `5` from the set\",\nit's \"remove _the contribution I made at that moment_\". Retracting a key you don't hold\n(or one already retracted) is a harmless no-op.\n\nUnder the hood it's a last-write-wins presence flip on that contribution — it converges\nlike any edit and can itself be re-added, and it's what turns a grow-only op-set into one\nyou can shrink. After retraction the op-set's `fold` runs over the remaining\ncontributions only.\n\n    -- a shopping cart as an add-wins set of item ids:\n    cart =\n        Crdt.opSet { contribution = Crdt.string, fold = dedupe }\n\n    -- add an item, remembering the key so it can be removed later:\n    ( appleKey, doc1 ) =\n        Edit.contribute cartRef Crdt.string \"apple\" doc\n            |> Result.withDefault ( \"\", doc )\n\n    -- …the shopper removes it again:\n    doc2 =\n        Edit.retract cartRef appleKey doc1\n            |> Result.withDefault doc1\n\nIf you want removal keyed by value rather than by contribution id, keep your own\n`Dict value key` alongside the document and look the key up when removing.\n\n","type":"Crdt.Ref.Internal.Ref r (Crdt.OpSetK ck c) a -> String.String -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"set","comment":" Set the value at a ref: a present spot is overwritten, emitting minimal ops (a text\nspot still merges character-wise).\n\nThrough a **sum-type payload ref**, the write lands only while that variant is active. If\nthe variant was active at some point and isn't now, the payload key still exists and the\nwrite is a harmless no-op on the read. But for a variant that has **never** been active the\nkey is absent entirely and there is nothing to write under, so you get\n`Err (PathNotFound …)` — use `switch` to make the variant active first. Call sites that\ndon't care can `Result.withDefault doc` either way.\n\n","type":"Crdt.Ref.Internal.Ref r kind a -> a -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"setBlockText","comment":" Replace the text of **block `blockIndex`** (a minimal diff scoped to that block's\ncharacters), leaving marks and other blocks untouched. This is what an editor should\ncall per keystroke so typed text lands inside the right block (unlike `setRich`, which\ndiffs the whole document and can misplace text across a block boundary).\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> String.String -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"setBlockType","comment":" Set (`Just t`) or clear (`Nothing`) the app-defined type of block `blockIndex`.\n`type_` is an opaque string the library never interprets.\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> Maybe.Maybe String.String -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"setKey","comment":" Set (create or overwrite) a dict key to a value. The value schema is recovered from the\ndict ref itself (no schema argument needed). Compiles for a dict ref (`DictK vk v`).\n","type":"Crdt.Ref.Internal.Ref r (Crdt.DictK vk v) (Dict.Dict String.String v) -> String.String -> v -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"setRich","comment":" Replace the character content of a **rich-text** ref (a minimal text diff, like\n`set` on plain text). Marks are preserved and follow the surviving characters. Only\ncompiles for a `RichK` ref.\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> String.String -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"splitBlock","comment":" Split at a block-relative caret: `charOffset` characters into block `blockIndex`\n(0 = the leading block). Inserts a block boundary there.\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> Basics.Int -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"switch","comment":" Change which **variant** a custom-type value is, seeding the new variant's payload\nfrom the value you pass. Only compiles for a `Variants` ref (one built by `Crdt.custom`).\n\nThe distinction to keep straight: `switch` moves between variants; a payload `Ref` (one\nthe `custom` builder handed you, reached via `Crdt.at`) edits _inside_ the variant that's\ncurrently active. Given the `Status` type from `Crdt.custom`:\n\n    -- switch the whole status from whatever it is to `Archived \"old project\"`:\n    doc |> Edit.switch statusRef (Archived \"old project\")\n\n    -- vs. edit the reason text *within* an already-Archived status (a no-op if the\n    -- status is currently Active or Snooze):\n    doc |> Edit.set (statusRef |> Crdt.at status.archivedReason) \"new reason\"\n\nSo reach for `switch` when the case changes (`Active` → `Archived`), and a payload ref\nwhen the case stays the same but its contents change. Which variant is active merges\nlast-write-wins.\n\n","type":"Crdt.Ref.Internal.Ref r (Crdt.Variants v) v -> v -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"},{"name":"unmark","comment":" Clear mark `type_` over the visible range `[from, to)` of a rich-text ref.\n","type":"Crdt.Ref.Internal.Ref r Crdt.RichK (List.List Crdt.RichText.Span) -> Basics.Int -> Basics.Int -> String.String -> Crdt.Doc.Doc doc -> Result.Result Crdt.Edit.EditError (Crdt.Doc.Doc doc)"}],"binops":[]},{"name":"Crdt.Id","comment":" The two identity types the library hands back to you.\n\nEvery replica — every independent copy of a document, such as one browser tab — has a\nstable `ReplicaId`, a string you choose (typically a random per-tab token). You pass one\nto `Crdt.init` so the library knows which replica it is editing on behalf of.\n\nEvery edit the library records is stamped with an `OpId`, a globally-unique operation\nid. You never mint these yourself, but they surface as **stable handles** to things\ninside a document: the id of an item in a `Crdt.Tree`, or the identity a `Crdt.Cursor`\nis pinned to. An `OpId` can be compared and turned into a string, which is all you need\nto use one as a dictionary key or to look a value up.\n\n\n# Replicas\n\n@docs ReplicaId, replica, replicaToString\n\n\n# Operation ids\n\n@docs OpId, opIdCounter, opIdReplica, compareOpId, opIdToString\n\n","unions":[],"aliases":[{"name":"OpId","comment":" A globally-unique id for a single recorded operation. You get these back as handles\nto things inside a document (tree item ids, cursor anchors); the library mints them, you\nread and compare them.\n","args":[],"type":"Crdt.Id.Internal.OpId"},{"name":"ReplicaId","comment":" A stable per-replica identifier. Two browser tabs, two `ReplicaId`s. Build one with\n`replica`.\n","args":[],"type":"Crdt.Id.Internal.ReplicaId"}],"values":[{"name":"compareOpId","comment":" A total order on `OpId`s — by counter, then replica as a tiebreak. Handy if you keep\nyour own sorted structure keyed by id.\n","type":"Crdt.Id.OpId -> Crdt.Id.OpId -> Basics.Order"},{"name":"opIdCounter","comment":" The counter part of an `OpId`.\n","type":"Crdt.Id.OpId -> Basics.Int"},{"name":"opIdReplica","comment":" The replica that minted an `OpId`.\n","type":"Crdt.Id.OpId -> Crdt.Id.ReplicaId"},{"name":"opIdToString","comment":" A stable string form of an `OpId`, suitable as a `Dict` key (`\"3@alice\"`).\n","type":"Crdt.Id.OpId -> String.String"},{"name":"replica","comment":" Build a `ReplicaId` from a string. Use something unique per replica — a random\nper-tab token, a device id, a signed-in user id.\n\n    Crdt.init (Crdt.Id.replica \"alice-tab-1\") schema\n\n","type":"String.String -> Crdt.Id.ReplicaId"},{"name":"replicaToString","comment":" The underlying string of a `ReplicaId`, e.g. to show who made a change.\n","type":"Crdt.Id.ReplicaId -> String.String"}],"binops":[]},{"name":"Crdt.Presence","comment":" **Presence** is the \"who's here and what are they doing\" layer of a collaborative\napp — each person's name and colour, whether they are online, where their cursor is —\nas opposed to the document they are editing together.\n\nIt is kept deliberately separate from your `Crdt.Doc`, because it behaves differently:\npresence is **throwaway**. You want the newest value and nothing else — when someone\nmoves their cursor you only care where it is _now_, and when they close the tab their\nmarker should just disappear. There is no history to preserve and nothing to reconcile\ncharacter by character, so presence has its own tiny, self-contained mechanism rather\nthan going through the document's CRDT machinery.\n\nEach peer owns one slot in a shared table, keyed by its `Crdt.Id.ReplicaId`. When two\ntables merge, the newer value wins for each peer — simple last-writer-wins, per peer.\n\n\n## Describing your presence value\n\nYou first describe the shape of one peer's presence with a small `Codec`, so it can be\nsent as JSON. It reads like a compact record builder:\n\n    type alias Peer =\n        { name : String\n        , color : String\n        , caret : Maybe Cursor\n        }\n\n    peerCodec : Presence.Codec Peer\n    peerCodec =\n        Presence.codec Peer\n            |> Presence.field \"name\" .name Presence.string\n            |> Presence.field \"color\" .color Presence.string\n            |> Presence.optional \"caret\" .caret Presence.cursor\n            |> Presence.buildCodec\n\n\n## Using it\n\nKeep one `Presence Peer` in your model alongside your document. Set your own state as it\nchanges, broadcast the table to peers exactly as you broadcast document updates, merge\nwhat comes back, and render everyone with `peers`:\n\n    model.presence\n        |> Presence.setLocal { name = \"Alice\", color = \"#e11\", caret = Just cursor }\n        |> Presence.encode\n\n    -- send this JSON to peers\n    -- on receiving a peer's table:\n    case Presence.decode peerCodec json of\n        Ok theirs ->\n            { model | presence = Presence.merge model.presence theirs }\n\n        Err _ ->\n            model\n\n\n# The presence table\n\n@docs Presence, Codec\n\n\n# Setting and reading state\n\n@docs init, setLocal, updateLocal, local, peers, merge, remove\n\n\n# Sending it over the wire\n\n@docs encode, decode\n\n\n# Describing a presence value\n\n@docs codec, field, optional, buildCodec\n\n\n# Field types\n\n@docs string, bool, int, cursor, range, custom\n@docs FieldCodec\n\n","unions":[{"name":"Codec","comment":" Describes how one peer's presence value is turned into JSON and back. Build one with\n`codec`/`field`/`buildCodec`. Opaque.\n","args":["a"],"cases":[]},{"name":"FieldCodec","comment":" Describes one field of a presence value (see [`string`](#string) / [`bool`](#bool) /\n[`int`](#int) / [`custom`](#custom)).\n","args":["a"],"cases":[]},{"name":"Presence","comment":" A table of everyone's presence state: this replica's own id and codec, plus the\nlatest known value for every peer (including itself). Opaque — build one with `init`.\n","args":["a"],"cases":[]}],"aliases":[],"values":[{"name":"bool","comment":" A boolean field.\n","type":"Crdt.Presence.FieldCodec Basics.Bool"},{"name":"buildCodec","comment":" Finish building a presence codec.\n","type":"Crdt.Presence.CodecBuilder a a -> Crdt.Presence.Codec a"},{"name":"codec","comment":" Start describing a presence value, given the constructor of your record (just like\n`Crdt.record`). Follow it with a `field`/`optional` per record field and finish with\n`buildCodec`.\n","type":"(a -> b) -> Crdt.Presence.CodecBuilder full (a -> b)"},{"name":"cursor","comment":" A `Crdt.Cursor` field — a peer's caret position. The common case for sharing where\neveryone is typing, so it is built in:\n\n    Presence.codec Peer\n        |> Presence.optional \"caret\" .caret Presence.cursor\n        |> Presence.buildCodec\n\n","type":"Crdt.Presence.FieldCodec Crdt.Cursor.Cursor"},{"name":"custom","comment":" A field with your own JSON encoder and decoder — for anything the built-in field\ntypes don't cover:\n\n    Presence.custom encodeColor colorDecoder\n\n","type":"(a -> Json.Encode.Value) -> Json.Decode.Decoder a -> Crdt.Presence.FieldCodec a"},{"name":"decode","comment":" Decode a presence table received from a peer. Merge the result into your own with\n`merge`.\n","type":"Crdt.Presence.Codec a -> Json.Encode.Value -> Result.Result String.String (Crdt.Presence.Presence a)"},{"name":"encode","comment":" Serialize the whole presence table to JSON, to broadcast to peers.\n","type":"Crdt.Presence.Presence a -> Json.Encode.Value"},{"name":"field","comment":" Add a required field: its JSON name, a getter, and its field type.\n","type":"String.String -> (full -> a) -> Crdt.Presence.FieldCodec a -> Crdt.Presence.CodecBuilder full (a -> b) -> Crdt.Presence.CodecBuilder full b"},{"name":"init","comment":" A fresh, empty presence table for a replica. Pass the same `ReplicaId` you gave\n`Crdt.init` and the `Codec` describing your presence value.\n","type":"Crdt.Id.ReplicaId -> Crdt.Presence.Codec a -> Crdt.Presence.Presence a"},{"name":"int","comment":" An integer field.\n","type":"Crdt.Presence.FieldCodec Basics.Int"},{"name":"local","comment":" This replica's own current state, if it has been set.\n","type":"Crdt.Presence.Presence a -> Maybe.Maybe a"},{"name":"merge","comment":" Merge a table received from a peer into this one, keeping the newer value for each\nreplica. Like document `merge`, the result is the same no matter what order tables\narrive in.\n","type":"Crdt.Presence.Presence a -> Crdt.Presence.Presence a -> Crdt.Presence.Presence a"},{"name":"optional","comment":" Add an optional field, read as a `Maybe` and sent as `null` when absent — good for\nthings not always present, like a cursor only shown while editing.\n","type":"String.String -> (full -> Maybe.Maybe a) -> Crdt.Presence.FieldCodec a -> Crdt.Presence.CodecBuilder full (Maybe.Maybe a -> b) -> Crdt.Presence.CodecBuilder full b"},{"name":"peers","comment":" Every known peer and its state, including yourself, ordered by replica id. This is\nwhat you fold over to render collaborators (filter out your own id if you don't want to\ndraw yourself).\n","type":"Crdt.Presence.Presence a -> List.List ( Crdt.Id.ReplicaId, a )"},{"name":"range","comment":" A `Crdt.Cursor.Range` field — a peer's text **selection** (its two ends). Like\n`cursor`, but for a highlighted range rather than a single caret.\n","type":"Crdt.Presence.FieldCodec Crdt.Cursor.Range"},{"name":"remove","comment":" Drop a peer from the table, e.g. when they disconnect. Because presence is\nthrowaway there is nothing left behind — the peer simply vanishes, and reappears if it\nbroadcasts again later.\n","type":"Crdt.Id.ReplicaId -> Crdt.Presence.Presence a -> Crdt.Presence.Presence a"},{"name":"setLocal","comment":" Set this replica's own presence state (replacing any previous value). Call it\nwhenever your local state changes — the user renamed themselves, moved their cursor —\nand then broadcast the table.\n","type":"a -> Crdt.Presence.Presence a -> Crdt.Presence.Presence a"},{"name":"string","comment":" A string field.\n","type":"Crdt.Presence.FieldCodec String.String"},{"name":"updateLocal","comment":" Update this replica's own state with a function, if it has been set. Handy for\nchanging one part — moving the cursor while keeping name and colour:\n\n    Presence.updateLocal (\\me -> { me | caret = Just cursor }) model.presence\n\n","type":"(a -> a) -> Crdt.Presence.Presence a -> Crdt.Presence.Presence a"}],"binops":[]},{"name":"Crdt.RichText","comment":" The values you read out of a **rich (formatted) text** field — the collaborative\nanalogue of a document with bold, links, headings and lists.\n\nYou never construct these yourself. You describe a rich-text field with `Crdt.richText`,\nedit it through `Crdt.Edit` (type into it with `Crdt.Edit.setRich` /\n`Crdt.Edit.setBlockText`, turn formatting on and off with `Crdt.Edit.mark`/`Crdt.Edit.unmark`,\nor restructure it with `Crdt.Edit.splitBlock` and friends), and\nread it back as the shapes below:\n\n  - `Crdt.Doc.read` decodes a rich-text field to a flat `List Span` — the text broken into\n    runs, each run tagged with the formatting active over it. This is enough to render\n    inline formatting (bold, italic, links).\n  - `Crdt.Edit.readBlocks` gives you a `List Block` — the same text grouped into the\n    document's paragraphs, headings and list items, each with its own spans. Use this\n    when your editor has block structure.\n\nFormatting is stored as **marks** anchored to individual characters, not to positions,\nso it survives concurrent editing: if one person bolds a word while another inserts\ntext inside it, the bold still covers the right characters after the two edits merge.\nWhen two people format the same character at the same time, the most recent edit wins\nfor that character.\n\n@docs Span, MarkValue\n\n@docs Block\n\n","unions":[{"name":"MarkValue","comment":" What a single mark carries where it is active.\n\n  - `Flag` — an on/off mark that carries no data, like bold or italic. Its presence in\n    a span's `marks` is all the information there is.\n  - `Value String` — a mark that carries a string, like a link's `href` or a highlight\n    colour.\n\nYou choose which kind a mark is when you apply it over a range — `Crdt.Edit.mark ref from to\n\"bold\" RichText.Flag doc` versus `Crdt.Edit.mark ref from to \"link\" (RichText.Value \"https://…\")\ndoc`. A mark that is not active over a span is simply absent from its `marks` — there is\nno \"off\" value to match on.\n\n","args":[],"cases":[["Flag",[]],["Value",["String.String"]]]}],"aliases":[{"name":"Block","comment":" One block of the document — a paragraph, heading or list item — as produced by\n`Crdt.Edit.readBlocks`.\n\n  - `type_` is the app-defined kind of block, the string you set with\n    `Crdt.Edit.setBlockType` (for example `\"h1\"`, `\"blockquote\"` or `\"ul\"`). An empty string\n    is the default block (a plain paragraph). The library never interprets this value;\n    your editor decides what the vocabulary means and how to render it.\n  - `depth` is the indent level, starting at `0`, changed with `Crdt.Edit.indentBlock` and\n    `Crdt.Edit.outdentBlock`.\n  - `spans` is the block's inline content, exactly as `Crdt.Doc.read` gives them — so\n    the same `viewSpan` renders both.\n  - `marker` is an internal identity for the block (`Nothing` for the leading block,\n    which has no marker element), used by the block-editing functions in `Crdt.Edit`.\n    You will rarely read it directly.\n\n","args":[],"type":"{ marker : Maybe.Maybe Crdt.Id.OpId, type_ : String.String, depth : Basics.Int, spans : List.List Crdt.RichText.Span }"},{"name":"Span","comment":" One run of text that shares a single set of active formatting marks.\n\n`marks` maps a mark's name (the string you passed to `Crdt.Edit.mark`, e.g. `\"bold\"` or\n`\"link\"`) to the value it carries over this run. A name being present means that mark\nis _on_ for every character in `text`; a name being absent means it is off.\n\nRendering a `List Span` is a matter of wrapping each run's `text` in whatever your\nview uses for the marks it carries:\n\n    viewSpan : Span -> Html msg\n    viewSpan span =\n        let\n            wrap name value html =\n                case name of\n                    \"bold\" ->\n                        Html.strong [] [ html ]\n\n                    \"link\" ->\n                        case value of\n                            RichText.Value href ->\n                                Html.a [ Html.Attributes.href href ] [ html ]\n\n                            RichText.Flag ->\n                                html\n\n                    _ ->\n                        html\n        in\n        Dict.foldl wrap (Html.text span.text) span.marks\n\n","args":[],"type":"{ text : String.String, marks : Dict.Dict String.String Crdt.RichText.MarkValue }"}],"values":[],"binops":[]},{"name":"Crdt.Tree","comment":" The value you read out of a **tree** field — hierarchical, re-orderable data such\nas an outline, a file tree, or threaded comments.\n\nYou describe a tree with `Crdt.tree`, edit it through `Crdt.Edit` (move a node\nunder a new parent, reorder it among its siblings, change its contents, or delete it),\nand read it back as a `Forest` of `Item`s. Each item keeps a **stable id** across every\nmove, so the ids you get from a read are exactly what you pass back to the editing\nfunctions in `Crdt.Edit` to say _which_ node to act on.\n\n    view : Forest Comment -> Html msg\n    view forest =\n        Html.ul [] (List.map viewItem forest)\n\n    viewItem : Item Comment -> Html msg\n    viewItem item =\n        Html.li []\n            [ Html.text (Tree.itemValue item).body\n            , view (Tree.itemChildren item)\n            ]\n\nTrees converge under concurrent editing without ever forming a cycle: if two people\nconcurrently move nodes in a way that would make a node its own ancestor, one of the\nmoves is dropped on merge and its node simply stays where it was. Siblings stay in a\nconsistent order for everyone.\n\n@docs Forest, Item\n\n@docs itemId, itemValue, itemChildren\n\n\n# Converting to a rosetree\n\nIf you'd rather work with a richer tree type, a `Forest` maps cleanly onto a\n[rosetree](https://package.elm-lang.org/packages/gampleman/elm-rosetree/latest/) — a\n`Forest a` becomes a `List (Tree a)`, since a rosetree has a single root while a `Forest`\ncan have several. Build each one with the rosetree package's own `unfold` (`Rose.unfold`\nbelow), seeding from an `Item`, so the construction stays stack-safe on deep trees:\n\n    import Tree as Rose\n\n    toRosetrees : Forest a -> List (Rose.Tree a)\n    toRosetrees forest =\n        List.map fromItem forest\n\n    fromItem : Item a -> Rose.Tree a\n    fromItem =\n        Rose.unfold\n            (\\item ->\n                ( Tree.itemValue item\n                , Tree.itemChildren item\n                )\n            )\n\n","unions":[],"aliases":[{"name":"Forest","comment":" A tree read as a nested value: an ordered list of top-level `Item`s, each carrying\nits own children. This is what `Crdt.tree` reads as.\n","args":["a"],"type":"Crdt.Tree.Internal.Forest a"},{"name":"Item","comment":" One node of a read tree. Read its parts with `itemId`, `itemValue` and\n`itemChildren` — it is opaque so that the tree can be extended without breaking your\ncode.\n","args":["a"],"type":"Crdt.Tree.Internal.Item a"}],"values":[{"name":"itemChildren","comment":" The node's children, in sibling order — itself a `Forest`, so you recurse into it\nthe same way you render the top level.\n","type":"Crdt.Tree.Item a -> Crdt.Tree.Forest a"},{"name":"itemId","comment":" The node's stable id. Pass this to the tree-editing functions in `Crdt.Edit`\n(`Crdt.Edit.moveInto`, `Crdt.Edit.removeNode`, …) to say which node you mean; it does not change\nwhen the node is moved or edited.\n","type":"Crdt.Tree.Item a -> Crdt.Id.OpId"},{"name":"itemValue","comment":" The node's decoded value (whatever your schema's element type reads as).\n","type":"Crdt.Tree.Item a -> a"}],"binops":[]}]