Skip to content

feat(treeView): enhance treeView-beta with icons, bare labels, annotations & highlights#7527

Open
notionparallax wants to merge 17 commits intomermaid-js:developfrom
notionparallax:feature/treeview-filetree-enhancements
Open

feat(treeView): enhance treeView-beta with icons, bare labels, annotations & highlights#7527
notionparallax wants to merge 17 commits intomermaid-js:developfrom
notionparallax:feature/treeview-filetree-enhancements

Conversation

@notionparallax
Copy link
Copy Markdown

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:

  • Bare labels — node names no longer require quotes (src/ instead of "src/")
  • File-type icons — 30+ SVG icons auto-detected from filename/extension (.ts, .json, Dockerfile, etc.), overridable via icon(name)
  • Directory detection — trailing / marks a node as a directory with a folder icon
  • :::class annotations — CSS class support consistent with other Mermaid diagrams (e.g. :::highlight for a yellow background)
  • ## inline descriptions — rendered next to labels in lighter italic text, aligned across the tree
  • icon(none) / icon() suppression — hide the icon on a specific node
  • showIcons: false config — globally disable all icons via treeView: { showIcons: false }
  • Backwards compatible — all existing "quoted" syntax continues to work identically

Screenshots

Basic TreeView (quoted labels)

Basic TreeView (quoted labels)

Bare Labels with Icons

Bare Labels with Icons

Annotations: :::class, icon()

Annotations: :::class, icon(), ##

Folder Names with Spaces

Folder Names with Spaces

Custom Config

Custom Config

Technical Approach

The Langium grammar's STRING2 terminal was replaced with a NODE_CONTENT terminal that captures the rest-of-line, with detailed parsing delegated to a TypeScript parseNodeContent() 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 to diagramId to support multiple diagrams on one page.

Icons

The 30+ file-type icons in icons.ts are 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)

Area Files What changed
Grammar treeView.langium, valueConverter.ts STRING2NODE_CONTENT terminal
Types types.ts Added NodeType, iconId, cssClass, description
Database db.ts Extended addNode() with new fields
Parser parser.ts Added parseNodeContent() annotation extractor
Icons icons.ts (new) 30+ SVG icon paths, resolveIcon()
Renderer renderer.ts Two-pass layout, icons, highlights, description alignment
Styles styles.ts Description, icon, highlight CSS classes
Config config.schema.yaml, config.type.ts Added showIcons boolean property
Tests parser.spec.ts (new), treeView.test.ts, treeView.spec.ts 30 + 13 + 9 = 52 new test cases
Docs treeView.md Comprehensive rewrite with all features
Demo treeView.html Side-by-side source + rendered output
Examples tree-view.ts 2 new example entries
Changeset enhance-treeview-filetree.md Minor bump for mermaid + @mermaid-js/parser

