feat(mix_markdown): add Markdown renderer styled with Mix - #1056
Open
leoafarias wants to merge 9 commits into
Open
leoafarias wants to merge 9 commits into
leoafarias wants to merge 9 commits into
Conversation
…den types The widget-call visibility check recursed into a type alias's underlying function type, so a visible typedef whose parameters name types the spec library does not import was rejected. Generated code only ever writes the alias name, so the aliased declaration's parts do not need to be visible. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv
Adds an experimental package that parses a Markdown document once and renders paragraphs, headings, nested inline formatting, and GitHub alerts through generated Specs and Stylers. - MixMarkdown is a StyleWidget over a generated MarkdownSpec, so every text slot is a TextStyler with tokens, variants, modifiers, and animation. - Alerts use the markdown package's own GitHub alert syntax and resolve per-type MarkdownAlertTypeSpec slots. - Style, theme, and text-scale updates reuse the parsed document; only new data or syntax parses again. - Documents with unsupported blocks go to an unsupportedBuilder or show a diagnostic instead of being flattened. - Registers the package and its example with melos and the mix skill. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv
The markdown parser keeps a soft line break as a newline inside a text node, so wrapped source lines rendered as hard breaks. Outside inline code those newlines now become spaces; hard breaks still arrive as `br` elements and raw `<br>` tags still become line breaks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv
- Version 0.0.1-beta.0 with a pub.dev description, topics, and light and dark screenshots of the example. - Type the `style` parameter as `MarkdownStyler` so dot shorthand works at call sites, matching mix_chart. - Add the publish-contract and public-API tests used by mix_chart. - Add an example README and list mix_chart and mix_markdown in the root README package table. - Add the mix_markdown tag pattern and job to the publish workflow. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv
- Move the unsupported-node scan to `parsing/unsupported_nodes.dart`; it is a pure AST check with no widget dependency. Its test moves with it. - Move `MarkdownBlocks` to its own widget file so the alert widget no longer imports the document state to reach it. - Keep the example's document style private. - List every package in the AGENTS.md structure block and add the mix_markdown README to llms.txt. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv
The cache class and the state both tracked the parsed nodes and compared sources, so the state now owns the parse directly. Parser-level tests move to the syntax test; parse counts stay covered by the widget tests, which now also assert that a new syntax changes the rendered output. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv
The Markdown tag vocabulary was spelled out as bare strings in four places
that had to be kept in sync by hand: the eligibility sets, the heading
style switch, the inline style switch, and a `startsWith('h')` test for
heading semantics that was also true for `hr`.
MarkdownBlockTag and MarkdownInlineTag now hold that vocabulary once. Both
switches are exhaustive over the enum, so adding a tag fails compilation at
every site that has to handle it. The public API is unchanged: the
unsupported-node scan still reports raw tag names.
Also drops a test that asserted the barrel exports the same six paths it
lists, and replaces two `isA` assertions that were true by construction
with checks on the resolved spec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv
4 tasks
This branch has not been deployed
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.
Related issue
Not linked to an open issue.
Description
Adds
mix_markdown, a new package that renders Markdown through generated Mix Specs and Stylers, and fixes onemix_generatorbug that the package surfaced.MixMarkdownis aStyleWidget<MarkdownSpec>, so every text slot is aTextStylerand inherits the rest of Mix: tokens,onDarkand other variants, widget modifiers, animation, and the generatedcall(). It parses a document once and reuses the parsed nodes across style, theme, and text-scale changes.The supported subset is paragraphs,
h1–h6, nested inline formatting, and GitHub alerts (including nested alerts and shared link references). Links are styled but not activated, and inline images contribute their alt text. A document containing any other block goes tounsupportedBuilder, or shows a diagnostic, rather than being silently flattened.The package started from an external handoff bundle that had never compiled. It was rebuilt against this repo's conventions rather than imported: the copied alert syntax was dropped in favour of the
markdownpackage's ownAlertBlockSyntax, the handwritten style classes andProbeSpecaliases were replaced with generated Specs and Stylers, and the SuperDeck-specific hero handling, Python validation tooling, and review documents were left out.Changes
New package
packages/mix_markdown(version0.0.1-beta.0)MixMarkdownwidget withsyntax,wrapBlock, andunsupportedBuilderhooks.MarkdownSpec,MarkdownAlertSpec, andMarkdownAlertTypeSpec, with a slot per block, inline run, and alert type.MarkdownSyntaxvalue object for parser configuration; equal configurations do not trigger a reparse.lib/src/parsing/, unit tested separately from the widgets.mix_chart.mix_generatorfixfirstInvisibleTypeNamerecursed into a type alias's expansion, so a visible typedef whose parameters name types the annotated library does not import was rejected. Generated code only ever writes the alias name, so the expansion does not need to be visible. Added a regression test.Wiring and docs
melos.yaml: register the package and its example underflutter_projects..github/workflows/publish.yml: add themix_markdown-v*tag pattern and publish job.README.md,AGENTS.md,llms.txt, and themixskill: list the package (the structure block inAGENTS.mdnow lists every package, not just the original four).Review Checklist
btwld/mix-docslives outside this repo.Additional Information (optional)
Verification on the current head (branch is merged up to date with
main):melos run format:checkmelos run analyze:dartmix_markdowntestsmix_markdown/exampletestmix_generatortestsdart pub publish --dry-runpanacwebp, no network for URL checks, shared lint file excluded from the archive by.pubignore, exactly asmix_chartdoes)DCM could not run in the environment used to prepare this, since it has no licence there. CI skips it too, so its rules were applied by hand (member ordering, newline-before-return, named booleans).
Where a reviewer should start:
lib/src/specs/markdown_spec.dartfor the public style contract. The main open design question is whether the slot set is the right one, particularly that inline slots contribute only their resolvedTextStyle(inline runs are spans, not widgets, so modifiers and animation cannot apply to them).lib/src/widgets/markdown_document.dartfor the parse lifecycle. The state parses ininitStateanddidUpdateWidgetonly, never inbuild, which is what keeps style changes free. Parse counts are asserted in the widget tests.lib/src/parsing/unsupported_nodes.dartfor the subset boundary. Worth checking the policy is the one the project wants: a paragraph holding only an image reportsstandalone-imagerather than rendering alt text, and unsupported blocks anywhere, including inside an alert, reject the whole document.packages/mix_generator/lib/src/core/helpers/library_scope.dartfor the one-line behavioural change outside the new package.Decisions worth a second opinion:
0.0.1-beta.0with publish metadata, matching howmix_chartentered the repo. Say if this should ship withpublish_to: noneuntil the API settles.mix: ^2.2.0-beta.5andmix_annotations: ^2.2.0-beta.1, followingmix_chartin not tracking every release bump.paragraph, andblockSpacingdefaults to zero. No theme-derived typography defaults, matching Mix's explicit-style approach.🤖 Generated with Claude Code
https://claude.ai/code/session_01QnNfDRXutaME1C2FAGBYKv