Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/docs/block-sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ The status (`LogStatus`: each writer directory's state, and the paths skipped) i
None of these block convergence — every device agrees on the same answer. They are about the answer being the one a person expected. Ranked by how much they will actually bite.

1. **`block.outdented` moves blocks it does not name.** Trailing siblings become children, so its effect is defined over whoever the siblings happened to be at fold time, which differs per device. Carrying the explicit ids it moves makes it mergeable. Payload `v: 3` — `v: 2` is the one that dropped `page`. **Do this one before relying on sync daily.**
2. **`block.deleted` has no tombstone.** Device A deletes X while device B writes a block `after: X`. The fallback in `reduce` (`packages/pages/blocks.ts`) keeps the content — the right instinct — but the block lands at the bottom of the page instead of where it was written. A tombstone restores the placement.
2. **`block.deleted` has no tombstone.** Device A deletes X while device B writes a block `after: X`. The fallback in `reduce` (`packages/pages/src/blocks/blocks.ts`) keeps the content — the right instinct — but the block lands at the bottom of the page instead of where it was written. A tombstone restores the placement.
3. **`block.indented` no-ops when its `parent` is gone**, silently dropping the user's intent. Deterministic, so both devices drop it identically. Still surprising.
4. **Text is last-writer-wins per block.** Concurrent edits to one block lose one side. The loser is still on disk forever, so it can be surfaced rather than pretended away.

Expand All @@ -55,7 +55,7 @@ None of these block convergence — every device agrees on the same answer. They

## Tests

The ledger's tests cover the order, the stalls and convergence, with events of their own that are not blocks. The block fold is tested without a log in `packages/pages/blocks.test.ts` and `packages/pages/pages.test.ts`, and through one in `src/main/features/projects/pages/pages-store/pages-store.test.ts`. Both write from a single device. Nothing yet tests two devices' block events interleaving, so each deferred item above lands with a test that folds its events in both orders.
The ledger's tests cover the order, the stalls and convergence, with events of their own that are not blocks. The block fold is tested without a log in `packages/pages/src/blocks/blocks.test.ts` and `packages/pages/src/pages.test.ts`, and through one in `src/main/features/projects/pages/pages-store/pages-store.test.ts`. Both write from a single device. Nothing yet tests two devices' block events interleaving, so each deferred item above lands with a test that folds its events in both orders.

## Order of work

Expand Down
6 changes: 3 additions & 3 deletions packages/pages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"description": "What pbnotes events mean: the pages fold, the queries over it, and the commands that write it.",
"private": true,
"type": "module",
"main": "./pages.ts",
"exports": "./pages.ts",
"main": "./src/pages.ts",
"exports": "./src/pages.ts",
"scripts": {
"test": "node --test '*.test.ts'"
"test": "node --test '**/*.test.ts'"
},
"dependencies": {
"mdast-util-gfm-autolink-literal": "^2.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
predict,
reduce,
} from "./blocks.ts";
import { getPage, getReferences, searchPages } from "./pages.ts";
import { getPage, getReferences, searchPages } from "../pages.ts";
import type { Block, PageEvent, Pages } from "./blocks.ts";

const TODAY = "2026-09-21";
Expand Down
19 changes: 13 additions & 6 deletions packages/pages/blocks.ts → packages/pages/src/blocks/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
* fold, or they drift: that is why this is a package and not a copy.
*/

import { canonicalize } from "./canonicalize.ts";
import { read } from "./reading.ts";
import type { Properties } from "./properties.ts";
import { canonicalize } from "../canonicalize/canonicalize.ts";
import { read } from "../reading/reading.ts";
import type { Properties } from "../properties/properties.ts";

export type Block = {
id: string;
Expand Down Expand Up @@ -123,7 +123,10 @@ export function titleOf(pages: Pages, title: string): string {
*
* Exported for the property fold, which spells a page the same way.
*/
export function spell(titles: Pages["titles"], names: string[]): Pages["titles"] {
export function spell(
titles: Pages["titles"],
names: string[],
): Pages["titles"] {
let next = titles;
for (const name of names) {
const key = canonicalize(name);
Expand Down Expand Up @@ -345,7 +348,10 @@ function onBlock(
if (!found) return state;
const blocks = change(found);
return blocks
? { ...state, blocks: new Map(state.blocks).set(canonicalize(found.page), blocks) }
? {
...state,
blocks: new Map(state.blocks).set(canonicalize(found.page), blocks),
}
: state;
}

Expand Down Expand Up @@ -531,7 +537,8 @@ export function outdented(
* children all over again.
*/
function landed(state: Pages, event: PageEvent): boolean {
if (event.type === `${NAMESPACE}block.created`) return !!find(state, event.payload.id);
if (event.type === `${NAMESPACE}block.created`)
return !!find(state, event.payload.id);
const found = find(state, event.payload.id);
if (event.type === `${NAMESPACE}block.indented`) {
return found?.parent?.id === event.payload.parent;
Expand Down
File renamed without changes.
26 changes: 13 additions & 13 deletions packages/pages/pages.ts → packages/pages/src/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,21 @@ import {
outdented,
reduce,
titleOf,
} from "./blocks.ts";
} from "./blocks/blocks.ts";
import type {
Block,
BlockIndented,
BlockOutdented,
Page,
PageEvent,
Pages,
} from "./blocks.ts";
import { canonicalize } from "./canonicalize.ts";
import { linking, read } from "./reading.ts";
import { PROPERTY_HANDLES, reduceProperties } from "./properties.ts";
import type { PropertyEvent } from "./properties.ts";
} from "./blocks/blocks.ts";
import { canonicalize } from "./canonicalize/canonicalize.ts";
import { linking, read } from "./reading/reading.ts";
import { PROPERTY_HANDLES, reduceProperties } from "./properties/properties.ts";
import type { PropertyEvent } from "./properties/properties.ts";

export { canonicalize } from "./canonicalize.ts";
export { canonicalize } from "./canonicalize/canonicalize.ts";
export {
beside,
blocksOn,
Expand All @@ -51,7 +51,7 @@ export {
reduce,
titleOf,
without,
} from "./blocks.ts";
} from "./blocks/blocks.ts";
export type {
Block,
BlockCreated,
Expand All @@ -63,8 +63,8 @@ export type {
Page,
PageEvent,
Pages,
} from "./blocks.ts";
export { inCode, linking, read } from "./reading.ts";
} from "./blocks/blocks.ts";
export { inCode, linking, read } from "./reading/reading.ts";
export {
children,
get,
Expand All @@ -73,7 +73,7 @@ export {
list,
path,
showPath,
} from "./properties.ts";
} from "./properties/properties.ts";
export type {
Entry,
Properties,
Expand All @@ -86,8 +86,8 @@ export type {
PropertyUnset,
Scalar,
Seen,
} from "./properties.ts";
export { remarkPlugins, remarkWikilink } from "./wikilink.ts";
} from "./properties/properties.ts";
export { remarkPlugins, remarkWikilink } from "./wikilink/wikilink.ts";

/**
* What makes a title a journal day. Whoever writes picks the day, so the fold
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import {
pagesFold,
path,
showPath,
} from "./pages.ts";
import type { Pages } from "./pages.ts";
} from "../pages.ts";
import type { Pages } from "../pages.ts";

const PAGE = "Mira";
const DAY = "2026-10-29";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
* Like `blocks.ts`, nothing here touches a log, a clock or a window.
*/

import { NAMESPACE, spell } from "./blocks.ts";
import { canonicalize } from "./canonicalize.ts";
import { NAMESPACE, spell } from "../blocks/blocks.ts";
import { canonicalize } from "../canonicalize/canonicalize.ts";

/**
* A value or an item. The four types stay distinct: `1`, `"1"`, `true` and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
*/
import { unified } from "unified";
import remarkParse from "remark-parse";
import { canonicalize } from "./canonicalize.ts";
import type { Block } from "./blocks.ts";
import { remarkPlugins } from "./wikilink.ts";
import { canonicalize } from "../canonicalize/canonicalize.ts";
import type { Block } from "../blocks/blocks.ts";
import { remarkPlugins } from "../wikilink/wikilink.ts";

/**
* The pages a text links to and the blocks it refers to, read with the same parser that renders it, so
Expand Down
File renamed without changes.
Loading