Test Coverage

  • 30 parseNodeContent() unit tests (bare labels, quoted labels, :::class, icon(), icon(none), ## descriptions, combined, directory detection, spaces)
  • 13 Langium parser integration tests
  • 9 Cypress E2E snapshot tests
  • All existing tests pass across all test suites
  • mermaidAPI accessibility test — ✅
  • Multi-diagram ID uniqueness test — ✅
  • Styles integration test — ✅

📋 Tasks

Make sure you

  • 📖 have read the contribution guidelines
  • 💻 have added necessary unit/e2e tests.
  • 📓 have added documentation. Make sure MERMAID_RELEASE_VERSION is used for all new features.
  • 🦋 If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running pnpm changeset and following the prompts. Changesets that add features should be minor and those that fix bugs should be patch. Please prefix changeset messages with feat:, fix:, or chore:.

…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
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 26, 2026

🦋 Changeset detected

Latest commit: 51cec1f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
mermaid Minor
@mermaid-js/parser Minor

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

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 26, 2026

Deploy Preview for mermaid-js ready!

Name Link
🔨 Latest commit 51cec1f
🔍 Latest deploy log https://app.netlify.com/projects/mermaid-js/deploys/69e16e690c2a1100083b31a0
😎 Deploy Preview https://deploy-preview-7527--mermaid-js.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions bot added the Type: Enhancement New feature or request label Mar 26, 2026
import { resolveIcon } from './icons.js';
import type { NodeType } from './types.js';

interface ParsedNodeContent {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 26, 2026

Open in StackBlitz

@mermaid-js/examples

npm i https://pkg.pr.new/@mermaid-js/examples@7527

mermaid

npm i https://pkg.pr.new/mermaid@7527

@mermaid-js/layout-elk

npm i https://pkg.pr.new/@mermaid-js/layout-elk@7527

@mermaid-js/layout-tidy-tree

npm i https://pkg.pr.new/@mermaid-js/layout-tidy-tree@7527

@mermaid-js/mermaid-zenuml

npm i https://pkg.pr.new/@mermaid-js/mermaid-zenuml@7527

@mermaid-js/parser

npm i https://pkg.pr.new/@mermaid-js/parser@7527

@mermaid-js/tiny

npm i https://pkg.pr.new/@mermaid-js/tiny@7527

commit: 51cec1f

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 26, 2026

Codecov Report

❌ Patch coverage is 0.94637% with 314 lines in your changes missing coverage. Please review.
✅ Project coverage is 3.31%. Comparing base (8b69f3c) to head (51cec1f).

Files with missing lines Patch % Lines
packages/mermaid/src/diagrams/treeView/icons.ts 0.74% 134 Missing ⚠️
packages/mermaid/src/diagrams/treeView/renderer.ts 0.99% 100 Missing ⚠️
packages/mermaid/src/diagrams/treeView/parser.ts 0.00% 24 Missing ⚠️
packages/mermaid/src/diagrams/treeView/styles.ts 0.00% 18 Missing ⚠️
packages/mermaid/src/diagrams/treeView/db.ts 0.00% 16 Missing ⚠️
...ges/parser/src/language/treeView/valueConverter.ts 0.00% 14 Missing ⚠️
packages/examples/src/examples/tree-view.ts 0.00% 8 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            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     
Flag Coverage Δ
unit 3.31% <0.94%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/mermaid/src/config.type.ts 100.00% <ø> (ø)
packages/mermaid/src/diagrams/treeView/types.ts 100.00% <100.00%> (ø)
packages/examples/src/examples/tree-view.ts 5.55% <0.00%> (-4.45%) ⬇️
...ges/parser/src/language/treeView/valueConverter.ts 3.70% <0.00%> (-3.44%) ⬇️
packages/mermaid/src/diagrams/treeView/db.ts 0.00% <0.00%> (-1.93%) ⬇️
packages/mermaid/src/diagrams/treeView/styles.ts 3.03% <0.00%> (-2.53%) ⬇️
packages/mermaid/src/diagrams/treeView/parser.ts 0.00% <0.00%> (-6.67%) ⬇️
packages/mermaid/src/diagrams/treeView/renderer.ts 0.52% <0.99%> (-0.43%) ⬇️
packages/mermaid/src/diagrams/treeView/icons.ts 0.74% <0.74%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

* 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).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@notionparallax this comment may help with these spellcheck shenanigans :) #7400 (comment)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.
@argos-ci
Copy link
Copy Markdown

argos-ci bot commented Apr 2, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ⚠️ Changes detected (Review) 1 changed, 9 added, 4 removed Apr 16, 2026, 11:31 PM

@notionparallax
Copy link
Copy Markdown
Author

Hi,
The lint and lint:fix steps are both failing with the same ESLint 9.35.0 cache corruption error — not related to any code changes in this PR:

TypeError: Cannot read properties of undefined (reading 'hashOfConfig') at LintResultCache.getCachedLintResults (eslint/lib/cli-engine/lint-result-cache.js:116:24)

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)
Adding rm -f .eslintcache before the lint step
Or deleting the relevant cache entry from the repo's Actions cache page (Settings → Actions → Caches)

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).
@ashishjain0512
Copy link
Copy Markdown
Collaborator

@aloisklink, can you check the CI lint cache rules?

@aloisklink
Copy link
Copy Markdown
Member

@aloisklink, can you check the CI lint cache rules?

The CI lint actions don't cache .eslintcache. And when I tried running a pnpm run lint on a fresh new machine locally, I also got the same error.

I suspect it's due to your change to the CHANGELOG.md file, which is now a broken symlink, since notionparallax@dbfde57

notionparallax and others added 4 commits April 15, 2026 09:49
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
@notionparallax
Copy link
Copy Markdown
Author

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?

@ashishjain0512
Copy link
Copy Markdown
Collaborator

@notionparallax I reviewed and approved the argos changes.

@notionparallax
Copy link
Copy Markdown
Author

@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?

Copy link
Copy Markdown
Collaborator

@ashishjain0512 ashishjain0512 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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:

  1. Exact match: node.cssClass === 'highlight' (simplest, breaks multi-class users)
  2. Word boundary: check cssClass.split(/\s+/).includes('highlight') (allows composition)
  3. Dedicated highlight annotation separate from cssClass (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
@notionparallax
Copy link
Copy Markdown
Author

Thanks for the review @ashishjain0512, I think all those issues are now addressed

@pbrolin47
Copy link
Copy Markdown
Collaborator

@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.

@notionparallax
Copy link
Copy Markdown
Author

@pbrolin47 it's merged in, as of now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants