feat(treeView): enhance treeView-beta with icons, bare labels, annotations & highlights#7527
Conversation
…tions, highlights Extends treeView-beta (PR mermaid-js#7396 by @lee-treehouse and @Vikrantpalle) with file-tree-specific features: - Bare (unquoted) labels: src/, index.js, .gitignore - 30+ auto-detected file-type icons (JS, TS, Python, Docker, etc.) - :::class annotations (e.g. :::highlight for yellow background) - icon(name) overrides for custom icons - ## inline descriptions, column-aligned across all nodes - Directory auto-detection via trailing slash - Folder names with spaces (unquoted) - Two-pass rendering for description column alignment - Diagram-scoped icon symbol IDs (no duplicates on multi-diagram pages) - Side-by-side demo page showing input syntax next to rendered output - 26 new parseNodeContent unit tests + 8 new Langium parser tests - 5 new Cypress E2E tests - Comprehensive docs rewrite with all new features Grammar: NODE_CONTENT terminal captures rest-of-line; detailed annotation parsing (:::class, icon(), ##) delegated to TypeScript parseNodeContent(). Closes mermaid-js#7516
…-filetree-enhancements
🦋 Changeset detectedLatest commit: 51cec1f The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
✅ Deploy Preview for mermaid-js ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
| import { resolveIcon } from './icons.js'; | ||
| import type { NodeType } from './types.js'; | ||
|
|
||
| interface ParsedNodeContent { |
There was a problem hiding this comment.
heya @notionparallax, this hand rolled parsing should go in langium instead.
…file - Swap all 30 icon SVG paths to genuine Material Design Icons (Apache 2.0) - Update viewBox from 16x16 to 24x24 to match MDI coordinate system - Remove stale @mdi/js reference from pnpm-lock.yaml
@mermaid-js/examples
mermaid
@mermaid-js/layout-elk
@mermaid-js/layout-tidy-tree
@mermaid-js/mermaid-zenuml
@mermaid-js/parser
@mermaid-js/tiny
commit: |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #7527 +/- ##
==========================================
- Coverage 3.33% 3.31% -0.02%
==========================================
Files 538 538
Lines 56368 56646 +278
Branches 822 823 +1
==========================================
- Hits 1878 1877 -1
- Misses 54490 54769 +279
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
| * Each value is an SVG path `d` attribute for a 16x16 viewBox. | ||
| * Paths were generated by LLM for this project. | ||
| * Each value is an SVG path `d` attribute for a 24×24 viewBox. | ||
| * Icon paths from Material Design Icons by Pictogrammers (Apache 2.0). |
There was a problem hiding this comment.
@notionparallax this comment may help with these spellcheck shenanigans :) #7400 (comment)
There was a problem hiding this comment.
Thanks!
I'm doing battle with the bots at the moment, Ignore everything for the next little while, it's just me thrashing against the CI
There was a problem hiding this comment.
@lee-treehouse , I'm stuck now behind an es lint caching issue, and the screenshot tests running out of credits:
Error: You have reached the maximum screenshot capacity included in your Pro open source xl Plan. Please upgrade your Plan.
Thanks for your input! I'll give it another go next week and maybe I'll come up against some new and exciting errors.
…inals Replace monolithic NODE_CONTENT terminal with structured terminals (QUOTED_NAME, BARE_NAME, CLASS_ANNOTATION, ICON_ANNOTATION, DESC_ANNOTATION) so the grammar handles parsing instead of imperative regex in parser.ts. - Rewrite treeView.langium with 5 structured terminals - Update valueConverter.ts with converters for each terminal - Simplify parser.ts by removing parseNodeContent() entirely - Rewrite parser.spec.ts as integration tests - Update treeView.test.ts for new AST fields Addresses reviewer feedback from @lee-treehouse on PR mermaid-js#7527.
- icons.spec.ts: resolution priority, case sensitivity, edge cases, SVG path coverage - db.spec.ts: tree building, stack-based nesting, config, accessibility - treeViewValueConverter.test.ts: all structured terminal conversions Addresses missing coverage flagged by Codecov.
…feature/treeview-filetree-enhancements
|
The latest updates on your projects. Learn more about Argos notifications ↗︎
|
|
Hi, This is a known ESLint issue where a stale .eslintcache from a previous CI run doesn't match the current config. The fix is to clear the cached .eslintcache on the CI runner — either by: Invalidating the GitHub Actions cache (e.g. bumping the cache key) Could a maintainer clear the CI lint cache when you get a chance? cc @sidharthv96 @aloisklink All other CI checks (build, unit tests, e2e) are passing. As far as I can tell, that linting issue is the only thing that's still upsetting the CI. |
Headless Chrome (used by Argos CI screenshots) does not resolve bare href on <use> elements. Switch to xlink:href to match the convention used by other mermaid diagrams (e.g. sequence diagram actors).
|
@aloisklink, can you check the CI lint cache rules? |
The CI lint actions don't cache I suspect it's due to your change to the |
The root CHANGELOG.md is a git symlink (mode 120000) to ./packages/mermaid/CHANGELOG.md. Commit dbfde57 inadvertently added a trailing newline to the symlink blob via prettier, which on Linux CI creates a broken symlink (the target path includes a literal newline). - Restore the symlink blob to the original 31 bytes (no trailing newline) - Add CHANGELOG.md to .prettierignore so prettier never touches it again
…feature/treeview-filetree-enhancements
|
Thanks @aloisklink, that was the issue. It's passing that test now, it's just the Argos test that's struggling now. Is that a me thing, or is that coming from somewhere else? |
|
@notionparallax I reviewed and approved the argos changes. |
Thank you @ashishjain0512 does that mean that the robo-gauntlet phase is done, and now a person decides if it's a worthwhile addition to the library now? |
ashishjain0512
left a comment
There was a problem hiding this comment.
[sisyphus-bot]
Thanks for the patience on this one, @notionparallax — this has been open three weeks and I know you've been grinding through lint and Argos issues to get it green. The implementation itself is genuinely solid; the structural choices here are exactly what an enhancement to the treeView-beta should look like. Let's get this across the finish line.
What's working well
🎉 Excellent test coverage. Five new/expanded spec files: db.spec.ts (+158), icons.spec.ts (+218), parser.spec.ts (+231), treeViewValueConverter.test.ts (+98), plus 70 lines of Cypress visual regression. That's thorough.
🎉 Clean icon architecture. <defs> + <symbol> + <use> with #tv-icon-${diagramId}-${iconId} scoped IDs is the right SVG pattern — icon definitions aren't duplicated per node, and multiple diagrams on the same page won't collide. injectIconDefs collects only the icons actually referenced in the tree, so unused icons aren't included in the output.
🎉 Grammar-level safety. CLASS_ANNOTATION ([A-Za-z_][\w-]*) and ICON_ANNOTATION ([\w-]*) terminals constrain user input to identifier characters at the lexer level. This is belt-and-braces with the downstream .text()/.attr() safety. The lexer-priority comment in the grammar explaining why annotation terminals must come before INDENTATION is a nice future-proofing touch.
🎉 Icon resolution priority is sensible. Explicit icon() annotation → filename match → extension match → directory → fallback file. Matches the mental model a user will have, and the exact-filename set (Dockerfile, LICENSE, .gitignore, etc.) covers the common repo landmarks.
🎉 Changeset and docs present. .changeset/enhance-treeview-filetree.md and 121 lines of updates to src/docs/syntax/treeView.md. Docs properly live in src/docs/.
Things to address
🟡 [important] — Highlight background color is hardcoded, not themed
packages/mermaid/src/diagrams/treeView/styles.ts:41-45
.treeView-highlight-bg {
fill: rgba(255, 193, 7, 0.15);
stroke: #ffc107;
stroke-width: 1;
}Per project convention, hardcoded colors should flow through theme variables so the highlight renders correctly in dark / forest / neutral themes. Consider adding highlightBg and highlightStroke to TreeViewDiagramStyles:
export interface TreeViewDiagramStyles {
// ...existing
highlightBg?: string;
highlightStroke?: string;
}
// in defaultTreeViewDiagramStyles:
highlightBg: 'rgba(255, 193, 7, 0.15)', // amber in default theme
highlightStroke: '#ffc107',Then consumers can override via themeVariables.treeView. The two other new colors in this PR (iconColor: '#546e7a', descriptionColor: '#6a9955') already follow this pattern correctly — they're defaults that the theme system can override — so this is just extending the same approach to the highlight.
🟢 [nit] — cssClass?.includes('highlight') will match substrings
packages/mermaid/src/diagrams/treeView/renderer.ts:93, 199
if (node.cssClass?.includes('highlight')) { ... }This matches any class name containing highlight as a substring. A user-defined class like my-highlight-custom or pre-highlighted would unintentionally trigger the highlight background rendering. Three options, in order of effort:
- Exact match:
node.cssClass === 'highlight'(simplest, breaks multi-class users) - Word boundary: check
cssClass.split(/\s+/).includes('highlight')(allows composition) - Dedicated
highlightannotation separate fromcssClass(cleanest, more grammar work)
Option 2 is probably the right balance.
🟢 [nit] — parseInt on pre-converted indent value
packages/mermaid/src/diagrams/treeView/parser.ts:12
const level = node.indent ? parseInt(node.indent as unknown as string) : 0;The TreeViewValueConverter.runCustomConverter already converts INDENTATION to input?.length || 0 (a number). The as unknown as string + parseInt here is roundabout. If the Langium type says string | number | undefined, a cleaner path would be typeof node.indent === 'number' ? node.indent : 0. Minor — works correctly as-is.
💡 [suggestion] — Consider sanitizeText on description
packages/mermaid/src/diagrams/treeView/parser.ts:38
DESC_ANNOTATION (##[^\n\r]*) allows arbitrary non-newline text, including < and >. It's rendered via D3 .text() (renderer.ts:187), so no XSS risk — but applying common.sanitizeText at the DB boundary would be defense in depth, consistent with how diagram titles and labels are handled elsewhere. Not required, and the XSS sub-agent confirmed no actual vulnerability.
Security
✅ No XSS or injection issues identified. The sub-agent reviewed all changed files. All icon SVG content is hardcoded from Material Design Icons (Apache 2.0). All user-controllable values (labels, descriptions, annotations) are rendered via D3 .text() or pass through grammar-constrained regex terminals. The <use xlink:href="#tv-icon-..."/> reference is a fragment-only internal reference with a regex-constrained iconId, so no javascript: or data: URI paths exist. Standard DOMPurify pipeline preserved.
Out of scope (FYI, pre-existing)
The existing hardcoded 'black' defaults for labelColor and lineColor (styles.ts:7-8) aren't introduced by this PR. Worth flagging as a follow-up cleanup candidate if a theming pass happens later, but not a blocker here.
Verdict
One 🟡 (highlight color theming) and two nits. Not blocking — the code is structurally sound and the tests are comprehensive. Once the highlight color flows through theme variables (5 minute change), this is ready. Thanks for sticking with it through the CI rough patches!
Self-check
- 🔴 blocking: 0
- 🟡 important: 1
- 🟢 nit: 2
- 💡 suggestion: 1
- 🎉 praise: 5
- Verdict: COMMENT (1 🟡) ✓
…sanitizeText, docs - highlightBg/highlightStroke theme vars replace hardcoded highlight colors - cssClass uses split(/\s+/).includes() to avoid substring false positives - indent uses typeof check instead of parseInt - description text sanitized via sanitizeText(rawDesc, getConfig()) - docs: fix heading, descriptionColor default, add showIcons + highlight vars
…ements' into feature/treeview-filetree-enhancements # Conflicts: # docs/syntax/treeView.md
|
Thanks for the review @ashishjain0512, I think all those issues are now addressed |
|
@notionparallax I see that you have resolved the issues from the review by @ashishjain0512. The failing argos test is not related to this PR. May I ask you to merge the latest changes from develop please, and if all good we can get this PR merged. |
…-filetree-enhancements
|
@pbrolin47 it's merged in, as of now. |
This PR implements some of the features suggested in #7516, and builds upon the excellent work done by @lee-treehouse in #7396.
The original treeView renders quoted labels in an indentation-based tree. This PR adds:
src/instead of"src/").ts,.json,Dockerfile, etc.), overridable viaicon(name)/marks a node as a directory with a folder icon:::classannotations — CSS class support consistent with other Mermaid diagrams (e.g.:::highlightfor a yellow background)##inline descriptions — rendered next to labels in lighter italic text, aligned across the treeicon(none)/icon()suppression — hide the icon on a specific nodeshowIcons: falseconfig — globally disable all icons viatreeView: { showIcons: false }"quoted"syntax continues to work identicallyScreenshots
Basic TreeView (quoted labels)
Bare Labels with Icons
Annotations: :::class, icon()
Folder Names with Spaces
Custom Config
Technical Approach
The Langium grammar's
STRING2terminal was replaced with aNODE_CONTENTterminal that captures the rest-of-line, with detailed parsing delegated to a TypeScriptparseNodeContent()function. This avoids tokenizer conflicts while keeping the grammar clean. The renderer uses a two-pass algorithm to align descriptions across all nodes, and icon<symbol>IDs are scoped todiagramIdto support multiple diagrams on one page.Icons
The 30+ file-type icons in
icons.tsare original SVG paths (16×16 viewBox) generated for this PR — they are not taken from any existing icon set. They are simple geometric representations of file-type concepts (folder shape, curly braces for JSON, etc.) and are contributed under the same MIT license as the rest of the project. If the maintainers would prefer to use an established icon set (e.g. Codicons), that could be a follow-up change.Files Changed (15 files, +1120 / -102)
treeView.langium,valueConverter.tsSTRING2→NODE_CONTENTterminaltypes.tsNodeType,iconId,cssClass,descriptiondb.tsaddNode()with new fieldsparser.tsparseNodeContent()annotation extractoricons.ts(new)resolveIcon()renderer.tsstyles.tsconfig.schema.yaml,config.type.tsshowIconsboolean propertyparser.spec.ts(new),treeView.test.ts,treeView.spec.tstreeView.mdtreeView.htmltree-view.tsenhance-treeview-filetree.mdmermaid+@mermaid-js/parserTest Coverage
parseNodeContent()unit tests (bare labels, quoted labels,:::class,icon(),icon(none),##descriptions, combined, directory detection, spaces)mermaidAPIaccessibility test — ✅📋 Tasks
Make sure you
MERMAID_RELEASE_VERSIONis used for all new features.pnpm changesetand following the prompts. Changesets that add features should beminorand those that fix bugs should bepatch. Please prefix changeset messages withfeat:,fix:, orchore:.