Skip to content

Split pages into slices; block fold parses before it applies - #154

Merged
shovon merged 1 commit into
mainfrom
pages-bunnyhug-cleanup
Oct 30, 2026
Merged

shovon merged 1 commit into
mainfrom
pages-bunnyhug-cleanup

Conversation

@shovon

@shovon shovon commented Oct 30, 2026

Copy link
Copy Markdown
Owner

Cleans up the inside of @pbnotes/pages along the lines of the bunnyhug skill. The public interface is unchanged: pages.ts exports every name it did before.

Structure

  • pages.ts is wiring only (514 → 155 lines): Pages, HANDLES, pagesFold, PagesView, getPages (the one read that needs both blocks and properties), and the re-exports.
  • The block slice is split in three:
    • blocks/blocks.ts: schema, tree operations, the fold.
    • blocks/queries.ts: the reads. linking moves here from reading, which breaks the blocks ↔ reading import cycle.
    • blocks/commands.ts: the event builders and the commands.
  • Shared pieces get their own modules, so properties no longer imports blocks:
    • events/: NAMESPACE, Named, payload helpers.
    • titles/: spell, titleOf.
    • journal/: DATE_PATTERN, today.
  • The block fold works on any state with blocks and titles, so blocks.ts no longer knows properties exist. PagesView is the commands' BlocksView plus properties.
  • Less repetition: the builders share build(), and commands on existing blocks share onExisting().

Behaviour change

The block fold now checks each event before applying it, like the property fold already did. A malformed block event, which only another program writing to the folder could produce, is skipped whole.

Event Before Now
block.created / block.edited with payload: null Fold throws, so the folder can't open (#62) Skipped
block.created with non-string text Shown, then every query throws Skipped
block.edited with non-string text Text replaced with a non-string Skipped
block.created with no text, no id, or a numeric id Shown, but can't be edited or deleted Skipped (could hide a block that showed before)
block.created with no page Filed under a page called "undefined" Same
block.created with non-string after Added at the end of the page Same

Open question: the "no text / numeric id" blocks could be coerced (String(id), text ?? "") instead of skipped, so they stay visible.

Testing

  • New test in blocks.test.ts pins the malformed-event behaviour. It fails on main and passes here.
  • pages tests: 58 pass. CLI tests pass. The 65 app tests that import @pbnotes/pages pass.
  • tsc --noEmit passes for the app, and for pages on its own under --strict.

🤖 Generated with Claude Code

- pages.ts is now wiring only: Pages, HANDLES, pagesFold, PagesView,
  getPages and the re-exports. The public API is unchanged.
- Block queries and commands move into blocks/queries.ts and
  blocks/commands.ts. linking moves out of reading, which breaks the
  blocks <-> reading cycle.
- Shared pieces get their own modules (events/, titles/, journal/), so
  properties no longer imports blocks, and the block fold works on any
  state with blocks and titles.
- Builders share build(); commands on existing blocks share onExisting().

Behaviour: a malformed block event from another writer is now skipped
whole. Before, a null payload threw in the fold (the folder could not
open), and non-string text broke every query. Blocks with no text or a
missing/non-string id, which used to show but could not be edited, are
now skipped too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@shovon
shovon merged commit f74554e into main Oct 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant