feat: Add hide completed tasks filter#3
Draft
DisciplinedSoftware wants to merge 15 commits intofeature/node-completion-statusfrom
Draft
feat: Add hide completed tasks filter#3DisciplinedSoftware wants to merge 15 commits intofeature/node-completion-statusfrom
DisciplinedSoftware wants to merge 15 commits intofeature/node-completion-statusfrom
Conversation
added 15 commits
March 23, 2026 22:45
- Add 🙈 toolbar toggle button that hides/shows all completed nodes and their entire subtrees - Completed node + descendant DOM wrappers get .mm-node-hidden (display: none) when the filter is active; the class is removed when the filter is toggled off - For level-1 nodes the me-wrapper is hidden (no blank connector stub left behind); for deeper nodes me-parent is hidden - applyFilter() is called from applyAllStatuses() so the filter stays consistent after every status change or layout refresh - Button shows an active/pressed style (.mm-btn-active) while the filter is on - Added help-text node to the default diagram describing the button
applyFilter and getHideTargetEl were defined inside initMindMap(), so they were out of scope when the button handler in setupUI() called scheduleApplyAllStatuses() — causing a silent ReferenceError that prevented any hiding from happening. Fixes: - Move getHideTargetEl and applyFilter to module level (accessible from anywhere in the script) - Simplify getHideTargetEl: use document.getElementById(id) directly to get me-parent, then check if its parentElement is me-wrapper (one straightforward traversal, no intermediate me-tpc lookup) - In the button click handler call applyFilter() directly instead of scheduleApplyAllStatuses(), which was out of scope
document.getElementById(id) always returned null because MindElixir does not use the node id as the HTML element id. Instead it renders nodes as me-parent[data-nodeid="me<id>"]. Use querySelector with the correct attribute selector.
The JS template literal inside the TS template string caused a parse error. Use string concatenation instead.
When a node was hidden with display:none, the SVG connector lines
drawn by MindElixir remained visible because they are rendered on
separate SVG overlay elements, not inside the node's own DOM subtree.
Fix:
- After hiding/showing me-wrapper elements, applyFilter() now also
syncs SVG path visibility:
Main branches (root → level-1):
The .lines SVG has one <path> per me-main>me-wrapper in DOM order.
Paths for hidden wrappers are set to display:none.
Sub-branches (level-2+):
Each level-1 me-wrapper owns a subLines SVG (its last child) that
holds ALL branch paths for the entire subtree, in the same DFS
pre-order used by MindElixir's internal Ie() function.
collectSubLineOrder() mirrors that traversal so paths[i] maps
to nodesInOrder[i], and paths for hidden nodes are hidden.
- getHideTargetEl() simplified: every me-parent[data-nodeid] is
always the first child of its own me-wrapper, so parentElement
is always me-wrapper regardless of depth.
- Button click also calls mind.linkDiv() so the layout recompacts
around hidden nodes; the debounced applyAllStatuses → applyFilter
then cleans up any stale paths drawn by that linkDiv pass.
getHideTargetEl was querying [data-nodeid] which MindElixir sets on
me-tpc, not me-parent. So .parentElement returned me-parent, while
the SVG path-hiding code checks classList on me-wrapper — a mismatch
that meant mm-node-hidden was never found on the queried wrappers.
Fix: go up two levels (me-tpc → me-parent → me-wrapper) so the class
lands on me-wrapper, matching what querySelectorAll('me-main>me-wrapper')
returns for path index correlation.
Also reorder button handler: call mind.linkDiv() before applyFilter()
so paths are hidden after linkDiv redraws them, not before.
Calling mind.linkDiv() while nodes are display:none causes linkDiv to query offsetLeft/offsetTop of hidden elements (which return 0), drawing all their SVG paths to (0,0). This permanently corrupts the layout even after filtering is turned off. Fix: just call applyFilter() — the existing paths drawn in the correct full layout are hidden/shown in place. No relayout, no corruption.
- Use mind.lines and mind.map.querySelectorAll (same references linkDiv
uses internally) instead of document.querySelector/querySelectorAll.
- Use subSvg.querySelectorAll('path') instead of .children so only
<path> elements are counted (avoids any stray non-path children).
- Set both setAttribute('display') and style.display for maximum SVG
compatibility with Chromium's rendering of SVG presentation attrs.
display:none removes hidden nodes from layout flow, causing sibling nodes to shift position. SVG paths (drawn at fixed coordinates by linkDiv) then point to the old positions of the shifted nodes, causing the visible branches and labels to appear misaligned. visibility:hidden keeps all nodes in layout (no shift), so SVG path coordinates remain correct for all visible nodes. The hidden nodes are invisible but still occupy their original space, eliminating misalignment.
Restore hideCompleted from vscode.getState() before initMindMap() so the filter is active immediately on load (applyAllStatuses -> applyFilter picks it up). Save the new value to vscode.setState() on every toggle, spreading any other existing state keys so nothing is overwritten.
… close) vscode.getState/setState only persists while the webview is alive (hide/show). When the panel is closed and reopened, a new webview is created and getState() returns null, so the previous approach lost the setting. Fix: store hideCompleted in ExtensionContext.workspaceState on the extension host, which survives panel close/reopen and VS Code restarts (workspace-scoped). - _getHtmlForWebview reads workspaceState and injects the value directly into the script as 'let hideCompleted = <true|false>' - no messaging or timing issues, the correct value is present from the very first applyFilter() call. - On toggle, webview posts setHideCompleted to extension which persists it.
… unfiltered content scheduleApplyAllStatuses() defers via RAF + 50ms, causing a visible frame of the full unfiltered graph before filtering is applied. Calling applyFilter() immediately after mind.init() runs before the first paint, so hidden nodes are never visible to the user when hideCompleted is active.
…iltered content mind.refresh() calls linkDiv() which fires the linkDiv bus event, but our listener debounces it by 50ms. During that window the full unfiltered diagram is visible. Calling applyFilter() immediately after mind.refresh() hides completed nodes before the browser paints the imported data.
…nfiltered content Start #container with opacity:0. After mind.init() or mind.refresh() calls applyFilter() synchronously, set opacity:1 with a 150ms ease-in transition. The filter is applied while the diagram is invisible, so completed nodes are never seen by the user regardless of load timing.
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.
Overview
This stacked PR adds a filter to hide completed tasks and their descendants on top of the node completion status feature.
Stack
Base branch: feature/node-completion-status
Related upstream PR: OlegIGalkin#14
Changes
Testing