A working mockup for Peregrine. Clicking a mini game opens its brief in full; clicking a round opens it for editing where it sits. Real corpus data throughout — all twelve briefs, all 35 Lineup rounds.
New in rev 3. Clicking a game title in the sidebar puts that game’s brief in the main window — every field at full height, nothing folded, clipped, or line-limited. Clicking Peregrine at the top of the sidebar opens the guide brief above it.
Try The Wager and Cold Case. Their divergence notes run 1,224 and 1,208 characters, and Cold Case’s reskin is another 481 — the three longest fields in the guide, and the reason the current 3-line cap loses most of what was written.
pick)break_idx)Eleven of Peregrine’s twelve mini games have an empty bank. Clicking any of them today lands you in a document with nothing in it. Their brief is the only content they have — and it is substantial: a mechanic, a reskin, a subject, a priority triple, and a divergence note that in several cases is the most carefully-written prose in the guide. Making the title click open the brief turns eleven dead ends into the thing you actually wanted to read.
The truncation is real and it’s in three places.
MiniGameViews.swift:167 puts .lineLimit(3) on
reskin — that alone hides two thirds of Cold Case’s. divergence
and What an item needs both sit inside DisclosureGroups that
are stateless and live inside a ForEach, so they collapse again on
any redraw. And the roster card itself is a fixed-height row in a band.
The fix in Swift is three specific things, none of them clever:
drop .lineLimit; put .fixedSize(horizontal: false, vertical: true)
on every prose Text so it takes the height it needs; and in Sculpt use an
auto-growing editor rather than a bare TextEditor, which defaults to a
fixed frame and will scroll a 1,224-character divergence inside a six-line box —
exactly the failure you asked me to avoid.
Two briefs, two doors. The guide brief (premise, role, the admission test, the forbidden vocabulary, the content bar) is guide-level and hangs off the sidebar header. The game brief is per-mini-game and hangs off the title. They’re different documents and shouldn’t compete for the same space — but the forbidden-vocabulary rule in particular is worth being one click away while you’re reviewing copy.
Yes — and it’s already half-coded. DocumentState.beginEditing
(line 160) ends with inspectorShown = true, under a comment reading
“Clicking INTO the artifact is a mode change: inspector opens.” The only thing
in the way is guard isSculpting else { return } on that function’s first line.
But the existing inspector is the wrong vessel for the question itself. Its file header scopes it deliberately: “reduced to what doesn’t belong in prose flow (CO-01 §4).” A stem, a passage, four options and five narration lines are exactly prose flow. Flip to Panel only above and edit option C — it fits, but you’re working in 372px on what you read in 660, in a different layout, with the item greyed beside you.
What I’d build is your idea, split by what the student sees. Click opens the round live where it sits — full width, the layout you were scanning. The panel opens alongside and takes the eight fields the student render deliberately hides. One new mechanism; the panel half already exists.
READING — review posture. One item opens under your cursor when you need it.
SCULPTING — authoring posture. Everything live at once, Tab walks regions,
sessions run across a scope.
| Piece | Size | What’s actually involved |
|---|---|---|
| Accordion sidebar, full lists | small | A String? on DocumentState for the open section, plus a
conditional ForEach in NavigatorView. One wrinkle: collapsing
removes rows from the List, so onMove drag-reorder inside a
collapsed section is unavailable. Behaviour change, not a pure addition. |
| Title click → game brief | small | The title button in MiniGameBandView already sets
state.focusIDs and scrollTarget. This adds a
briefTarget: String? that the document reads to decide which brief to draw
above the bank. The brief view itself is a new MiniGameBriefView — mostly
laying out fields that already decode. |
| Untruncated prose | small | Drop .lineLimit(3); add .fixedSize(horizontal: false, vertical:
true) to prose Text; replace the stateless
DisclosureGroups with always-open sections in the brief. In Sculpt, an
auto-growing text editor — TextEditor alone will scroll long prose inside
a fixed frame. |
| Click-to-engage in Read | small | Relax guard isSculpting in beginEditing, add
engagedEntryID: String? beside editingRegion. Single-cursor
discipline already exists — engaging a second item must commit the first, which
beginEditing already does. Add scroll anchoring on engage. |
| Game-shaped Read layout | medium | A GameItemView beside the itinerary branch in classBody.
Draws stem/passage/options/reasoning bespoke, falls back to
SchemaRegionsView for the rest — the split PlaceCardView
already uses. Risk: two rendering paths over one schema, so a newly-added field lands
in the fallback region rather than the designed one. |
| Option list editing | medium | EntryDraft has stringBinding, intBinding,
stringListBinding and friends but no objectListBinding.
That’s the missing primitive: a binding into the nth object of an array with
insert/remove/move, keeping debounced autosave and commit-level undo intact. Build it
once and the reasoning editor plus Cold Case’s faults /
near_faults come nearly free. |
| Break & pick as gestures | small | One rule enforced in the UI: all three of break_idx/pick/con_id
or none — the validator’s “a round is clean or it is not.” Best as the single
CLEAN/CON control in the mockup, which clears or requires all three together. |
| Editable brief | large-ish | The awkward one. Read/Sculpt is plumbed through
RegionAddress{entryID, path} and EntryDraft — both
per-entry. Everything in the brief lives in guide.json, and there’s
no updateMiniGame on CorpusStore, only
addMiniGame. Either a parallel GuideDraft (honest, bigger) or
a modal sheet extending NewMiniGameSheet (quick, breaks edit-in-place
everywhere else). I’d build the former, in its own pass. Note this now blocks more than
it did — the brief is the main window for eleven of twelve games, so read-only hurts
more here than it did on a roster card. |
It only proves the-lineup. All 35 entries are Lineup rounds.
target-practice is a one-line claim with a verdict and no options at all;
snap-call is two parallel choice lists on a clock. Cold Case will not
fit this item layout — its faults anchor into prose by quoted substring, wanting
inline highlighting over the report rather than a list beneath it. Expect a second
designed layout there. Its brief, though, works fine in this one.
Six of twelve games declare no item fields at all. Their brief says so explicitly rather than showing an empty section — but it means “What an item needs” can’t yet answer the question for half the roster, and that’s a content gap, not a UI one.
Colour. The clean system is deliberately monochrome —
accent, gold and ink are all
#171715. The oxblood on con/break markers and on the unset-mechanic
warning is a proposed token addition, and the one place this mockup exceeds its
brief.
Sculpt sessions. Tab/Shift-Tab walks regions today. What “next region” means inside an option list — next option, or next field of this option — is undecided, and Tab collides with the obvious behaviour inside a table-like editor.
Confidence: 86 / 100. The brief view is the lowest-risk, highest-return piece
in the set: it’s mostly laying out fields that already decode, and it rescues eleven
mini games from being empty documents. Remaining deductions unchanged — making
guide.json editable in place is a different plumbing problem I’ve drawn
the destination for without being certain of the route, and Cold Case is a known seam
in an item design that otherwise covers eleven of twelve families.