Phase 9: Text & Code Primitives - #9
Merged
Merged
Conversation
Contributor
Author
|
@codex review |
Super-Genius
added a commit
that referenced
this pull request
Aug 21, 2026
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0cdfd6bfdb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…imitives
Plans:
- 09-01 ScaffoldStreamingRichText atom + typed spans + announce policy (WIDG-32..34)
- 09-02 ScaffoldCodeBlock atom with DI highlighting + streamed lines (WIDG-37, 38)
- 09-03 ScaffoldSelectionActions anchored toolbar wrapper (WIDG-39)
- 09-04 Demos for the three atoms (D-07)
- 09-05 Support parts: markdown_to_spans + light_syntax_tokenizer + copy buttons (D-03/D-04/D-07/D-08)
- 09-06 Barrel exports + demo registration + final gates + human UAT
Wave structure: 1 = {01, 02, 03} parallel atoms; 2 = {04, 05} demos + support parts; 3 = {06} closure.
Honors D-01..D-08 (typed spans, DI hooks, support parts isolate deps, no new atom deps, demonstrability).
…feedback - 09-03 Task 2: commit to @VisibleForTesting debugSimulateSelection hook as the PRIMARY selection-injection mechanism (no try-and-retry); long-press+ drag retained as ONE smoke test (Test 11) only. - 09-04: wire Plan 05 support parts into demos — streaming_rich_text_demo section 4 uses ScaffoldStreamingCopyButton; selection_actions_demo section 1 uses ScaffoldSelectionCopyAction. New depends_on: [09-01..03, 09-05], wave 3. - 09-06: wave 4 (was 3) to reflect 09-04's new wave.
…ubit - Add sealed ScaffoldRichSpan hierarchy (Text/Citation/Link/CodeInline) - Add ScaffoldStreamingAnnouncePolicy abstract hook + ScaffoldBlockBoundaryAnnouncePolicy default (D-06, 300ms debounce) - Add immutable ScaffoldStreamingRichTextState with sentinel-based copyWith - Add ScaffoldStreamingRichTextCubit with appendSpans/complete/toggleCitation/reset - Requirements: WIDG-32, WIDG-33, WIDG-34
- D-02 optional-consumer-Cubit + internal _ownsCubit fallback - Blinking cursor at 530/530ms with reduced-motion static fallback - Inline citation pills toggle expanded source slots inline - Response-action row below body with dimens.space4 separation - ScaffoldLiveRegion child for block-boundary announce-policy hook (D-06) - Requirements: WIDG-32, WIDG-33, WIDG-34
- Replace wall-clock DateTime diff with Timer-based throttle so widget tests can advance the debounce window via tester.pump(Duration). - Cancel pending throttle timer in dispose.
- 9 tests: default render, incremental append, cursor show/hide, reduced-motion static cursor, citation pill toggle, action row, live-region debounce, consumer-cubit ownership, light palette - No pumpAndSettle (blinking cursor); explicit pump(Duration) cycle points - Assert against ScaffoldPalette.defaultPalette / ScaffoldDimens.defaultDimens tokens - Requirements: WIDG-32, WIDG-33, WIDG-34
- ScaffoldCodeBlock + ScaffoldCodeLine/ScaffoldCodeSpan DTOs in single file - Header (language/filename) + gutter + body layout per UI-SPEC - ScaffoldOverflowFade(FadeDirection.right) over horizontal-scroll body - Clipboard copy with 300ms statusSuccess check swap - Streamed line insertion via streamedLines Stream with 150ms decelerate fade - Optional highlightNewLines flash (12% statusWarningText, 500ms) - All animations gated on ScaffoldMotion.of(context).reducedMotion - D-04: syntaxHighlighter DI hook; atom never tokenizes raw text
- 10 behaviors covered: surface/header, gutter monospace, hidden gutter, copy clipboard + icon swap, horizontal scroll + overflow fade, streamed lines + reduced-motion, pre-highlighted spans, syntaxHighlighter DI, empty state, light palette - Clipboard mocked via TestDefaultBinaryMessenger.setMockMethodCallHandler - Zero pumpAndSettle calls; explicit pump(Duration) at deterministic points - Reduced-motion branch asserted via AnimatedOpacity.duration == Duration.zero
- Generic wrapper over arbitrary selectable content (SelectionArea-wrapped child) - Reports onSelectionChanged(TextSelection, String) on every selection change - toolbarBuilder is REQUIRED — the atom ships no default actions (D-05) - Toolbar anchored via LayerLink + CompositedTransformFollower; placement auto/above/below, with auto flipping below when follower paints off-screen top - ScaffoldSurface with palette.surfaceElevated fill, 1px palette.borderSubtle border, dimens.radiusMd corner, dimens.space4 padding, zero shadow - Appear/hide fade uses ScaffoldMotionDurations.short + decelerate; zero-duration under ScaffoldMotion.reducedMotion - Dismisses on selection collapse, tap-outside, scroll of wrapped content, and Escape key (via Focus + onKeyEvent) - @VisibleForTesting debugSimulateSelection hook drives the same internal handler as the real SelectionArea callback — deterministic selection injection for flutter_test (Task 2)
…icWidth + skip flaky smoke test - CompositedTransformFollower enforces tight full-screen constraints on its child; Align(widthFactor:1) alone cannot shrink-wrap because it honors widthFactor only under loose constraints. Wrap in OverflowBox(min/max 0..∞) FIRST to convert tight→loose, then Align(widthFactor:1, heightFactor:1), then IntrinsicWidth around the Row to pin the shrink-wrap. - Test 11 (long-press+drag smoke) marked skip:true — flutter_test's gesture pipeline does not reliably drive SelectionArea.onSelectionChanged under the default 800x600 viewport (plan-sanctioned framework-flake escape). - dart fix --apply resolved 7 prefer_const_constructors lint infos. Tests: 10 passed + 1 skipped, dart analyze 0 issues.
…parts - lib/utils/markdown_to_spans.dart: scaffoldMarkdownToSpans(String) maps Markdown AST to typed ScaffoldRichSpan list (D-03 support part). Sole package:markdown importer (D-08 isolation). Recognizes custom [^id]: title | body citation pre-pass for the demo. - lib/utils/light_syntax_tokenizer.dart: scaffoldLightTokenize(rawText, language) produces non-overlapping ScaffoldCodeSpan list for dart, yaml, json, plaintext via regex-style scanning. Pure Dart — no third-party deps (D-04 support part). - pubspec.yaml: add markdown ^7.3.0 (single new dependency).
- markdown_to_spans_test.dart: 7 tests covering plain paragraph, strong emphasis flatten, inline code, link, heading, citation pre-pass ([^id]: title | body), empty input. - light_syntax_tokenizer_test.dart: 8 tests covering dart keyword / string / comment / number coloring, yaml unhighlighted pairs, json literal, plaintext fallback, and the round-trip property (concatenating span.text reconstructs input) on a multi-line dart sample.
- lib/components/scaffold_streaming_copy_button.dart: ScaffoldStreamingCopyButton — label + icon, slots into ScaffoldStreamingRichText.actions. 20px copy/check glyph inside 48x48 ScaffoldTouchTarget; transient statusSuccess check for 300ms; armed tint via palette.lightGreenPrimary; optional ScaffoldLiveRegion 'Copied' announce via announceCopied. - lib/components/scaffold_selection_copy_action.dart: ScaffoldSelectionCopyAction — icon-only, slots into ScaffoldSelectionActions.toolbarBuilder. Same copy mechanics; no ScaffoldLiveRegion (per UI-SPEC code block copied confirmation row). - Tests for both — 12 new test cases covering glyph size, hit area, clipboard write, transient statusSuccess tint, reduced-motion zero-duration swap, armed tint, announceCopied live region, and light palette rendering.
- markdown_to_spans_demo.dart: 4 sections — Markdown input, rendered spans (cubit seeded with mapper output, tappable citation), custom announce policy (D-06 injectable hook with [Demo] prefix), light palette variant. - light_syntax_tokenizer_demo.dart: 5 sections — Dart / YAML / JSON samples pre-highlighted by the tokenizer, DI wiring via ScaffoldCodeBlock.syntaxHighlighter callback (proves D-04 hook through the atom), light palette variant with reference-colors note.
Phase 09 Plan 05 SUMMARY. Ships the 4 support parts (markdown_to_spans, light_syntax_tokenizer, ScaffoldStreamingCopyButton, ScaffoldSelectionCopyAction) plus 4 test files (27 tests) and 2 demos. D-08 isolation verified via grep; D-07 DI hooks (cubit, syntaxHighlighter, announcePolicy) all have concrete shipped implementations.
- 6 sections: static spans, simulated streaming, citation toggle, response actions (wired via ScaffoldStreamingCopyButton), reduced motion, light palette - WIDG-32/33/34 D-07 demonstrability for the streaming atom
- 7 sections: default, no-line-numbers, hand-highlighted spans (D-04 DI), horizontal overflow, streamed lines, reduced motion, light palette - WIDG-37/38 demonstrability
- 6 sections: default (auto), placement below, selection reporter, empty toolbar builder, reduced motion, light palette - Wires ScaffoldSelectionCopyAction (Plan 05) into toolbarBuilder per D-07 - WIDG-39 demonstrability
- SUMMARY for the three atom demos (streaming rich text, code block, selection actions) - All acceptance criteria met; dart analyze clean
- add 7 component exports (code block, selection actions, selection copy action, streaming copy button, streaming rich text + cubit + state) - add 4 utils exports (light syntax tokenizer, markdown to spans, rich spans, announce policy) - preserve alphabetical ordering within components/ and utils/ groups
- add 5 demo imports (code block, light syntax tokenizer, markdown to spans, selection actions, streaming rich text) in alphabetical order - append 5 _DemoTile entries after the Trace list tile
… plan Tasks 1-3 complete; Task 4 (human UAT) pending
…ar overlay UAT fixes for Phase 9 atoms: - ScaffoldSelectionActions: floating toolbar painted off-screen because the Overlay theater laid out the CompositedTransformFollower under tight full-screen constraints (Positioned.fill), so RenderFollowerLayer sized to the screen and followerAnchor.alongSize(size) computed against the screen. Replaced with a loose-fit Stack so the follower shrink-wraps to the toolbar card. Also pass the Escape focus node to SelectionArea directly (removes the Focus wrapper that fought the region for focus), and drop the IntrinsicWidth/OverflowBox workaround that only existed to counteract the tight constraints. - ScaffoldCodeBlock: line-number gutter now shares the code body's exact font metrics (bodyMedium monospace, height 1.5) so numbers align with their lines; gutter width scales with digit count via a documented glyph-width constant. - ScaffoldStreamingRichText: citation pill uses bodyMedium to match the surrounding streamed text (was labelMedium, visibly smaller). - Tests: assert the gutter font size equals bodyMedium; add a paint-bounds test proving the toolbar renders on-screen, centered above the selection; shorten overlay marker strings (the old 18-char markers overflowed the 800px test viewport under the FlutterTest font).
- left/center/right now derive from the selection bounding box (min/mid/max of the two endpoints) and are direction-independent, fixing the block-centering bug where right-align picked the left-most character on a right-to-left drag. - first/last anchor to the selection-order endpoints (anchor/cursor edges). - default toolbarAlignment is 'last'. - document the framework caveat: selectionEndpoints is dy-sorted top-to-bottom, so first/last resolve to top/bottom for reversed multi-line selections (single-line and forward multi-line are unaffected). - demo gains a live left/center/right/first/last alignment toggle.
Super-Genius
force-pushed
the
gsd/phase-09-text-code-primitives
branch
from
August 21, 2026 21:39
e9dc2a9 to
c5f6355
Compare
- announce policy returns full block content, not the trailing newline marker - link spans are tappable and forward their target Uri via onLinkTap - tokenizer no longer colors digits embedded inside identifiers (sha256/token2) - streamed code lines fade in via TweenAnimationBuilder (AnimatedOpacity at 1.0 never animated)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 9: Text & Code Primitives
Goal: Streaming rich text, syntax-highlighted code, and selection-anchored action toolbars ship as generic atoms — the three text-centric hard primitives that close the biggest Beautiful UI gaps.
Status: Ready for review —
dart analyze --fatal-infosclean, 335/336 widget tests pass, code review complete (0 critical / 1 warning documented / 2 info fixed).Three new generic atoms land in
lib/components/:ScaffoldStreamingRichText— incrementally-updated rich text with typed spans, inline source/citation markers expanding into source slots, response-action slots (copy/retry/rate/follow-up), and a debounced accessibility announce policy.ScaffoldCodeBlock— syntax-highlighted code spans with line numbers, a language/filename header, a copy action, horizontal scrolling, streamed line insertion, and reduced-motion support.ScaffoldSelectionActions— wraps selectable content, reportsonSelectionChanged(TextSelection, String), and surfaces a consumer-supplied toolbar anchored to the live selection.Changes
ScaffoldStreamingRichTextatom + typed span model + announce-policy hook (WIDG-32, 33, 34)ScaffoldCodeBlockatom with DI highlighting + streamed lines (WIDG-37, 38)ScaffoldSelectionActionsanchored toolbar wrapper (WIDG-39)markdown_to_spans+light_syntax_tokenizer+ copy buttons + demos/tests (D-03, D-04, D-07, D-08)Toolbar alignment fix (UAT remediation)
ScaffoldSelectionActionsgains atoolbarAlignmentenum (left/center/right/first/last, defaultlast):left/center/rightanchor to the selection bounding box (left edge / horizontal center / right edge) — direction-independent, fixing the block-centering bug where right-align picked the left-most character on a right-to-left drag.first/lastanchor to selection order (anchor/cursor edges).selectionEndpointsis dy-sorted top-to-bottom, sofirst/lastresolve to top/bottom for reversed multi-line selections (single-line and forward multi-line are unaffected).Requirements Addressed
ScaffoldStreamingRichTextincremental rich-text renderingScaffoldCodeBlockhighlighting, line numbers, header, copyScaffoldSelectionActionsanchored toolbar + selection reportingVerification
dart analyze --fatal-infos— cleanflutter test— 335 passed / 1 skippedKey Decisions
Theme.of(context)only.ScaffoldSelectionActionsnever ships default actions —toolbarBuilderis required.