InstantPage list checkboxes (task-list style): parse, serialize, render

Add a first-class `checked: Bool?` to InstantPageListItem (orthogonal to the
ordered-list `num`), replacing the prior sentinel-string-in-num prototype.

- Data model: enum third associated value on .text/.blocks; Postbox + a new
  FlatBuffers `checkState` tri-state field (0=nil,1=unchecked,2=checked,
  backward-compatible); Equatable.
- Transmission: read/write the native API `checkbox`=flags.0 / `checked`=flags.1
  bits on all four PageListItem / PageListOrderedItem constructors, so checkbox
  state survives the server round-trip for sender and recipients.
- Markdown: forward parser routes detected `[ ]`/`[x]` into `checked` (keeping
  the real number for ordered items); reverse converter emits `- [ ]`/`- [x]`
  so editing a rich message round-trips.
- Display: V1 + V2 layout detect via `item.checked`; the V2 renderer draws real
  CheckNode artwork (was a placeholder); preview text shows a checkbox glyph
  instead of leaking the old sentinel.

Spec: docs/superpowers/specs/2026-05-27-instantpage-list-checkbox-design.md
Plan: docs/superpowers/plans/2026-05-27-instantpage-list-checkbox.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
isaac 2026-05-27 16:44:47 +02:00
parent ebe37cc714
commit 81e3be1923
12 changed files with 198 additions and 140 deletions

View file

@ -57,13 +57,16 @@ extension InstantPageListItem {
switch self {
case .unknown:
return ""
case let .text(text, num):
if let num, !num.isEmpty {
return "\(num). \(text.previewText())"
case let .text(text, num, checked):
let body = text.previewText()
if let checked {
return "\(checked ? "☑︎" : "") \(body)"
} else if let num, !num.isEmpty {
return "\(num). \(body)"
} else {
return text.previewText()
return body
}
case let .blocks(blocks, num):
case let .blocks(blocks, num, checked):
var blocksText = ""
for block in blocks {
if !blocksText.isEmpty {
@ -71,7 +74,9 @@ extension InstantPageListItem {
}
blocksText.append(block.previewText())
}
if let num {
if let checked {
return "\(checked ? "☑︎" : "") \(blocksText)"
} else if let num {
return "\(num). \(blocksText)"
} else {
return blocksText