diff --git a/CHANGELOG.md b/CHANGELOG.md index 741ce797..b440583f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,631 @@ static analyzer; their figures are historical and are not rewritten. Current truth lives in [docs/STATUS.md](docs/STATUS.md) and [docs/VALIDATION.md](docs/VALIDATION.md). +## Unreleased — viewer M3: review walk and the way back + +The viewer's third milestone: the review walk, which goes +through the diagram claim by claim and opens each one's cited lines beside it +(roadmap step 11), the host side of that walk (step 12), a fix for Escape, a +look at the cost of jumping through a large notebook, the phase overview +with the labelled phase index that replaced the minimap (step 13), and +**Reveal in Diagram**, the way back from a cited line in the editor to the +claim in the diagram (step 14), the fixes from an independent review of +those steps, the viewer's fixes from a live check of the milestone, and what +was behind that check's notebook focus loss, with one host fix for the walk, +and the corrections from a review of those fixes (all at the end of this +section). Steps 11 to 14 were checked by local jsdom and mock `vscode` tests +(mutation-checked against the fixes) and in an isolated VS Code 1.139 +Extension Development Host on macOS driven over the DevTools protocol (step 13 +there only in the milestone's two live checks; its own checks were jsdom tests +and headless-Chrome screenshots of a simulated host). Not tried on Windows +or Linux, with a screen reader or as a usability check. No contract change, no +new setting, no geometry change, and the version is unchanged. Step 14 adds +the extension's second command, with no default keybinding. + +The review walk (step 11): +- Press `r` on the diagram, **Review** in the header or **Review the claims** + in the **⋯** menu to go through the displayed revision claim by claim. The + order is the diagram's: phase by phase, each step, then its outgoing + connections, then the findings whose first cited step it is. A finding that + cites no step (only connections, or the workflow as a whole) has no place + in that order; the roadmap does not say where it goes, so these come last, + in document order. Every step, connection and finding is visited exactly + once: 79 claims (31 + 41 + 7) in the vit-cc shakedown artifact and 176 + (59 + 113 + 4) in yolov5-cc2, counted by the walk's own order on both + documents and pinned by synthetic test documents of the same shape. +- The walk starts on **Not observed**, the claims marked inferred or + unresolved: 7 in vit-cc and 16 in yolov5-cc2, the same numbers as the + header's **N not observed**. The other filters are **Findings**, **All + claims** and, only when VS Code reports changed or missing cited files, + **Changed files**. Each shows its count; a filter with nothing in it is not offered. +- Keys while walking: `j` / `k` or ↓ / ↑ for the next or previous claim, `[` + / `]` for the claim's quotes, Enter to open the current quote again + (Alt+Enter also moves the focus to the editor), `n` / `p` for the next or + previous finding in the walk's order, and `r` or Escape to end it. `u` starts + the walk on Not observed, and `u` / Shift+U then step through those claims + whatever the filter. ← and → still move to a card as they do outside the + walk (since the live-check fixes, from a connection to its end that lies + that way, where they went to the diagram's first card). Outside the + walk `j`, `k`, `[`, `]` and the arrows do what they did; the walk uses no Ctrl + or Cmd keys. +- Each step selects the claim and brings it into view (a finding frames the + steps it cites), shows it in the Selection tab (a hidden side panel or a + collapsed bottom panel opens), and, once you stop for about 150 ms, opens + its cited lines in the editor beside the diagram with the range highlighted + while the keyboard stays on the diagram. A claim with no quotes opens + nothing and says so, and the previous claim's highlight goes. A quote VS + Code did not open (its file changed or went missing, or its unsaved edits + no longer contain the cited lines) shows the reason in the walk bar and + under the quote in the Selection tab; + nothing is opened and no notification appears. A screen reader hears, for + example, "Claim 3 of 16, not observed: Step Load batches, inferred." +- The walk bar sits at the foot of the diagram, directly above the bottom + panel's tabs (with the side panel, along the bottom of the diagram), and is + shown only while walking: "Claim 3 of 16 · Not observed", the filters, the + keys, **Exit**, and a line such as "In the editor beside: train.py · lines + 12–14, highlighted. Focus stays here." Below 620 px wide it is one row: + "3/16", Previous and Next (added in the review fixes), the filters and + **Exit** (below about 420 px the filters wrap onto a second row, since the + live-check fixes); a quote that was not opened adds a warning + mark there and the Selection tab says why. It sits under the diagram rather + than under the header so the diagram stays within four Tab presses of the + top, and Tab goes from the diagram to the walk's controls to the claim. +- The walk remembers where it was for each revision in the panel's saved view + state: `r` resumes there, and a panel VS Code rebuilt brings a running walk + back without opening anything until you move or press Enter. A new revision + starts fresh. Nothing marks a claim as checked, and nothing is written to + any file. +- The notice for changed cited files has **Review affected claims**, which + walks the claims whose quotes cite those files. +- The header's **Review** button is shown from 620 px wide and folds into the + **⋯** menu with the revision when the row is short; the menu always has it. +- Clicking or searching to another claim the walk holds moves the walk there + without opening it (Enter opens it), and clears the earlier highlight. +- The walk counts every claim in the document; the severity toggles and the + **N not observed** fade do not change what it visits. +- Live (vit-cc, VS Code 1.139, macOS, the diagram about 393, 543 and 902 px + wide beside the notebook): `r` started the walk on 7 claims, each step + opened the notebook beside it with the cited cell lines highlighted and the + focus left in the diagram, `]` opened the second quote, Escape ended the + walk, and no notification appeared. Found in that run: after `]` the + second quote stayed below the Selection tab's fold. The tab now scrolls the + walk's quote into view; that fix is checked in jsdom only, not live. + +The host side, for the walk (added with step 11): +- A new `walk` message state, `clear`, sent when the walk moves to a claim it + opens nothing for (no quotes, or a claim you only selected): the host clears + the highlight and drops a walk open still on its way, as for the walk's + end. +- A blocked walk open also clears the previous claim's highlight, so the + editor never shows an earlier claim's lines while the walk says this one was + not opened. A blocked Enter, double-click or Open link keeps it, as before. + +Opening a cited range for the walk (`openLocation`, no change for Enter, +double-click or an Open link): +- An open can carry a sequence number (`seq`), a request id, `walk: true` and + `highlight: false`. The host drops an open whose number is not above the + last one it saw, and one a later open overtook while it waited (for example + for a notebook cell's editor). A new page starts the numbering again. The + walk waits about 150 ms after the reader stops moving before it asks for a + jump. +- Each open with a request id gets one answer: done, blocked, cancelled or + failed. A blocked answer names the reason and says nothing was + opened, for example "vit_pytorch/efficient.py changed after revision + cats-dogs-r1 was published; not opened." A changed, missing, unreadable or + oversized file, a file in another folder, unsaved text that lost the cited + lines, or a notebook cell that no longer exists is never opened. +- A blocked walk open raises no VS Code notification, since the walk shows the + reason itself. A blocked open from Enter, a double-click or an Open link + still shows its notification, as before. +- A new message tells the host the walk ended: it clears the cited-range + highlight, including its mark in the editor's overview ruler, and drops a + walk open still on its way. Closing the panel also clears the highlight. +- The check behind each jump is cached for the displayed revision and + freshness. On a synthetic 200-cell, 3.8 MB notebook, 60 cell jumps hashed + the notebook once instead of 60 times, and the extension host was busy for + 310 ms instead of 466 ms over the 12 s run. A file that changes on disk is + checked again on the next jump, even before VS Code reports the change. + +Escape: +- An Escape the viewer used (closing the menu, the shortcut sheet, the Refine + popover or the legend, ending the review walk, collapsing the bottom panel, leaving focus mode, + clearing the selection or the search) no longer also reaches VS Code. VS + Code's webview forwards every key to the workbench, even one the page has + handled, so a viewer Escape also hid a visible notification, which was + measured live. The panel now stops such an Escape inside the page. +- When nothing is left to close and the focus is already off the diagram, + Escape is left to VS Code, for example to hide a notification. +- The M2 report of the diagram losing focus when Escape followed an Enter that + opened a notebook (vit-cc, the diagram alone at about 1430 px) was not + reproduced in 14 live attempts, with waits from 150 ms to 7 s and a + notification on screen. The fix above removes the one way found for a + viewer Escape to act in VS Code as well; whether it was the cause of that + report is not confirmed. After the fix, the same steps keep the focus in + the diagram, nothing is forwarded, and the notification stays. Live, with + a warning notification on screen, Escape in the search box cleared it and + returned to the diagram, the next two cleared the selection and left the + diagram, all with the notification still up, and only the fourth reached VS + Code and hid it. + +Notebook load (measured, see [docs/PERFORMANCE.md](docs/PERFORMANCE.md)): +- Over 60 paced cell jumps MLView's own code used about 40 ms of extension + host CPU before and after the cache; the cache removed the 93 ms spent + hashing the notebook again on every jump. +- Creating and then deleting 2,000 files in the workspace, in one profiled + run with counting breakpoints, called MLView's file watcher 1,157 times for + about 6 ms in total. That run's output and profile were not kept, so these + figures cannot be re-derived (unlike the notebook figures above). The + watcher adds no operating-system watcher and must see every cited file, so + it was not narrowed. +- With Pylance running, the extra time was outside MLView's code. These runs + cannot attribute it more finely, and no freeze was reproduced. + +The phase overview (step 13): +- Press Shift+0 on the diagram, or choose **Phase overview** in the **⋯** menu, + to see every phase at once. Each phase is a block: its number and name, its + step count (with how many steps are inferred or unresolved), the findings + that touch it, then its step titles in the Outline's order, a group before + its steps. Inferred steps are marked ◌ and unresolved ones ?; observed steps + carry no mark. Each title is followed by the F labels of its findings, for + the severities the header's toggles show. +- An arrow between two neighbouring blocks says how many connections go from + one phase to the next ("3 connections"). A connection that skips a phase or + goes back to an earlier one is a bracket on the right with its count, + dashed when it goes back. The header gives the connections as a sum, for + vit-cc "6 phases · 31 steps · 41 connections: 19 inside a phase, 8 to the + next phase, 14 skip ahead or go back." +- A block with more titles than fit lists its first ones and ends with "… N + more steps", so every step is either listed or counted, and the counts add + up to the document's steps. The longest blocks give up rows first, and no + block drops below two titles and its count; past that the overview scrolls. + The layout on the two shakedown artifacts: at 1440x900 vit-cc lists all 31 + titles and yolov5-cc2 52 of 59. At 900x800 they list 19 and 38. Beside the + code at 541x798 the titles are one column: yolov5-cc2's four phases list 13 + titles and fit, while vit-cc's six phases list 13 titles and scroll a little. +- ↑ / ↓ (or ← / →), Home and End move between blocks. Enter, Space or a click + goes to that phase at reading size, the same zoom rule as the first view + uses for phase 1. The move takes about 240 ms, or happens at once with VS + Code's Reduce Motion, the OS's reduced-motion setting or a screen reader. + The selection does not change, and the next arrow key starts at that + phase's first step. Escape, Shift+0 again or **Back** returns to the diagram + exactly as it was, with the keyboard where it was (since the review fixes, a + legend opened over the overview closes on the first Escape). Any other key the + diagram uses (for example `r`) closes the overview and then acts. The + shortcut sheet, search and the legend open over it. +- A screen reader reads each block as a button, for example "Phase 2 of 6: + Data preparation. 9 steps (1 inferred). 2 findings touch this phase. 3 + connections to phase 3; 1 connection ahead to phase 5. Enter goes to this + phase.", described by its titles. Opening the overview announces the + number of phases and steps and the keys. +- Shift+0 no longer folds every group and fits the diagram. **Fit the whole + diagram** stays in the **⋯** menu, and each group keeps its own chevron. +- The overview is drawn over the diagram, not in it: nothing in the diagram's + layout moves, and the geometry golden is unchanged. + +The phase index (step 13): +- The labelled phase index replaces the minimap (the unlabelled overview map + in the corner). It lists every phase in the diagram's lower right corner: + the number in the phase's colour, the name, the findings that touch it by + severity (a finding on two phases counts in each, as on the lane headings) + and the step count. The phases in view are marked, and a row goes to that + phase, as a block of the overview does. +- In a panel under 1000 px wide, or on a diagram under 350 px tall, it is one + line naming the phase most in view, such as "4/6 Objective, optimizer & + scheduler", which opens the list above itself; Escape closes it. Wide, the + list's chevron folds it to that line. It sits inside the diagram, so in the + bottom panel's layout it stays above the panel. +- It never covers the card you are on. Moving with the arrow keys, choosing a + step in the Outline or in search, stepping through the review walk and VS + Code's reveal all pan the least distance that keeps the card out from under + it. A finding keeps its first cited step clear; a finding whose cited steps + span more than the room beside the index can still have a later one under + it. +- **Phase index** in the **⋯** menu (in place of **Overview map**) shows or + hides it. It is disabled, with the reason, for a diagram with fewer than + two phases. The choice is kept in the panel's view state, and a minimap you + had collapsed opens as the folded index. + +Checked for step 13: `webview/test/phase-overview.test.mjs` runs on synthetic +documents of the vit-cc and yolov5-cc2 shapes and small hand-made ones. It +covers the overview's layout at the canvases of 1440x900, 900x800 and 541x798 +(every step listed or counted, the connection counts summing, blocks inside +the canvas, one or two columns), the keys, Escape giving the focus back, the +animation and reduced motion, the blocks' names, and the diagram's geometry +unchanged by the overlay. It also covers the index's rows and pill, and the +selected card never under the index along each path above, at all three +sizes. Removing the index's cover rule makes that last check fail. Headless +Chrome screenshots of vit-cc and yolov5-cc2 at the three sizes were looked at. +Not tried in VS Code, with a screen reader, on Windows or Linux, or as a +usability check. + +Reveal in Diagram (step 14): +- Right-click a line in the editor and choose **Reveal in Diagram** to see + which claims of the open diagram cite it. The Command Palette has it too, as + **MLView: Reveal in Diagram**. There is no default keybinding; you can add + one in VS Code's Keyboard Shortcuts. It is the extension's second command, + next to **MLView: Open Generated Diagram**. +- The menu item appears on a file an open diagram cites, while VS Code has + not reported it changed or missing since the revision was published. Its + `when` clause is `resourcePath in mlview.citedFiles` (with a file or + notebook-cell scheme): the extension keeps that list of the open diagrams' + cited, unchanged files and empties it when the last diagram closes. (This + replaced, in the review fixes below, a key worked out from the active + editor, which also hid the item while unsaved edits had moved every quote.) + An unsaved edit that moved the quotes is explained by the command, and a + quote that moved no longer counts. +- The cursor's line, or the selected lines, decide the claim. One claim cites + them: it is shown at once. Several do: a list asks which, steps first, then + connections, then findings, each in document order. Each row gives the kind + and title (a finding's F label first, a connection as its two ends), then + the connection's label, the finding's severity, the phase and the basis when + it is not observed, and where it is cited; a finding's counter-evidence says + so. A row from the tests' small document: "F1 Finding: Unverified output", + "medium · Train · inferred", "Counter-evidence at lines 2–3". +- A line no claim cites offers the nearest claims in the file instead (at most + 30), each with how far it is, such as "Cited at lines 2–3, 1 line above". + Choosing one shows it; Escape cancels. A notebook cell's nearest claims come + from that cell first, then the nearest cells. +- The diagram selects the claim, opens a folded group that hides it, brings it + into view and shows it in the Selection tab. A connection is framed with + both ends where they fit, a finding with the steps it cites. A hidden side + panel or a collapsed bottom panel opens, so the claim shows at 541 px as + well as at 900 px. The shortcut sheet, the phase overview and the refine + box close (the refine box keeps your text). The keyboard moves to the + diagram, on the card or connection: the one place the viewer moves the focus + by itself, because you asked for the diagram. Nothing is opened in the + editor. During the review walk, the walk moves to the claim when its filter + holds it. +- A diagram hidden behind another editor comes to the front. VS Code reloads + a hidden panel's page, so the claim is shown again once the page is ready + (within 5 seconds). If the diagram moved to another revision while you were + choosing, it says so and asks you to run the command again. +- With several diagrams open, the one that cites the file is used; when more + than one does, a list asks which, with the number of claims each has there. +- Notebooks: it works in a notebook's cell editors, by the cell index the + diagram records. A notebook the diagram cites by cell, opened as plain JSON + text, gets a message saying to open it in the notebook editor (and the other + way round for a notebook cited as text). +- The menu and the palette hide the command on files no open diagram cites + and on changed ones; from the diagram itself the palette does not offer it. + Run where it does not apply (from a keybinding of your own, or on a file + whose unsaved edits moved the quotes), it says why in one line: no diagram is open, the + editor is not a cited file or cell, the file changed after the revision was + published (ask the assistant for a fresh revision), its unsaved text no + longer has the quotes, or the diagram cites the file from a parent folder + that is not in the workspace. A changed file is checked again before the + answer. +- How it finds the claims: each open diagram keeps an index of its validated + revision's evidence, file by file, with the steps, connections and findings + that cite each range. It is rebuilt when a new revision is shown and + dropped with the panel. It reads no code and infers nothing: a line counts + as cited when it lies inside a quoted range that still matches. No hover, + CodeLens or Problems-panel entries are added to your editors. +- The webview takes one new host frame, `reveal {kind, id}`, for a step, a + connection or a finding; the older `revealNode` and `revealIssue` frames go + the same way. An id the shown revision lacks gets "That claim is not in the + revision shown here." The shortcut sheet's note mentions the command. +- `scripts/vsix_check.py` now allows the second command in a packaged VSIX. + +Checked for step 14: `vscode-extension/test/reveal-host.test.js` (19 tests +then, 21 after the review fixes; mock `vscode`) covers the index, the nearest +claims and the list's rows, the context key for cited, uncited, changed, +edited and closed files (since the review fixes, the list and the manifest's +`when` clause evaluated against it), the index +rebuilt on a new revision, one claim shown at once, the list for several and +for the nearest, the plain messages, the parent-folder case, two diagrams +citing one file, the reveal sent again after the page's `ready`, a revision +changing while you choose, notebook cells, and everything disposed with the +last panel. `packaging.test.js` and `activation.test.js` were changed on +purpose to name the second command and to check it has no keybinding. +`webview/test/reveal.test.mjs` (17 tests, jsdom) covers the frames for the +three kinds and the focus they leave, folded groups, connections and +findings, the older frames, malformed frames and unknown ids, the overlays +closing, the walk following, a hidden side panel opening, and, at 1440x900, +900x800 and 541x798 on the vit-cc and yolov5-cc2 shapes, each step card and +connection revealed on the canvas and clear of the phase index. (The review +found that this check took only the first 40 connections and checked group +boxes as selected only; it now takes all 41 and 113, and the group exception +is in its title.) An +integration test drives the real host with the built page. Removing the +stale check, the quote check, the focus move, the replay, the index refresh, +the diagram list, the connection case or the side panel opening each makes a +test fail. + +It was also tried live in an isolated VS Code 1.139 Extension Development +Host on macOS, on the vit-cc artifact, driven over the DevTools protocol with +the window at 1440x900. The menu item showed on the cited `efficient.py` and +not on `README.md` (tried with the editor already focused; the review found it +missing on a right-click that came straight from the diagram, fixed below). A line cited by a step, a connection and a finding gave a +list of three; choosing the connection selected it and opened the bottom +sheet with the diagram 541 px wide. A line with one claim showed it at once, +and Enter on it then opened the code beside with the focus kept. An uncited +line offered the nearest claims. At 866 px (the narrowest the editor beside +allowed, not 900) a finding was framed clear of the phase index. In a +notebook cell the list showed six claims and the step was revealed. A diagram +hidden behind the code came to the front and showed the claim after its page +reloaded. An unsaved edit at the top of the file hid the item and undo +brought it back (the context key as it was then; the item now stays and the +command explains). After the diagram closed, the item was gone. The live run +showed that VS Code's hand-over of the focus left it on the page's body, so +for 1.5 seconds after a reveal the claim takes the focus back if nothing else +has it; checked live afterwards. A changed file was checked only by the unit +tests, not live. Not tried with a screen reader, on Windows or Linux, or as a +usability check. + +Review fixes (independent reviewers of steps 11 to 14; each finding was +reproduced or checked first, and each fix has a test that fails on the code +before it): +- **Reveal in Diagram on the first right-click.** The menu item was missing + when the right-click in the editor beside came straight from the diagram, + for example after Enter on a card: the old context key was worked out from + the active editor, which VS Code reports as none while the diagram has the + focus, and the menu was read before the extension's update came back (seen + live by the reviewer, three times out of three). The `when` clause now reads + `resourcePath in mlview.citedFiles`, a list of the open diagrams' cited, + unchanged files that the extension refreshes when a diagram opens, closes or + validates again, so VS Code decides for the editor under the pointer by + itself. No editor listener is left. What the list cannot see (unsaved edits + that moved the quotes, a notebook cited by cell opened as text) the command + explains in one line. A file opened through a symlinked folder is still + matched when the command runs, but the menu matches the path only. +- **Review affected claims starts at the first one.** It (and `u` after a walk + on another filter) used to begin after the place the last walk ended, so on + vit-cc, after walking Not observed to its end, it began at "Claim 8 of 8" + and never showed the other seven. A remembered place is now resumed only for + the same filter. +- **A claim that leaves Changed files.** When a file is fixed while the walk is + on a claim that cites only that file, the walk moves to the next affected + claim. It used to name that claim in the bar without selecting it, and `j` + then skipped it. Now it is selected, shown and announced, and the earlier + highlight is cleared; it is not opened by itself (you may be editing beside + the diagram), and Enter opens it. +- **"Enter shows" for a stale file.** A filter that keeps the claim, a click + on a claim the walk holds, or a panel VS Code rebuilt said "Enter shows …" + for a quote whose file VS Code had reported changed. They now say why it is + not opened, and a filter that keeps the claim keeps what the bar said. +- **Escape over the phase overview.** With the legend opened over the + overview, Escape closed the overview first. Escape now goes through the one + cascade the shortcut sheet describes: the legend, then the overview. +- **Review from the header or the ⋯ menu.** The keyboard stayed on the button, + so `j` and `k` did nothing. Starting the walk there now puts the keyboard on + the diagram, as **Review affected claims** does. +- **Previous and Next.** The walk bar has up and down buttons for the previous + and next claim at every width, named "Previous claim (k)" and "Next claim + (j)", inside the bar's one tab stop, so a pointer can walk too. +- **Alt+Enter.** After Alt+Enter the bar said "Focus stays here." It now says + "Focus moved to the editor.", and the Selection tab says so too. +- **A late answer.** An answer for an open of a claim the walk had already left + was shown as the current claim's, as "In the editor beside: , highlighted." + for a claim with no quotes. Every move now drops answers to earlier opens. +- **Words and screen readers.** The **All** filter reads **All claims**. A + blocked open is announced as the host's sentence, without "Not opened:" + before "…; not opened.". The narrow bar's full place ("Claim 3 of 16, Not + observed" and why a quote was not opened) is screen-reader text inside the + bar, not a name on a paragraph, which ARIA does not allow. The overview's + blocks name each F label once and end each title with a separator; a phase + index row's tooltip is "Go to this phase" (the full name is on the label), + so its name is not read twice; the header's Review toggle keeps the name + "Review the claims" and reports its state as pressed. +- **The phase overview's key.** Below 620 px the overview dropped its key, so + ◌, ? and the brackets were explained nowhere on screen. It now keeps a + shorter key, and both say what a bracket's number counts: "Arrows go to the + next phase; brackets count connections that skip ahead (solid) or back + (dashed). ◌ inferred, ? unresolved." (still one line at 900 px). +- **Longer phase names.** The phase index panel grows from 288 px to fit its + longest name, up to 360 px, so at 1440x900 vit-cc's "Objective, optimizer & + scheduler" and "Training loop (20 epochs)" are no longer cut short. +- **Records.** The workflow guide and this changelog no longer say a quote + with unsaved edits is not opened; it is not opened only when those edits no + longer contain the cited lines. The reveal check now takes every connection. + The watcher-load figure here and in [docs/PERFORMANCE.md](docs/PERFORMANCE.md) + says the run created and then deleted the files and that its output and + profile were not kept. + +Checked for the review fixes: `webview/test/walk.test.mjs`, +`phase-overview.test.mjs` and `reveal.test.mjs` (jsdom), and +`vscode-extension/test/reveal-host.test.js`, `packaging.test.js` and +`activation.test.js` (mock `vscode`, with a small stand-in for VS Code's +when-clause evaluation). The new tests were also run against the code before +the fixes, where each failed. The full reveal sweep passes on both, since it +pins behaviour that already held. Headless-Chrome screenshots of vit-cc at +541x798, 900x800 and 1440x900 (walk bar, overview, phase list) were looked at. +In an isolated VS Code 1.139 Extension Development Host on macOS, on a scratch +copy of vit-cc, driven over the DevTools protocol with `window.menuStyle` set +to custom so the menu could be read: after Enter on a card opened +`efficient.py` beside the diagram with the focus kept, a right-click on cited +line 20 straight from the diagram showed **Reveal in Diagram** three times out +of three; a cited notebook cell showed it twice out of two, and choosing it +selected the step and put the keyboard on its card; `README.md` did not show +it; the Command Palette offered it from a cited cell and not from the diagram. +With the diagram about 541 px wide, **Review the claims** in the **⋯** menu +put the keyboard on the diagram and `j` `j` reached "3/7"; Next and Previous +stepped; after Alt+Enter the focus was in the notebook and the bar said +"Focus moved to the editor." A changed file, the phase overview changes and +the phase index were not tried live. Not with a screen reader, on Windows or +Linux, or as a usability check. + +Live-check fixes, the viewer (found in a live check of the milestone in an +isolated VS Code 1.139 Extension Development Host on macOS, on a scratch copy +of vit-cc driven over the DevTools protocol; each was reproduced first in +jsdom or in headless Chrome with the screenshot harness's simulated host, and +each fix has a test that fails on the code before it): +- **The Selection tab starts each claim at its top.** The tab is one scroller + and kept the offset of the claim before. Live at 540x798 with the bottom + panel, after `k` `k` `j` `j` in the walk it was scrolled 194 px in a 315 px + pane: the claim's title sat 163 px above the pane and the walk's quote 335 + px down, so neither showed. A plain click did the same (since before M3). A + new claim now starts at the top, so its title shows; the same claim built + again (a host answer, a freshness change, a resize) keeps your place. +- **The walk brings its quote into view.** Each step then scrolls the tab the + least distance that shows the quote's file line and the line under it + saying what the editor beside shows, when that fits with the claim's title; + when it does not, the title stays and the quote is left below the fold, + since you meet the claim before its quotes and the editor beside already + shows the quoted lines highlighted. `[` and `]` ask for the quote, so it + wins there, keeping the title when both fit (they scrolled the whole quote + into view before). The scroll is set at once, so nothing moves under Reduce + Motion. When the step also opens the bottom panel, the quote is brought + into view again once the panel has its height: with reduced motion the + panel still measured 16 px during the step in headless Chrome. (In the + round-2 live check a step showed the quote and scrolled the title away when + both did not fit; the step now keeps the title.) +- **A restored file.** After a changed file was restored, the walk bar and the + quote kept "… changed after revision … was published; not opened." and the + warning mark until Enter. When the host's list of stale files changes for + the file of a quote it did not open, they now say "Enter shows …" (nothing + is opened by itself) or the new reason, such as "data.py: file missing; not + opened." A quote not opened for another reason (unsaved edits that lost the + cited lines, a cell that no longer exists) keeps its message until the next + open. +- **No hover card while the keyboard moves the diagram.** Live, the hover card + of whatever passed under the resting pointer covered part of the diagram + while the walk or the arrow keys panned it. The arrow keys, `e` / Shift+E, + `n` / `p`, zoom, fit, `z`, Enter on a phase overview block and every walk + step now hide the hover card (with a card's highlight of its connections), + and nothing that passes under the resting pointer gets one until the + pointer moves or is pressed. In headless Chrome on vit-cc at 541x798, with + the pointer resting at four places and ten walk and arrow keys at each, 3 + of the 40 keys left a hover card before the fix and none after. +- **The phase overview keeps its header.** With the focused block low (live at + 901 and 540 px), **Back**, the counts and the key scrolled out of view, and + Home stopped 12 px above the first block with the header still hidden. The + header now sticks to the top of the overview while it scrolls, a block + brought into view is placed below it, and Home scrolls to the top. A header + taller than half the overview scrolls with the blocks, as before; there + Home shows the header whole when the first block fits below it, and + otherwise the first block's top. In + headless Chrome at 541x798, 541x500 and 901x470 **Back** stayed in view and + clickable at every block. +- **Arrow keys from a connection.** Live, → on a focused connection selected + the first step of phase 1. The arrows started only from a selected step; from + a connection or a finding they went to the diagram's first card. From a + connection, → and ← now select whichever of its two cards lies further right + or left, and ↓ and ↑ the lower or higher one (on a tie, → and ↓ take its + target, ← and ↑ its source). From a finding they move as from its first + cited step, which the diagram marks for it (a finding on connections only: + as from its first connection), and from a selected step folded inside a + group as from that group (it went to the first card too). With nothing + selected, or a finding that cites neither, they still select the first + card. In vit-cc, → on "torch + generator seeds torch.randn parameter init" now selects seed_everything(42) + and ← the ViT wrapper group. The shortcut sheet's line for the arrows says + so. +- **The walk bar at 320 px.** At the documented floor the narrow bar wrapped + into four rows, 102 px of the column. It stays in the column's flow, so it + never covered the panel's tabs. Its controls now share the place's row and + the filters wrap between Next and Exit: two rows, 55 px, in the same order + for the eye and for Tab. At 541 px it is one row, as before. + +Checked for the live-check fixes: `webview/test/m3-live.test.mjs` (jsdom, 14 +tests, with a stand-in layout for the Selection tab and the overview; all 14 +fail on the build before the fixes, and the reduced-motion one also fails with +only the second reveal switched off) and the `[` / `]` case in +`walk.test.mjs`, which now checks where the tab is scrolled instead of the +`scrollIntoView` call. Headless-Chrome runs of +the screenshot harness's simulated host on vit-cc, before and after: the tab +at 541x798 with and without reduced motion, the hover cards at 541x798, the +overview at 541x798, 541x500 and 901x470, the arrows at 1440x900 and the walk +bar at 320, 400 and 541 px wide. Not checked in VS Code after the fixes, not +with a screen reader, on Windows or Linux, or as a usability check. No +contract change, no new setting or keybinding, and the geometry golden is +unchanged. + +The live check's notebook focus loss, and the walk's selection (the host): +- **The focus loss came from how the live check drove VS Code, not from + MLView.** In the M3 live check the diagram lost the keyboard on the first + key after Enter or a walk step opened a notebook that was not open: Escape, + an arrow key, `q`, and in the walk the `j` after a step had opened the + notebook again (the walk's preview tab reopens it after each step to + `efficient.py`). The workbench got the focus and the walk stopped + answering. The report carried from M2 describes the same symptom. It was + seen only in a window that had never been the active window since it + started (one launched while the screen was locked; that check ran while the + display was asleep) with the keys coming over the DevTools protocol. In a + fresh isolated VS Code 1.139 window started while the screen was locked, on + the current build, it happened for 4 of 5 Escapes, + 2 of 2 arrow keys and 2 of 2 `q`, and a walk over All claims (79) stopped + at claim 50, the key after claim 49 had reopened the notebook. In a window + started while VS Code was the active application it did not happen in 19 + Escapes after Enter (waits of 150 ms to 5 s, 6 with the display asleep, 5 + on the live check's webview build), in 2 more with another application in + front, or in a walk over all 79 claims. A + Chromium trace of a failing key shows, within a quarter of a millisecond: + the browser giving the page focus (the trace shows this before each key + event DevTools sends), the process of the notebook's output webview reporting that the + workbench frame took focus, and the browser moving the focus there; the + diagram's frame then lost its focused element and the key's release went + to the workbench. The workbench's scripts called no `focus()` or `blur()`, + and nothing changed on the diagram's frame or its parents. Emulating a + focused page in that notebook webview's process before the key kept the + focus 3 times of 3 (it was lost 3 times of 3 without). Nothing in MLView + changed for this, and no focus recovery was added: the loss needs a window + that was never active receiving keys from DevTools, which a person typing + into VS Code does not do. Not checked by a person at the machine, nor + whether switching to another application and back after a notebook opened + moves the focus the same way. +- **The walk keeps opening sources in a preview tab.** Keeping a notebook the + walk opened as an ordinary tab would avoid reopening it after each step to + another file (about 480 to 520 ms for such a step against about 190 ms + within one file, in the live check), but a walk would then leave one tab + for every cited file it passed, and the reopening is not what moved the + focus. +- **The walk lets go of the lines it selected.** When the walk ended, moved + to a claim it opens nothing for, or did not open a quote, the highlight + went but the editor kept the earlier claim's lines selected (in the + inactive-selection colour). If that editor is still shown and its + selection is still exactly the one the walk set, the selection now + collapses to the start of the cited lines. A selection you changed is left + alone, and an Enter or Open link that is not opened keeps both the + highlight and the selection, as before. Live on vit-cc after `r`, `k`, + Escape (cell 35, lines 34 to 36): the lines stayed selected on the build + before and are no longer selected now; with `efficient.py` edited in a + scratch copy, the walk step to its claim was blocked and the notebook's + lines from the claim before were no longer selected either. + +Checked for this: `vscode-extension/test/walk-host.test.js` gained four mock +`vscode` tests (the walk end and a walk clear, a selection the reader changed +and an editor no longer shown, a blocked walk open, a notebook cell editor); +the three that collapse fail with the collapse switched off, and the one for +the reader's selection fails without the check. In the isolated window that is +not active, with every webview frame emulating a focused page as an active +window's would: Escape kept the focus 5 times of 5, an arrow key and `q` 2 of 2 +each, a walk over all 79 claims kept it through each change between the +notebook and `efficient.py`, Alt+Enter moved it into the notebook, Cmd+1 (on +macOS, VS Code's key for the first editor group) gave the keyboard back to +the diagram's panel with no card focused (the page body), and Cmd+2 from the +diagram moved it to the notebook. From the page body the diagram's keys +still answer, as since R1 (a page-level key listener); a new jsdom test in +`webview/test/walk.test.mjs` pins it for the walk's `j`, ↑ and Escape. Not +pressed live after Cmd+1. These runs show +that the product keeps its focus rules under that emulation; they are not a +check of a person using it. Not on Windows or Linux or with a screen reader. +No contract change, no new setting or keybinding, and the geometry golden is +unchanged. + +Round-2 review corrections (an independent review of the live-check fixes +found no product defect and four gaps, now closed; the walk's scroll rule was +also changed after a second live check): +- **A walk step keeps the claim's title.** At 541 px with the bottom panel, a + step to a long claim showed its quote and scrolled the title away. A step + now shows the quote only when it fits with the title (`[` and `]` still + bring the quote). The W1 tests in `webview/test/m3-live.test.mjs` follow + the new rule and fail with it switched off. +- **Home in a short phase overview** shows the header whole only when the + first block fits below it; this section first said it always did. The test now + presses Home in both cases and fails without the first-block rule. +- **A host block for unsaved edits** stays when its file then leaves the + stale list; that clause had no test, and now has one that fails without it. +- **Wording:** the notebook focus loss was seen only in a window that had + never been active since it started, not in any inactive window; Cmd+1 gives + the keyboard back to the panel's page body, where the diagram's keys still + answer (a new jsdom test pins it for the walk; not pressed live). + +Checked for the corrections: `npm run check` and `npm test` in webview/ and +vscode-extension/, `python tools/verify.py --all` and +`python scripts/check_docs.py`; each new or changed test was run against the +code with its rule switched off and failed. Local checks only: not a check by +a person, on Windows or Linux, or with a screen reader. + ## Unreleased — viewer M2: readable at your width The viewer's second milestone: a diagram you can read in the panel beside your diff --git a/README.md b/README.md index cfd14266..b761f056 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,10 @@ in the runbook. Only install one copy of the skill in each discovery path. To refine a particular node, edge, or finding, select it and use **Refine → Copy prompt**. Choose an intent or enter a specific question, then paste the prompt into the same assistant. The header is one row: the title, the -assistant and revision, search, the severity toggles, **N not observed**, a -**⋯** menu (legend, flow animation, fit, exports, shortcuts) and **Refine…**. -In a narrow panel, such as beside your code, search and the revision move +assistant and revision, search, the severity toggles, **N not observed**, +**Review**, a **⋯** menu (legend, flow animation, fit, exports, shortcuts) and +**Refine…**. +In a narrow panel, such as beside your code, search, the revision and **Review** move into the **⋯** menu, and a row that is still too full folds **N not observed** into it too, so **Refine…** stays in view. The status bar counts steps and connections, shows the coverage status with its limitations, and says whether the cited files are @@ -82,6 +83,36 @@ narrower than about 1260 px the rail is a bottom sheet under the diagram: a tab strip until you select something, then about half the height, with the selected card kept in view above it; Escape collapses it. +To check a diagram claim by claim, press `r` (or **Review**). The review walk +starts on the claims marked inferred or unresolved, the header's **N not +observed** count, and can also walk the findings, every claim, or, after a +cited file changed, the claims citing it. Each step selects the claim, shows +it in Selection and, after a short pause, opens its cited lines beside the +diagram, highlighted, while the keyboard stays on the diagram: `j` / `k` (or +↓ / ↑, or the walk bar's up and down buttons) move, `[` / `]` change quote, +Enter opens again and Escape ends the walk. A quote whose file changed is not +opened; the walk says why. The walk remembers its place for each revision and +records nothing else. + +To see the whole workflow at once, press Shift+0 (or **Phase overview** in the +**⋯** menu). The overview shows each phase as a block of its step titles, with +inferred and unresolved marks and the F labels of findings. Arrows count the +connections from one phase to the next, and brackets count those that skip +ahead or go back. Arrow keys and Enter (or a click) go to a phase; Escape +returns you to where you were. The phase index in the diagram's lower right +corner lists every phase with its findings and step count and marks the ones +in view. Beside the code it shrinks to one line such as "4/6 Objective, +optimizer & scheduler". + +To go the other way, from the code to the diagram, right-click a line in the +editor and choose **Reveal in Diagram** (also **MLView: Reveal in Diagram** in +the Command Palette; there is no default keybinding). It is offered on a file +an open diagram cites that has not changed since the revision was published. +The claim citing that line, or the selected lines, is selected and +shown in the diagram, and the keyboard moves there. When several claims cite +the line, a list asks which; on a line no claim cites, it offers the nearest +claims in the file. It works in notebook cells too. + The workflow supports custom phases, nested groups, branches and cycles, notebook cell references, and findings with evidence and counter-evidence. Observed, inferred, and unresolved claims remain distinguishable: only the diff --git a/docs/LLM_WORKFLOW.md b/docs/LLM_WORKFLOW.md index f062d5c1..9b188ca2 100644 --- a/docs/LLM_WORKFLOW.md +++ b/docs/LLM_WORKFLOW.md @@ -168,6 +168,107 @@ again keeps its tab, its selection and an open bottom panel. With VS Code's screen-reader optimisation on, a selection is announced by its claim and nothing animates. +The review walk goes through the displayed revision claim by claim. Press `r` +on the diagram, **Review** in the header or **Review the claims** in the **⋯** +menu. It follows the diagram's order: phase by phase, each step, then its +outgoing connections, then the findings whose first cited step it is; +findings that cite no step (only connections, or the workflow as a whole) come +last, in document order. It starts on **Not observed**, the claims marked +inferred or unresolved, with the same count as the header's **N not +observed**; **Findings**, **All claims** and, while VS Code reports changed or +missing cited files, **Changed files** are the other filters, each with its +count. `j` / `k` or ↓ / ↑ (or the walk bar's up and down buttons) go to the +next or previous claim, `[` / `]` to the +claim's other quotes, Enter opens the current quote again (Alt+Enter moves the +focus to the editor), `n` / `p` go to the next or previous finding, `u` / +Shift+U to the next or previous claim not observed (`u` also starts the walk +there), and Escape or `r` ends the walk. ← and → still move to the nearest +card. Each step selects the claim, brings it into view and shows it in the +Selection tab; about 150 ms after you stop, its cited lines open in the editor +beside the diagram with the range highlighted, and the keyboard stays on the +diagram (starting the walk from **Review** or the **⋯** menu puts the keyboard +on the diagram too). A claim with no quotes opens nothing and clears the +previous highlight. A quote whose file changed or went missing, or whose +unsaved edits no longer contain the cited lines, is not opened: the walk bar +and the quote in the Selection tab say why, and no notification appears. +When the walk ends, or a step opens nothing, the lines the walk selected in +the editor are no longer selected, unless you changed the selection. A +screen reader hears, for example, "Claim 3 of 16, not observed: Step Load +batches, inferred." The walk bar sits under the diagram, above the bottom +panel's tabs (with the side panel, along the bottom of the diagram), and shows +the place and filter, up and down buttons for the previous and next claim, the +filters, the keys, **Exit** and what the editor beside shows (after Alt+Enter, +that the focus moved to the editor); below 620 px wide it is "3/16", the two +buttons, the filters and **Exit**. The walk remembers its place for each +revision in the panel's view state, so `r` resumes there; a new revision +starts fresh. It marks nothing as checked and writes nothing to any file. When +cited files change, the notice offers **Review affected claims**, which walks +the claims citing them from the first one. If a file is fixed while the walk +is on a claim that cites only that file, the walk shows the next affected +claim and says so; Enter opens it. + +The phase overview shows the whole workflow at once. Press Shift+0 on the +diagram, or choose **Phase overview** in the **⋯** menu. Each phase is a block +with its number, name, step count (and how many steps are inferred or +unresolved) and the findings that touch it. Under that come its step titles in +the Outline's order, a group before its steps, with ◌ for inferred, ? for +unresolved and the F labels of the findings on each step. An arrow between +two neighbouring blocks says how many connections go from one phase to the +next. A connection that skips a phase or goes back is a bracket on the right +with its count, dashed when it goes back; a key under the header says so at +every width. The header gives the counts as a +sum, for example "6 phases · 31 steps · 41 connections: 19 inside a phase, 8 +to the next phase, 14 skip ahead or go back." A block with more titles than +fit ends with "… N more steps", so every step is either listed or counted. +Beside your code, a document with many phases scrolls a little rather than +dropping the titles. ↑ / ↓ (or ← / →), Home and End move between blocks. +Enter or a click goes to that phase at reading size; the move takes about a +quarter of a second, or happens at once with Reduce Motion or a screen reader. +The next arrow key then starts at the phase's first step. Escape, Shift+0 or +**Back** returns to the diagram exactly as it was, with the keyboard where it +was; a legend opened over the overview closes first. Any other key the diagram +uses closes the overview first. A screen +reader reads each block as a button named by its phase, counts and +connections, with its titles as the description. + +The phase index sits in the lower right corner of the diagram. It lists every +phase with its number, name, findings by severity (a finding on two phases +counts in each, as on the lane headings) and step count, and it marks the +phases in view. The list is wide enough for names up to about 360 px; a +longer name is cut short, with the whole name in its tooltip. Clicking a row +goes to that phase. In a panel under 1000 px +wide, or on a diagram under 350 px tall, it is one line naming the phase most +in view, such as "4/6 Objective, optimizer & scheduler"; clicking it opens the +list above it. When wide, its chevron folds it to that line. It stays inside +the diagram, above the bottom panel, and never hides the card you are on: +arrows, search, the Outline, a finding, the review walk and VS Code's reveal +all pan the card out from under it. **Phase index** in the **⋯** menu shows or +hides it, and the panel remembers the choice. + +Reveal in Diagram goes from the code back to the diagram. Right-click a line +in the editor and choose **Reveal in Diagram**, or run **MLView: Reveal in +Diagram** from the Command Palette. There is no default keybinding. The item +is offered on a file an open diagram cites, as long as VS Code has not +reported it changed or missing since the revision was published. It is there +on the first right-click, even straight after pressing Enter on a card in the +diagram. If your unsaved edits have moved the quoted lines, the command says +so in one line instead of guessing. The cursor's line, or the selected lines, decide +the claim. When one claim cites them it is shown at once; when several do, a +list names each one's kind, title, F label, severity, phase and cited lines, +steps first, then connections, then findings; on a line no claim cites, the +list offers the nearest claims in the file. The diagram selects the claim, +unfolds its group, brings it into view and shows it in the Selection tab +(opening a hidden side panel or a collapsed bottom panel), and the keyboard +moves to the diagram. It is the one time the viewer moves the focus by +itself, because you asked for the diagram; nothing is opened in the editor. A +diagram hidden behind the code comes to the front. With several diagrams +open, the one that cites the file is used, or a list asks which. It works in +notebook cell editors. Run where it does not apply (from a keybinding of your +own, or after the file changed), it says why, for example that the file +changed after the revision was published and the assistant should publish a +fresh one. The claims come from the displayed revision's +evidence records; MLView reads no code to find them. + On the diagram, observed claims carry no basis mark. An inferred step or connection has a dashed border or line and an `inferred` tag; an unresolved one has a dotted border or line and a `? unresolved` tag. Line style shows @@ -191,23 +292,27 @@ The diagram opens readable: the whole document if it fits at 62% zoom or more, otherwise the first phase at 90% (or all of the first phase, when it fits at 75% or more). Reopening the same revision keeps where you were. Press `0` to return to that first view from any zoom; **Fit the whole diagram** in -the header's **⋯** menu shows everything, and Shift+0 (Overview) also folds -every group. Zoomed out below 62%, cards show only their titles, at about +the header's **⋯** menu shows everything. Shift+0 opens the phase overview; +it no longer folds every group (each group keeps its own chevron). Zoomed out below 62%, cards show only their titles, at about 11 px on screen down to about 35% zoom; the full title is in the hover card. The header is one row: the title, the assistant and revision, search, the -severity toggles (each with its count), **N not observed**, a **⋯** menu and -**Refine…**. In a panel under 620 px wide, such as beside your code, search -and the revision move into the **⋯** menu; a row still too full folds **N not -observed** into it as well, so **Refine…** stays in view. The menu also holds the legend, the -flow animation, the overview map, the side or bottom panel, **Fit the whole diagram**, -zoom to the selection, the SVG and PNG exports and the shortcut sheet. The -overview map item is disabled, with the reason, when the map is not drawn: in -a panel 900 px wide or narrower, a diagram under 350 px tall, or with fewer -than 30 cards. Ctrl+F (Cmd+F on macOS), or `/` on the diagram, focuses +severity toggles (each with its count), **N not observed**, **Review**, a +**⋯** menu and **Refine…**. In a panel under 620 px wide, such as beside your +code, search, the revision and **Review** move into the **⋯** menu; a row still too full folds **N not +observed** into it as well, so **Refine…** stays in view. The menu also holds the +phase overview, the legend, the flow animation, the phase index, the side or +bottom panel, **Fit the whole diagram**, zoom to the selection, the SVG and PNG +exports and the shortcut sheet, and always **Review the claims**. The phase +index item is disabled, with the reason, for a diagram with fewer than two +phases. Ctrl+F (Cmd+F on macOS), or `/` on the diagram, focuses search; the viewer's other keys are single keys, so VS Code's own Ctrl and Cmd shortcuts keep working, and the shortcut sheet prints them for your platform. -Each search result shows its +Escape closes one thing at a time in the viewer (a menu or panel, the phase +index's list, the phase overview, the review walk, the bottom sheet, focus mode, the selection, then the focus on the +diagram) without VS +Code also acting on it; after that it goes to VS Code, for example to hide a +notification. Each search result shows its title and, under it, where it is cited. The status bar counts steps and connections, shows the coverage status with its limitations, and says whether the cited files are unchanged (muted text) or changed or missing (a @@ -379,6 +484,8 @@ Codex, and Claude Code inside VS Code: - Discover and invoke the installed skill in the native assistant. - Have the model read source and author a diagram without the static analyzer. - Open the artifact, inspect node and edge evidence, and navigate to exact lines. +- Right-click a cited line and choose Reveal in Diagram; verify that the claim + citing it is shown and that an uncited file does not offer the command. - Exercise a notebook cell and repeated/custom phase names. - Request a refinement; verify a new revision and stable unaffected node IDs. - Copy a Refine prompt for each intent; verify that Explain answers without diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md index a4176061..148859ab 100644 --- a/docs/PERFORMANCE.md +++ b/docs/PERFORMANCE.md @@ -158,3 +158,42 @@ limits timing precision. Its different Chromium version, host capabilities and much narrower viewport mean these values and DOM counts cannot be compared directly with the standalone Chrome table. The heap samples are live JavaScript heap snapshots, not total or peak process memory. + +## Notebook cell jumps in VS Code (viewer M3, 2026-10-02) + +A synthetic notebook (200 cells, 171 of them code with outputs, 3,826,440 +bytes) and a diagram citing 60 of its cells were opened in an isolated VS Code +1.139.0 Extension Development Host on macOS. A script sent 60 cell jumps +through the viewer's bridge over the DevTools protocol, either one every +150 ms ("paced") or all at once ("burst"), and recorded a CPU profile of the +extension host and of the workbench renderer (0.5 ms sampling). These are +single runs on one machine: read them as an attribution of costs, not as a +budget. + +| Run | Extension host busy | MLView's own code | Hashing the notebook | Garbage collection | +|---|---:|---:|---:|---:| +| Paced, before the jump cache | 466 ms of 12.1 s | 36 ms | 93 ms (60 reads) | 45 ms | +| Paced, with the jump cache | 310 ms of 12.1 s | 45 ms | 1.3 ms (1 read) | 11 ms | +| Burst, before the jump cache | 206 ms of 3.2 s | 11 ms | 101 ms | 22 ms | +| Burst, with the jump cache | 70 ms of 3.2 s | 13 ms | none sampled | under 1 ms | + +"Busy" is the profile's time not spent idle. Before the cache every jump +hashed the whole notebook again to prove it unchanged; now the first jump +hashes it once and later jumps compare the file's size, inode and times. In a burst the host opens only the last jump, and a jump a later one +has overtaken now stops before it reads the file. + +The workbench renderer was busy for about 2.2 to 2.3 s of each 12 s paced run +(VS Code's own notebook and editor code); MLView runs no code there outside +its webview. Creating and then deleting 2,000 files in the workspace, in one +profiled run with counting breakpoints on MLView's listener, called MLView's +file-watcher listener 1,157 times, about 1 ms of MLView code and 5 ms of path +handling in total. Unlike the table above, these figures cannot be re-derived: +that run's output and profile were not kept. The watcher (`**/*`) shares VS Code's own workspace +watching and adds no operating-system watcher, and it has to see every cited +or inspected file, whatever its name, so it was left as it is. + +With Pylance 2026.4.1 installed and a Python language server running, the same +paced run spent 42 ms in MLView's code and 528 ms busy in the extension host; +the renderer was busy for 2.6 s. The extra time is outside MLView's code (in +Pylance, VS Code's messaging and the workbench); these runs cannot divide it +further. No freeze was reproduced. diff --git a/docs/STATUS.md b/docs/STATUS.md index 9908ec73..d6bcd64c 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -143,6 +143,182 @@ the hint's and VS Code's Add Folder, Save Workspace As, Restart Extension Host, Reload Window and the multi-root case; an extension install or update was not tried ([changelog](../CHANGELOG.md)). +Unreleased viewer M3, step 11 (the review walk): `r`, the header's **Review** +button or **Review the claims** in the **⋯** menu goes through the displayed +revision claim by claim, in the diagram's order (each step, its outgoing +connections, then the findings whose first cited step it is; findings that +cite no step come last). Every claim is visited once: 79 in vit-cc, 176 in +yolov5-cc2. It starts on **Not observed** (7 and 16, the header's counts); +**Findings**, **All claims** and, when cited files changed, **Changed files** +are the other filters. `j` / `k` or ↓ / ↑ step, `[` / `]` change quote, Enter opens +again, `u` / Shift+U go through the claims not observed, `n` / `p` through the +findings, and Escape or `r` ends it. About 150 ms after each step the cited +lines open beside the diagram, highlighted, with the keyboard kept on the +diagram; a quote VS Code did not open says why in the walk bar and the +Selection tab, with no notification. The bar sits under the diagram, above the +bottom panel's tabs, and shrinks to "3/16", Previous and Next, the filters and +Exit below 620 px. +The place is remembered per revision in the panel's view state; nothing marks +a claim as checked. The changed-files notice offers **Review affected +claims**. The host gained a `walk` `clear` message and clears the highlight +on a blocked walk open. Checked by local jsdom and mock `vscode` tests (with +synthetic documents of the vit-cc and yolov5-cc2 shape, the real host with +the built viewer, and mutation checks) and briefly in an isolated VS Code +1.139 Extension Development Host on macOS (vit-cc beside its notebook at about +393, 543 and 902 px); not with a screen reader, on Windows or Linux, or as a +usability check ([changelog](../CHANGELOG.md)). + +Unreleased viewer M3, step 12 (the host side of the review walk) and Escape: +an `openLocation` request may carry a sequence number, a request id, a walk +flag and `highlight: false`. The host drops an open a later one has +overtaken, answers a request id with done, blocked (with the reason, for +example "train.py changed after revision r3 was published; not opened."), +cancelled or failed, and never opens a changed, missing or otherwise stale +file. A blocked walk open raises no VS Code notification; one from Enter, a +double-click or an Open link still does. A `walk` end message clears the +cited-range highlight and its overview-ruler mark, as closing the panel does. +The check behind a jump is cached per revision and freshness: 60 jumps into a +synthetic 3.8 MB notebook hashed it once instead of 60 times +([performance](PERFORMANCE.md)). An Escape the viewer used no longer also reaches VS Code (where +it hid a notification); with nothing left to close and the focus off the +diagram, Escape goes to VS Code. The M2 report of focus lost on Escape after +Enter opened a notebook was not reproduced in 14 live attempts, so this fixes +the mechanism found, not a confirmed reproduction (the same symptom in the M3 +live check came from how that check drove VS Code; see below). No contract +change, no new setting and no geometry change. Checked by local jsdom and mock `vscode` +tests and in an isolated VS Code 1.139 Extension Development Host on macOS +driven over the DevTools protocol; not on Windows or Linux, with a screen +reader or as a usability check ([changelog](../CHANGELOG.md)). + +Unreleased viewer M3, step 13 (the phase overview and the phase index): +Shift+0, or **Phase overview** in the ⋯ menu, shows every phase as a block +over the diagram: its number, name, step count and the findings that touch it, +then its step titles in the Outline's order with ◌ for inferred, ? for +unresolved and the F labels of the findings on each step. Arrows between +neighbouring blocks say how many connections go to the next phase, and +brackets on the right count the ones that skip ahead or go back. The header +reads, for vit-cc, "6 phases · 31 steps · 41 connections: 19 inside a phase, 8 +to the next phase, 14 skip ahead or go back." A block that does not fit ends +with "… N more steps", and the listed titles and those counts add up to the +document's steps. Arrow keys move between blocks; Enter or a click goes to +that phase at reading size (about 240 ms, instant under reduced motion) and +Escape returns to the diagram as it was. The labelled phase index replaces the +overview map (the minimap): a list in the lower right corner with each phase's +number, name, findings by severity and step count, the phases in view marked, +and a row going to its phase. Below 1000 px wide it is one line such as "4/6 +Objective, optimizer & scheduler" that opens the list. It sits inside the +diagram, above the bottom panel, and the card you are on is panned out from +under it. The ⋯ menu's **Phase index** shows or hides it. Shift+0 no longer +folds every group; **Fit the whole diagram** stays in the ⋯ menu. No contract +change, no new setting and no geometry change. Checked by local jsdom tests +(with synthetic documents of the vit-cc and yolov5-cc2 shape) and headless-Chrome +screenshots of a simulated host at 1440x900, 900x800 and 541x798; not in live +VS Code, with a screen reader or as a usability check +([changelog](../CHANGELOG.md)). + +Unreleased viewer M3, step 14 (Reveal in Diagram, from the code back to the +diagram): right-click a line in the editor and choose **Reveal in Diagram** +(or run **MLView: Reveal in Diagram** from the Command Palette) to show the +claim that cites it. This is the extension's second command and has no +default keybinding. The item appears on a file an open diagram cites that has +not changed since the revision was published; the `when` clause is +`resourcePath in mlview.citedFiles`, a list the extension keeps of the open +diagrams' cited, unchanged files (see the review fixes below). One claim on the +cursor's line or the selection is shown at once; several are listed (steps, +then connections, then findings, with the F label, severity, phase and where +each is cited); a line no claim cites offers the nearest claims in the file. +The diagram selects the claim, unfolds its group, brings it into view clear +of the phase index, shows it in the Selection tab (opening a hidden side +panel or bottom panel), and takes the keyboard: the one place the viewer +moves the focus by itself. A diagram hidden behind the code comes to the +front and shows the claim once its page has reloaded. With several diagrams +open, the one citing the file is used, or a list asks which. It works in +notebook cell editors. Each panel keeps an index of its validated revision's +evidence, rebuilt on a new revision and dropped with the panel; it reads no +code. The webview gained a `reveal {kind, id}` host frame for steps, +connections and findings. No hover, CodeLens, diagnostics, contract change, +new setting or geometry change. Checked by local mock `vscode` and jsdom tests +(mutation-checked, including 1440x900, 900x800 and 541x798 on the vit-cc and +yolov5-cc2 shapes) and in an isolated VS Code 1.139 Extension Development +Host on macOS driven over the DevTools protocol (vit-cc, with the diagram at +541 and 866 px, a notebook cell, a hidden panel, an unsaved edit); a changed +file was checked by unit tests only. Not with a screen reader, on Windows or +Linux, or as a usability check ([changelog](../CHANGELOG.md)). + +Unreleased viewer M3, review fixes: independent reviewers found five +behaviour problems, three record problems and eight accessibility or wording +problems, all fixed. **Reveal in Diagram** was missing from the editor's +context menu on the first right-click after Enter in the diagram, because its +context key was worked out from the active editor and was still off when the +menu was read (measured live); the `when` clause now reads a list of the open +diagrams' cited, unchanged files, and unsaved edits that moved the quotes are +explained by the command instead of hiding the item. **Review affected claims** +(and `u` after a walk on another filter) starts at the first claim instead of +after the old place. When a fixed file takes the walk's claim out of +**Changed files**, the walk shows and announces the next one (not opened by +itself) instead of naming a claim that was not on screen. A quote whose file +is stale never reads "Enter shows". Escape with a legend open over the phase +overview closes the legend first. **Review** and the **⋯** menu's item put +the keyboard on the diagram, so `j` steps at once. The walk bar has Previous +and Next buttons at every width and says when Alt+Enter moved the focus to the +editor; a late answer for a claim the walk has left is ignored; the narrow +bar's full place is screen-reader text; the filter reads **All claims**; a +blocked open is announced without "not opened" twice. The phase overview +keeps a short key below 620 px and says what a bracket's number counts; its +screen-reader text says each F label once; the phase index grows to 360 px to +show longer names, and its rows no longer repeat their name as a tooltip; the +header's Review toggle keeps one name. Checked by local jsdom and mock `vscode` +tests (each new test fails on the code before the fix) and, for the context +menu, the Previous and Next buttons, the menu's Review and the Alt+Enter +wording, in an isolated VS Code 1.139 Extension Development Host on macOS +driven over the DevTools protocol (vit-cc, the diagram at about 541 and +786 px); a changed file and the phase overview changes were not tried live. +Not with a screen reader, on Windows or Linux, or as a usability check +([changelog](../CHANGELOG.md)). + +Unreleased viewer M3, live-check fixes in the viewer: a live check of the +milestone in an isolated VS Code 1.139 Extension Development Host (vit-cc) +found five viewer problems, now fixed. The Selection tab starts each new +claim at its top (it kept the scroll of the claim before, so beside the code +neither the title nor the walk's quote showed), and the review walk then +brings the quote's file line and what the editor shows into view, keeping the +title when both fit. After a changed file is restored, the walk no longer says +its quote was not opened. A keyboard move or a walk step hides the hover card +until the pointer moves, so a card passing under a resting pointer no longer +covers the diagram. The phase overview's header, with **Back**, the counts +and the key, stays at the top while the overview scrolls. From a connection +the arrow keys select its end that lies that way (they went to the diagram's +first card); from a finding they move as from its first cited step. The +narrow walk bar is two rows at 320 px instead of four. No contract change, no +new setting or keybinding and no geometry change. Checked by local jsdom tests +(each fails on the code before the fixes) and headless-Chrome runs of a +simulated host; not yet in VS Code after the fixes, with a screen reader, on +Windows or Linux, or as a usability check ([changelog](../CHANGELOG.md)). + +Unreleased viewer M3, the live check's notebook focus loss and the walk's +selection: in the live check, the first key after Enter or a walk step opened +a notebook that was not open took the keyboard from the diagram to the +workbench (the symptom the M2 report describes). It was seen only in a window +that had never been active since it started (launched while the screen was +locked, as in that check, which ran with the display asleep) with keys sent +over the DevTools protocol: there it happened for 4 of 5 Escapes and stopped a +walk over all 79 claims at claim 50; in a window started while VS Code was the +active application it did not happen in 19 Escapes, 2 more with another +application in front, or a walk over all 79 claims. A Chromium trace shows +the notebook's output webview process moving the focus to the workbench frame +right after DevTools gives the page focus for a key; no MLView call takes +part, so MLView is unchanged for it and adds no focus recovery. Not checked by +a person at the machine, nor after switching to another application and back. +The walk still opens sources in a preview tab. One host fix: when the walk +ends, moves to a claim it opens nothing for, or does not open a quote, the +cited lines it selected are no longer left selected, if the editor still shows +them and you did not change the selection. No contract change, no new setting +or keybinding and no geometry change. Checked by mock `vscode` tests +(mutation-checked) and in an isolated VS Code 1.139 Extension Development Host +on macOS driven over the DevTools protocol, with every webview frame emulating +a focused page for the focus checks; not with a screen reader, on Windows or +Linux, or as a usability check ([changelog](../CHANGELOG.md)). + Unreleased viewer M2, live-check fixes: a live check of the branch found seven problems, now fixed. When the panel resizes or the rail changes shape and a selection that was in view no longer is, the diagram pans the least diff --git a/scripts/test_vsix_check.py b/scripts/test_vsix_check.py index 219063d6..eaeafbf8 100644 --- a/scripts/test_vsix_check.py +++ b/scripts/test_vsix_check.py @@ -70,6 +70,16 @@ def test_legacy_command_fails(tmp_path): assert any("commands" in p for p in module.check(tmp_path, package(tmp_path, commands=["mlview.visualize"]))[0]) +def test_reveal_in_diagram_command_passes(tmp_path): + vsix = package(tmp_path, commands=["mlview.openGeneratedDiagram", "mlview.revealInDiagram"]) + assert module.check(tmp_path, vsix, payload_only=True).problems == [] + + +def test_reveal_without_the_open_command_fails(tmp_path): + vsix = package(tmp_path, commands=["mlview.revealInDiagram"]) + assert any("commands" in p for p in module.check(tmp_path, vsix, payload_only=True)[0]) + + def test_retired_show_output_command_fails(tmp_path): vsix = package(tmp_path, commands=["mlview.openGeneratedDiagram", "mlview.showOutput"]) assert any("commands" in p for p in module.check(tmp_path, vsix)[0]) diff --git a/scripts/vsix_check.py b/scripts/vsix_check.py index 4fcb9f60..af030119 100644 --- a/scripts/vsix_check.py +++ b/scripts/vsix_check.py @@ -19,7 +19,8 @@ "extension/package.json", "extension/out/extension.js", "extension/media/mlview.js", "extension/media/mlview.css", "extension/LICENSE.txt", "extension/THIRD_PARTY_NOTICES.md", } -ALLOWED_COMMANDS = {"mlview.openGeneratedDiagram"} +# Viewer M3 (step 14): the owner approved one widening, MLView: Reveal in Diagram. +ALLOWED_COMMANDS = {"mlview.openGeneratedDiagram", "mlview.revealInDiagram"} # (packaged entry, working-tree source relative to the checkout, label used in messages) FRESHNESS = ( ("extension/LICENSE.txt", "LICENSE", "notice", "LICENSE"), diff --git a/vscode-extension/README.md b/vscode-extension/README.md index 720e1f6b..7815b184 100644 --- a/vscode-extension/README.md +++ b/vscode-extension/README.md @@ -64,6 +64,74 @@ the arrow keys choose one; `b` shows or hides the rail. A resize or a rail change that hides the selected card pans just enough to show it again, and a panel shown again after being hidden keeps its selection and an open sheet. Notebook cells are numbered from 0, as the artifact records them. +`r` (or **Review** in the header, or **Review the claims** in the **⋯** menu) +starts the review walk: claim by claim in the diagram's order (each step, its +outgoing connections, then the findings whose first cited step it is; +findings citing no step last), starting on the claims not observed, with +**Findings**, **All claims** and, after a cited file changed, **Changed +files** as the other filters. `j` / `k` or ↓ / ↑ (or the walk bar's up and +down buttons) move, `[` / `]` change quote, Enter opens +again, `u` / Shift+U and `n` / `p` go through the claims not observed and the +findings, and Escape or `r` ends it. About 150 ms after each step the claim's +cited lines open beside the diagram, highlighted, with the keyboard kept on +the diagram (also when the walk was started from **Review** or the **⋯** +menu); a quote whose file changed or is missing, or whose unsaved edits no +longer contain the cited lines, is not opened, and the walk bar (under the +diagram) and the Selection tab say why, with no notification; once VS Code +reports the file unchanged again, they say Enter shows it. When the walk +ends, moves to a claim it opens nothing for, or does not open a quote, the +highlight goes and the lines it selected are no longer selected (unless you +changed the selection). Each claim +starts at the top of the Selection tab, and the walk brings its quote's file +line and what the editor shows into view, with the title when both fit. A +walk step or a keyboard move hides the hover card until the pointer moves. The walk +remembers its place per revision in the panel's view state and records +nothing else; the changed-files notice offers **Review affected claims**, +which starts at the first affected claim. +Shift+0 (or **Phase overview** in the **⋯** menu) shows every phase as a block +of its step titles, with ◌ for inferred, ? for unresolved and the F labels of +findings. Arrows count the connections to the next phase, and brackets count +those that skip ahead or go back. A block that does not fit ends with "… N +more steps". The arrow keys move between blocks, Enter or a click goes to that +phase at reading size, and Escape returns to where you were (a legend opened +over it closes first). A key under the overview's header explains ◌, ? and +the brackets at every width; the header, with **Back**, stays at the top +while the overview scrolls. The phase index +in the diagram's lower right corner lists each phase with its findings and +step count and marks the phases in view; a row goes to its phase. Below 1000 +px wide it is one line such as "4/6 Objective, optimizer & scheduler" that +opens the list. It never covers the card you are on. +**Reveal in Diagram** goes from the code back to the diagram. Right-click a +line in the editor and choose it, or run **MLView: Reveal in Diagram** from +the Command Palette; there is no default keybinding, so bind one in Keyboard +Shortcuts if you want one. It is offered on a file an open diagram cites that +has not changed or gone missing since the revision was published, on the first +right-click, even straight after you pressed Enter in the diagram. The `when` +clause is `resourcePath in mlview.citedFiles`: the extension keeps that list +of the open diagrams' cited, unchanged files, so VS Code decides for the +editor under the pointer without waiting for the extension. If unsaved edits +moved the quoted lines, or a notebook cited by cell is open as text, the +command says so in one line. A file opened through another path (a symlinked +folder) is matched when the command runs, but the menu matches the path only. The +cursor's line or the selected lines decide the claim: one claim is shown at +once; several are listed as steps, then connections, then findings (with the +F label, severity, phase, a basis other than observed, and where each is +cited, counter-evidence marked); a line no claim cites lists the nearest +claims in the file, with how far each is. The diagram selects the claim, +unfolds its group, brings it into view (a connection with its ends, a finding +with its steps), shows it in the Selection tab, opening a hidden side panel or +a collapsed bottom panel, and moves the keyboard to the diagram. That is the +one time the viewer moves the focus by itself. A diagram hidden behind the +code comes to the front and shows the claim once VS Code has reloaded its +page. With several diagrams open, the one citing the file is used, or a list +asks which. In a notebook it works in the cell editors, by the cell numbers +the artifact records; a notebook cited by cell and opened as text gets a +message saying to use the notebook editor. Run where it does not apply (from +a keybinding of your own, or after the file changed), it says why in one +line. Each panel keeps an index of its revision's evidence +(files, lines, and the claims citing them), rebuilt on a new revision and +dropped with the panel; it reads no code, and adds no hover, CodeLens or +Problems entries. Enter, a double-click (on a step, connection, finding or Outline step) or a quote's **Open** link opens the cited range beside the diagram: the whole range is selected and highlighted, and focus stays in the diagram. The source goes to an editor group you already have @@ -74,6 +142,10 @@ focus to the editor. For a notebook citation the cell is selected and revealed; its lines are highlighted when VS Code has the cell's editor ready. In High Contrast themes the highlighted lines are outlined with the theme's range-highlight border, since those themes define no highlight background. +The highlight also marks the editor's overview ruler. MLView checks a cited +file once per revision and freshness check: later jumps into it read it again +only after it changes on disk, so stepping through the cells of a large +notebook does not re-read the whole notebook each time. Only inferred (dashed, `inferred` tag) and unresolved (dotted, `? unresolved` tag) steps and connections are marked; observed ones carry no mark, and the header's **N not observed** toggle fades them. Line style shows certainty, @@ -86,16 +158,22 @@ document if it fits at 62% or more, otherwise the first phase at 90%); `0` returns there, and **Fit the whole diagram** shows everything. Zoomed out, cards show only their titles, at about 11 px. The header is one row of about 36 px: the title, the assistant and revision, -search (Ctrl+F, or Cmd+F on macOS), the severity toggles, **N not observed**, a **⋯** menu -and **Refine…**, which names the step, connection or finding it will refine. +search (Ctrl+F, or Cmd+F on macOS), the severity toggles, **N not observed**, +**Review**, a **⋯** menu and **Refine…**, which names the step, connection or +finding it will refine. Below 620 px wide only the title, the severity toggles, **N not observed**, -**⋯** and **Refine…** stay in the row; search and the revision move into the -**⋯** menu, which also holds the legend, the flow animation, the overview map, -the side or bottom panel, **Fit the whole diagram**, the exports and the shortcut sheet. -The overview map item is disabled, with the reason, when the map is not drawn -(at 900 px wide or narrower, under 350 px tall, or below 30 cards). The +**⋯** and **Refine…** stay in the row; search, the revision and **Review** move +into the **⋯** menu, which always has **Review the claims** and also holds the phase overview, the legend, the flow animation, +the phase index, the side or bottom panel, **Fit the whole diagram**, the exports and the shortcut sheet. +The phase index item is disabled, with the reason, for a diagram with fewer +than two phases. The viewer's other keys are single keys, so VS Code's Ctrl and Cmd shortcuts are left alone; the shortcut sheet prints the keys for your platform. +Escape in the diagram closes the topmost thing (the menu, the shortcut sheet, +the Refine popover, the phase list, the legend, the phase overview, the review walk, the open bottom panel, focus +mode, the selection) or clears the search, and VS Code does not also act on that key +press. Once nothing is left to close and the focus is off the diagram, Escape +goes to VS Code, for example to hide a notification. The status bar reads, for example, "31 steps · 41 connections", "Coverage: scoped · 6 limitations" (which opens About), the source freshness, and the zoom. diff --git a/vscode-extension/media/mlview.css b/vscode-extension/media/mlview.css index 18b4e882..b92b4154 100644 --- a/vscode-extension/media/mlview.css +++ b/vscode-extension/media/mlview.css @@ -1 +1 @@ -:root,.mlv-root[data-theme=light]{--mlv-font: var(--vscode-font-family, ui-sans-serif, -apple-system, "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif);--mlv-font-mono: var(--vscode-editor-font-family, "Cascadia Code", "JetBrains Mono", Consolas, ui-monospace, monospace);--mlv-fs-10: 10.5px;--mlv-fs-11: 11px;--mlv-fs-12: 12px;--mlv-fs-13: 13px;--mlv-fs-14: 14px;--mlv-fs-16: 16px;--mlv-fs-20: 20px;--mlv-compact-title: 11.2px;--mlv-lh-tight: 1.35;--mlv-lh-body: 1.5;--mlv-fw-regular: 450;--mlv-fw-medium: 550;--mlv-fw-semi: 650;--mlv-track-caps: .04em;--mlv-s1: 2px;--mlv-s2: 4px;--mlv-s3: 6px;--mlv-s4: 8px;--mlv-s5: 12px;--mlv-s6: 16px;--mlv-s7: 20px;--mlv-s8: 24px;--mlv-s9: 32px;--mlv-s10: 48px;--mlv-r-chip: 4px;--mlv-r-btn: 6px;--mlv-r-node: 10px;--mlv-r-lane: 14px;--mlv-r-panel: 12px;--mlv-r-pill: 999px;--mlv-sh-1: 0 1px 2px rgba(16, 18, 24, .06), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 4px 12px rgba(16, 18, 24, .1), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 16px 40px rgba(16, 18, 24, .18), 0 0 0 1px var(--mlv-border-strong);--mlv-dur-1: .12s;--mlv-dur-2: .18s;--mlv-dur-3: .26s;--mlv-ease: cubic-bezier(.2, .8, .2, 1);--mlv-flow-speed: 220;--mlv-flow-pulse-speed: 320;--mlv-flow-head: 12px;--mlv-flow-gap: 34px;--mlv-flow-gap-call: 96px;--mlv-flow-gap-back: 24px;--mlv-flow-dur-data: .21s;--mlv-flow-dur-call: .49s;--mlv-flow-dur-back: 165ms;--mlv-flow-dur-min: .38s;--mlv-flow-dur-max: 2.2s;--mlv-flow-hop: 90ms;--mlv-flow-width: 3px;--mlv-flow-port-r: 3.5px;--mlv-flow-color: var(--mlv-accent);--mlv-flow-halo-opacity: .18;--mlv-flow-glow-opacity: .42;--mlv-flow-core-width: 1.6;--mlv-flow-underlay-opacity: .28;--mlv-node-w: 216px;--mlv-node-w-lg: 248px;--mlv-node-w-sm: 176px;--mlv-node-h: 72px;--mlv-rail-w: 4px;--mlv-group-header-h: 34px;--mlv-lane-pad: 20px;--mlv-header-h: 36px;--mlv-status-h: 22px;--mlv-rail-width: 360px;--mlv-bg: var(--vscode-editor-background, #FBFBFD);--mlv-surface: var(--vscode-editorWidget-background, #FFFFFF);--mlv-surface-2: var(--vscode-list-hoverBackground, #F3F4F8);--mlv-border: var(--vscode-widget-border, #E3E5EB);--mlv-border-strong: var(--vscode-widget-border, #C9CDD6);--mlv-text: var(--vscode-editor-foreground, #16181D);--mlv-text-2: var(--vscode-descriptionForeground, #5A6070);--mlv-text-3: var(--vscode-descriptionForeground, #676E81);--mlv-link: var(--vscode-textLink-foreground, #2B57C4);--mlv-accent: var(--vscode-focusBorder, #3B6CF6);--mlv-accent-solid: var(--vscode-button-background, #2B57C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #3B6CF6);--mlv-canvas-dot: var(--vscode-widget-border, #E6E9F0);--mlv-edge: color-mix(in srgb, var(--mlv-text) 64%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 58%, var(--mlv-bg));--mlv-scrim: rgba(16, 18, 24, .36);--mlv-phase-0: #00796B;--mlv-phase-1: #6A4FB3;--mlv-phase-2: #0A693C;--mlv-phase-3: #A0388F;--mlv-phase-4: #558B2F;--mlv-phase-5: #0B7F99;--mlv-phase-6: #546E7A;--mlv-phase-7: #1F6FB2;--mlv-stage-unknown: #7A8090;--mlv-lane-tint: .025;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #D0342C));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #E8A317));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #2F5FD0));--mlv-sev-high-ink: var(--vscode-editor-background, #FFFFFF);--mlv-sev-medium-ink: #3A2500;--mlv-sev-low-ink: var(--vscode-editor-background, #FFFFFF)}@media(prefers-color-scheme:dark){:root:not([data-theme=light]):not([data-theme=hc]),.mlv-root:not([data-theme=light]):not([data-theme=hc]){--mlv-bg: var(--vscode-editor-background, #131417);--mlv-surface: var(--vscode-editorWidget-background, #1A1C21);--mlv-surface-2: var(--vscode-list-hoverBackground, #22252C);--mlv-border: var(--vscode-widget-border, #2C3038);--mlv-border-strong: var(--vscode-widget-border, #3B414C);--mlv-text: var(--vscode-editor-foreground, #E6E8EE);--mlv-text-2: var(--vscode-descriptionForeground, #9AA1B1);--mlv-text-3: var(--vscode-descriptionForeground, #969DAD);--mlv-link: var(--vscode-textLink-foreground, #8FB0FF);--mlv-accent: var(--vscode-focusBorder, #6E96FF);--mlv-accent-solid: var(--vscode-button-background, #3355C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #6E96FF);--mlv-canvas-dot: var(--vscode-widget-border, #23262D);--mlv-edge: color-mix(in srgb, var(--mlv-text) 54%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 46%, var(--mlv-bg));--mlv-scrim: rgba(0, 0, 0, .5);--mlv-sh-1: 0 1px 2px rgba(0, 0, 0, .36), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 6px 16px rgba(0, 0, 0, .44), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 20px 48px rgba(0, 0, 0, .55), 0 0 0 1px var(--mlv-border-strong);--mlv-phase-0: #4DB6AC;--mlv-phase-1: #A48BE0;--mlv-phase-2: #4CA871;--mlv-phase-3: #D684C5;--mlv-phase-4: #9CCC65;--mlv-phase-5: #4FC3D9;--mlv-phase-6: #90A4AE;--mlv-phase-7: #6FA8DC;--mlv-stage-unknown: #8B93A7;--mlv-lane-tint: .035;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #FF6169));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #F2B03C));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #6E96FF));--mlv-sev-high-ink: #1A0405;--mlv-sev-medium-ink: #2A1A00;--mlv-sev-low-ink: #0B1020}}body.vscode-dark,:root[data-theme=dark],.mlv-root[data-theme=dark]{--mlv-bg: var(--vscode-editor-background, #131417);--mlv-surface: var(--vscode-editorWidget-background, #1A1C21);--mlv-surface-2: var(--vscode-list-hoverBackground, #22252C);--mlv-border: var(--vscode-widget-border, #2C3038);--mlv-border-strong: var(--vscode-widget-border, #3B414C);--mlv-text: var(--vscode-editor-foreground, #E6E8EE);--mlv-text-2: var(--vscode-descriptionForeground, #9AA1B1);--mlv-text-3: var(--vscode-descriptionForeground, #969DAD);--mlv-link: var(--vscode-textLink-foreground, #8FB0FF);--mlv-accent: var(--vscode-focusBorder, #6E96FF);--mlv-accent-solid: var(--vscode-button-background, #3355C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #6E96FF);--mlv-canvas-dot: var(--vscode-widget-border, #23262D);--mlv-edge: color-mix(in srgb, var(--mlv-text) 54%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 46%, var(--mlv-bg));--mlv-scrim: rgba(0, 0, 0, .5);--mlv-sh-1: 0 1px 2px rgba(0, 0, 0, .36), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 6px 16px rgba(0, 0, 0, .44), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 20px 48px rgba(0, 0, 0, .55), 0 0 0 1px var(--mlv-border-strong);--mlv-phase-0: #4DB6AC;--mlv-phase-1: #A48BE0;--mlv-phase-2: #4CA871;--mlv-phase-3: #D684C5;--mlv-phase-4: #9CCC65;--mlv-phase-5: #4FC3D9;--mlv-phase-6: #90A4AE;--mlv-phase-7: #6FA8DC;--mlv-stage-unknown: #8B93A7;--mlv-lane-tint: .035;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #FF6169));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #F2B03C));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #6E96FF));--mlv-sev-high-ink: #1A0405;--mlv-sev-medium-ink: #2A1A00;--mlv-sev-low-ink: #0B1020}body.vscode-high-contrast,body.vscode-high-contrast-light,:root[data-theme=hc],.mlv-root[data-theme=hc]{--mlv-surface: var(--vscode-editor-background, #000000);--mlv-surface-2: var(--vscode-editor-background, #000000);--mlv-bg: var(--vscode-editor-background, #000000);--mlv-text: var(--vscode-editor-foreground, #FFFFFF);--mlv-text-2: var(--vscode-editor-foreground, #FFFFFF);--mlv-text-3: var(--vscode-editor-foreground, #FFFFFF);--mlv-link: var(--vscode-textLink-foreground, #6BB7FF);--mlv-border: var(--vscode-contrastBorder, #FFFFFF);--mlv-border-strong: var(--vscode-contrastBorder, #FFFFFF);--mlv-edge: var(--vscode-contrastBorder, #FFFFFF);--mlv-node-edge: var(--vscode-contrastBorder, #FFFFFF);--mlv-canvas-dot: transparent;--mlv-lane-tint: 0;--mlv-group-tint: 0;--mlv-phase-0: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-1: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-2: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-3: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-4: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-5: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-6: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-7: var(--vscode-contrastBorder, #FFFFFF);--mlv-sh-1: 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 0 0 2px var(--mlv-border);--mlv-sh-3: 0 0 0 2px var(--mlv-border);--mlv-flow-color: var(--mlv-text);--mlv-flow-width: 4px;--mlv-flow-core-width: 2.2;--mlv-flow-underlay-opacity: .45}.mlv-root{--mlv-stale-ink: color-mix(in srgb, var(--mlv-sev-medium) 70%, var(--mlv-text))}html.mlv-fills-page,html.mlv-fills-page>body{height:100%;margin:0;padding:0;overflow:hidden}html.mlv-fills-page>body>.mlv-root{height:100%}.mlv-root{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:240px;overflow:hidden;font-family:var(--mlv-font);font-size:var(--mlv-fs-13);line-height:var(--mlv-lh-body);color:var(--mlv-text);background:var(--mlv-bg);-webkit-font-smoothing:antialiased}.mlv-root *,.mlv-root *:before,.mlv-root *:after{box-sizing:border-box}.mlv-root button,.mlv-root input,.mlv-root select{font:inherit;color:inherit}.mlv-body{position:relative;display:flex;flex:1 1 auto;min-height:0}.mlv-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.mlv-root :focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px;border-radius:var(--mlv-r-btn)}.mlv-btn{display:inline-flex;align-items:center;gap:var(--mlv-s3);min-height:24px;padding:3px var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);cursor:pointer;transition:background var(--mlv-dur-1) var(--mlv-ease),border-color var(--mlv-dur-1) var(--mlv-ease)}.mlv-btn:hover{background:var(--mlv-surface-2);border-color:var(--mlv-border-strong)}.mlv-btn--primary{background:var(--mlv-accent-solid);border-color:var(--mlv-accent-solid);color:var(--mlv-on-accent)}.mlv-root .mlv-btn--primary{color:var(--mlv-on-accent)}.mlv-btn--primary:hover{background:var(--mlv-accent-solid);filter:brightness(1.08)}body.vscode-high-contrast .mlv-root .mlv-btn--primary,body.vscode-high-contrast-light .mlv-root .mlv-btn--primary,:root[data-theme=hc] .mlv-root .mlv-btn--primary,.mlv-root[data-theme=hc] .mlv-btn--primary{border-color:var(--vscode-button-border, var(--vscode-contrastBorder, var(--mlv-text)))}.mlv-btn--icon{min-width:26px;justify-content:center;padding:3px var(--mlv-s3)}.mlv-btn[aria-pressed=true]{background:var(--mlv-surface-2);border-color:var(--mlv-border-strong)}.mlv-btn[hidden]{display:none}.mlv-chip{display:inline-flex;align-items:center;gap:var(--mlv-s2);min-height:20px;padding:1px var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-medium);white-space:nowrap}.mlv-chip[hidden]{display:none}.mlv-chip--btn{cursor:pointer;min-height:24px;padding:2px var(--mlv-s4)}.mlv-chip--btn[aria-pressed=false]{border-style:dashed;border-color:var(--mlv-text-3);text-decoration:line-through}.mlv-mono{font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11)}.mlv-link{color:var(--mlv-link);background:none;border:0;padding:0;font:inherit;text-align:left;cursor:pointer;text-decoration:underline;text-underline-offset:2px}.mlv-link--inline{padding:0;border:0;background:none;color:var(--mlv-link);font:inherit;cursor:pointer;text-decoration:underline}.mlv-input{min-width:0;padding:3px var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12)}.mlv-input::placeholder{color:var(--mlv-text-3)}.mlv-uicon,.mlv-icon{flex:none;display:block}@media(prefers-reduced-motion:reduce){.mlv-root *,.mlv-root *:before,.mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}body.vscode-reduce-motion .mlv-root *,body.vscode-reduce-motion .mlv-root *:before,body.vscode-reduce-motion .mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}body.vscode-using-screen-reader .mlv-root *,body.vscode-using-screen-reader .mlv-root *:before,body.vscode-using-screen-reader .mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}.mlv-skiplink{position:absolute;left:-9999px;top:0;z-index:100;padding:var(--mlv-s3) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);text-decoration:none}.mlv-skiplink:focus,.mlv-skiplink:focus-visible{left:var(--mlv-s4);top:var(--mlv-s4)}.mlv-main{position:relative;display:flex;flex:1 1 auto;flex-direction:column;min-width:0;min-height:0}.mlv-main>.mlv-canvas{min-height:0}.mlv-loc{display:inline-flex;align-items:baseline;min-width:0;max-width:100%}.mlv-loc__file{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.mlv-loc__at{flex:0 0 auto;white-space:pre}.mlv-header{position:relative;display:flex;align-items:center;gap:var(--mlv-s4);flex:none;min-width:0;height:var(--mlv-header-h);padding:0 var(--mlv-s4) 0 var(--mlv-s5);border-bottom:1px solid var(--mlv-border);background:var(--mlv-surface);white-space:nowrap}.mlv-header__title{flex:0 1 auto;min-width:48px;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);line-height:1.3}.mlv-header__bar{display:flex;align-items:center;gap:var(--mlv-s3);flex:1 0 auto;min-width:0}.mlv-header__spacer{flex:1 1 0;min-width:var(--mlv-s2)}.mlv-header__prov{flex:0 1 auto;min-width:0;max-width:38ch;height:22px;gap:5px;overflow:hidden;cursor:pointer;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-regular);background:transparent}.mlv-header__prov:hover{color:var(--mlv-text);border-color:var(--mlv-border-strong)}.mlv-header__prov[aria-expanded=true]{color:var(--mlv-text);border-color:var(--mlv-border-strong);background:var(--mlv-surface-2)}.mlv-header__rev{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-header__host{flex:none}.mlv-header__dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--mlv-text-3);opacity:.6}.mlv-header__prov[data-stale=true] .mlv-header__dot{background:var(--mlv-stale-ink);opacity:1}.mlv-header .mlv-chip--btn{height:24px;gap:var(--mlv-s2);padding:0 var(--mlv-s3);color:var(--mlv-text);font-size:var(--mlv-fs-12);font-variant-numeric:tabular-nums;background:transparent}.mlv-header .mlv-chip--btn:hover{background:var(--mlv-surface-2)}.mlv-chip--exceptions{white-space:nowrap}.mlv-chip--exceptions[hidden]{display:none}.mlv-chip--exceptions .mlv-uicon{color:var(--mlv-text-2)}.mlv-chip--exceptions[aria-pressed=false]{border-style:solid;border-color:var(--mlv-border);text-decoration:none}.mlv-chip--exceptions[aria-pressed=true]{border-color:var(--mlv-text-2);color:var(--mlv-text);background:var(--mlv-surface-2)}.mlv-chip--exceptions .mlv-chip__detail{color:var(--mlv-text-2)}.mlv-header__unit{flex:none;margin-left:calc(-1 * var(--mlv-s2));color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-header[data-fit="3"] .mlv-header__title,.mlv-header[data-fit="4"] .mlv-header__title{position:absolute;width:1px;min-width:0;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.mlv-header .mlv-btn--icon{width:26px;height:26px;min-height:0;padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-header .mlv-btn--icon:hover,.mlv-header .mlv-btn--icon[aria-expanded=true]{border-color:transparent;background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-header__refine{display:inline-flex;flex:none}.mlv-header .mlv-workflow__refine{height:24px;min-height:0;padding:0 var(--mlv-s4);font-weight:var(--mlv-fw-medium)}.mlv-header__searchbtn{display:none}.mlv-search{display:flex;align-items:center;flex:0 1 260px;min-width:150px;height:24px}.mlv-header[data-layout=mid] .mlv-header__host,.mlv-header[data-layout=narrow] .mlv-header__host,.mlv-header[data-layout=mid][data-search=closed] .mlv-search{display:none}.mlv-header[data-layout=mid][data-search=closed] .mlv-header__searchbtn{display:inline-flex}.mlv-header[data-layout=narrow] .mlv-header__prov,.mlv-header[data-layout=narrow][data-search=closed] .mlv-search,.mlv-header[data-layout=narrow][data-search=open] .mlv-header__title,.mlv-header[data-layout=narrow][data-search=open] .mlv-header__spacer{display:none}.mlv-header[data-layout=narrow] .mlv-search{flex:1 1 auto;min-width:96px}.mlv-header[data-fit="3"],.mlv-header[data-fit="4"]{gap:var(--mlv-s2);padding:0 var(--mlv-s3)}.mlv-header[data-fit="3"] .mlv-header__bar,.mlv-header[data-fit="4"] .mlv-header__bar{gap:var(--mlv-s2)}.mlv-search__glyph{position:relative;left:22px;margin-left:-16px;color:var(--mlv-text-3);pointer-events:none}.mlv-search .mlv-input{flex:1 1 auto;height:24px;padding-left:26px}.mlv-search__results{position:absolute;top:calc(100% + 2px);right:var(--mlv-s4);z-index:50;width:min(460px,calc(100% - 2 * var(--mlv-s4)));max-height:min(380px,60vh);overflow-y:auto;padding:var(--mlv-s2);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3);list-style:none;margin:0;white-space:normal}.mlv-search__results[hidden]{display:none}.mlv-result__count{padding:var(--mlv-s2) var(--mlv-s4) var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-result{display:grid;grid-template-columns:14px minmax(0,1fr);column-gap:var(--mlv-s3);row-gap:1px;align-items:start;width:100%;padding:var(--mlv-s3) var(--mlv-s4);border:0;border-radius:var(--mlv-r-btn);background:none;cursor:pointer;text-align:left}.mlv-result>.mlv-sev-glyph,.mlv-result>svg{grid-column:1;margin-top:2px}.mlv-result:hover,.mlv-result[aria-selected=true]{background:var(--mlv-surface-2)}.mlv-result__label{grid-column:2;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;overflow-wrap:anywhere;font-size:var(--mlv-fs-12);line-height:1.35}.mlv-result__meta{grid-column:2;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-result__jump{grid-column:2;justify-self:start}.mlv-result__empty{padding:var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-moremenu{position:fixed;z-index:60;display:flex;flex-direction:column;min-width:240px;max-width:calc(100vw - 16px);max-height:calc(100vh - 48px);overflow-y:auto;padding:var(--mlv-s2);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--vscode-menu-background, var(--mlv-surface));box-shadow:var(--mlv-sh-3);color:var(--vscode-menu-foreground, var(--mlv-text))}.mlv-moremenu[hidden],.mlv-moremenu__item[hidden],.mlv-moremenu__sep[hidden]{display:none}.mlv-moremenu__sep{height:1px;margin:var(--mlv-s2) var(--mlv-s3);background:var(--vscode-menu-separatorBackground, var(--mlv-border))}.mlv-moremenu__item{display:flex;align-items:center;gap:var(--mlv-s4);width:100%;min-height:26px;padding:2px var(--mlv-s4);border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer;font-size:var(--mlv-fs-12);text-align:left}.mlv-moremenu__item .mlv-uicon{color:var(--mlv-text-2)}.mlv-moremenu__item:hover,.mlv-moremenu__item:focus-visible{background:var(--vscode-menu-selectionBackground, var(--mlv-surface-2));color:var(--vscode-menu-selectionForeground, var(--mlv-text));outline-offset:-2px}.mlv-moremenu__item[disabled]{cursor:default;opacity:.45}.mlv-moremenu__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-moremenu__item[role=menuitemcheckbox]:after{content:"";flex:none;width:14px;text-align:center}.mlv-moremenu__item[aria-checked=true]:after{content:"\2713"}.mlv-moremenu__keys{flex:none;color:var(--mlv-text-2);font-family:inherit;font-size:var(--mlv-fs-11)}.mlv-moremenu__item[data-more-item=minimap]{flex-wrap:wrap;row-gap:0}.mlv-moremenu__item[data-more-item=minimap]:after{order:2}.mlv-moremenu__item[data-more-item=minimap][disabled]{opacity:1}.mlv-moremenu__item[data-more-item=minimap][disabled] .mlv-moremenu__label,.mlv-moremenu__note{color:var(--mlv-text-2)}.mlv-moremenu__note{order:3;flex:1 0 100%;padding-left:calc(14px + var(--mlv-s4));font-size:var(--mlv-fs-11);white-space:normal}.mlv-moremenu__note[hidden]{display:none}.mlv-banner__detail{margin-top:var(--mlv-s2);color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);white-space:pre-wrap;max-height:120px;overflow:auto}.mlv-status{display:flex;align-items:center;gap:var(--mlv-s5);flex:none;min-width:0;height:var(--mlv-status-h);padding:0 var(--mlv-s3) 0 var(--mlv-s5);border-top:1px solid var(--mlv-border);background:var(--mlv-surface);color:var(--mlv-text-2);font-size:var(--mlv-fs-11);white-space:nowrap;overflow:hidden}.mlv-status__counts{flex:0 0 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-status__coverage{flex:0 1 auto;min-width:0;height:18px;padding:0 var(--mlv-s2);overflow:hidden;text-overflow:ellipsis;border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer;font:inherit}.mlv-status__coverage[hidden]{display:none}.mlv-status__coverage:hover,.mlv-status__coverage[aria-expanded=true]{background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-status__spacer{flex:1 1 0}.mlv-status__freshness{display:inline-flex;align-items:center;gap:var(--mlv-s4);flex:0 100 auto;min-width:0;overflow:hidden}.mlv-status__fresh{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-status__fresh.is-warn,.mlv-status__freshness[data-stale=true]{flex:none}.mlv-status[data-layout=narrow] .mlv-status__coverage{flex-shrink:1000}.mlv-status[data-layout=narrow] .mlv-status__counts{flex-shrink:1}.mlv-status[data-layout=narrow]{gap:var(--mlv-s4)}.mlv-status[data-layout=narrow] .mlv-zoom__btn{display:none}.mlv-minimap{position:absolute;right:var(--mlv-s5);bottom:var(--mlv-s5);z-index:20;width:200px;border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);overflow:hidden}.mlv-minimap[hidden]{display:none}.mlv-minimap.is-collapsed{width:28px;height:28px}.mlv-minimap.is-collapsed .mlv-minimap__svg{display:none}.mlv-minimap__toggle{position:absolute;top:2px;right:2px;z-index:1;width:24px;height:24px;min-height:0;padding:0;border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text-2)}.mlv-minimap__toggle .mlv-uicon{transform:rotate(-90deg)}.mlv-minimap.is-collapsed .mlv-minimap__toggle .mlv-uicon{transform:rotate(90deg)}.mlv-minimap__svg{display:block;width:100%;height:130px;cursor:pointer}.mlv-minimap__node{fill:var(--mlv-stage, var(--mlv-stage-unknown));opacity:.75}.mlv-minimap__node--issue{stroke:var(--mlv-sev, var(--mlv-sev-low));stroke-width:1.5}.mlv-minimap__view{fill:var(--mlv-accent);fill-opacity:.12;stroke:var(--mlv-accent);stroke-width:1}.mlv-toasts{position:absolute;left:50%;bottom:var(--mlv-s7);z-index:60;display:flex;flex-direction:column;gap:var(--mlv-s3);transform:translate(-50%);pointer-events:none}.mlv-toast{min-width:240px;max-width:360px;padding:var(--mlv-s4) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);font-size:var(--mlv-fs-12);text-align:center}.mlv-state{position:absolute;inset:0;z-index:30;display:flex;align-items:center;justify-content:center;padding:var(--mlv-s9);background:var(--mlv-bg)}.mlv-state[hidden]{display:none}.mlv-state__inner{width:min(460px,100%);text-align:center}.mlv-state__title{margin:0 0 var(--mlv-s4);font-size:var(--mlv-fs-16);font-weight:var(--mlv-fw-semi)}.mlv-state__body{margin:0 0 var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-state__list{margin:0 auto var(--mlv-s5);padding:0;list-style:none;text-align:left;max-width:420px;color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-state__actions{display:flex;gap:var(--mlv-s4);justify-content:center}.mlv-btn[disabled]{opacity:.4;cursor:default}.mlv-sheet{position:absolute;inset:0;z-index:60;display:flex;align-items:center;justify-content:center}.mlv-sheet[hidden]{display:none}.mlv-sheet__scrim{position:absolute;inset:0;background:var(--mlv-scrim)}.mlv-sheet__panel{position:relative;width:min(560px,92%);max-height:min(70vh,620px);overflow-y:auto;padding:var(--mlv-s5) var(--mlv-s6) var(--mlv-s6);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3)}.mlv-sheet__head{display:flex;align-items:center;gap:var(--mlv-s4);margin-bottom:var(--mlv-s5)}.mlv-sheet__title{flex:1 1 auto;margin:0;font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi)}.mlv-sheet__list{display:grid;grid-template-columns:minmax(140px,auto) 1fr;gap:var(--mlv-s3) var(--mlv-s5);margin:0}.mlv-sheet__keys{display:flex;flex-wrap:wrap;gap:var(--mlv-s2);margin:0}.mlv-sheet__desc{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);align-self:center}.mlv-kbd{display:inline-block;padding:1px var(--mlv-s3);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);white-space:nowrap}.mlv-result__foot{display:flex;align-items:center;justify-content:space-between;gap:var(--mlv-s4);padding:var(--mlv-s3) var(--mlv-s4);border-top:1px solid var(--mlv-border);color:var(--mlv-text-3);font-size:var(--mlv-fs-10)}.mlv-result__jump{flex:none;padding:1px var(--mlv-s3);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-accent);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-medium)}.mlv-legend{position:absolute;right:var(--mlv-s5);bottom:var(--mlv-s5);z-index:45;display:flex;flex-direction:column;width:min(320px,calc(100% - 2 * var(--mlv-s5)));max-height:min(560px,calc(100% - 2 * var(--mlv-s5)));border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-shadow-panel, 0 8px 24px rgba(0, 0, 0, .18));overflow:hidden}.mlv-legend[hidden]{display:none}.mlv-legend__head{display:flex;align-items:center;justify-content:space-between;padding:var(--mlv-s4) var(--mlv-s5);border-bottom:1px solid var(--mlv-border)}.mlv-legend__title{margin:0;font-size:var(--mlv-fs-12);font-weight:var(--mlv-fw-semi)}.mlv-legend__body{overflow-y:auto;padding:var(--mlv-s4) var(--mlv-s5) var(--mlv-s5)}.mlv-legend__section+.mlv-legend__section{margin-top:var(--mlv-s5)}.mlv-legend__heading{margin:0 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);letter-spacing:var(--mlv-track-caps);text-transform:uppercase}.mlv-legend__list{display:grid;grid-template-columns:minmax(0,auto) minmax(0,1fr);gap:var(--mlv-s2) var(--mlv-s4);margin:0}.mlv-legend__term{display:flex;align-items:center;gap:var(--mlv-s3);color:var(--mlv-text);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-medium)}.mlv-legend__desc{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);line-height:var(--mlv-lh-tight)}.mlv-legend__swatch{flex:none;overflow:visible}.mlv-legend__basis{display:inline-flex;flex-direction:column;align-items:flex-start;gap:var(--mlv-s3)}.mlv-legend__desc strong{color:var(--mlv-text);font-weight:var(--mlv-fw-semi)}.mlv-legend__card{position:relative;display:inline-flex;align-items:center;min-width:44px;height:20px;padding:0 3px;box-sizing:border-box;border:1px solid var(--mlv-node-edge, var(--mlv-border));border-radius:4px;background:var(--mlv-surface)}.mlv-legend__card[data-basis=inferred]{border:1.5px dashed var(--mlv-text-2)}.mlv-legend__card[data-basis=unresolved]{border:1.5px dotted var(--mlv-text-2);background-image:repeating-linear-gradient(135deg,transparent 0 4px,color-mix(in srgb,var(--mlv-text) 8%,transparent) 4px 5px)}.mlv-legend__tag{padding:0 3px;border:1px dashed var(--mlv-text-2);border-radius:3px;background:var(--mlv-bg);color:var(--mlv-text);font-size:9px;line-height:11px;white-space:nowrap}.mlv-legend__tag[data-basis=unresolved]{border-style:dotted}.mlv-legend__phases{display:inline-flex;gap:2px}.mlv-legend__phase{width:3px;height:14px;border-radius:1.5px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-legend__short{color:var(--mlv-text);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi)}@media(max-width:900px){.mlv-legend{right:var(--mlv-s3);bottom:var(--mlv-s3);width:calc(100% - 2 * var(--mlv-s3))}}.mlv-legend__body{background-color:var(--mlv-surface);background-image:linear-gradient(to bottom,var(--mlv-surface) 30%,transparent),linear-gradient(to top,var(--mlv-surface) 30%,transparent),linear-gradient(to bottom,var(--mlv-border-strong),transparent),linear-gradient(to top,var(--mlv-border-strong),transparent);background-repeat:no-repeat;background-size:100% 24px,100% 24px,100% 8px,100% 8px;background-position:top,bottom,top,bottom;background-attachment:local,local,scroll,scroll;scrollbar-gutter:stable}.mlv-canvas{position:relative;flex:1 1 auto;min-width:0;overflow:hidden;background-color:var(--mlv-bg);background-image:radial-gradient(var(--mlv-canvas-dot) 1px,transparent 1px);background-size:20px 20px;cursor:grab;touch-action:none}.mlv-canvas[data-lod=compact]{background-image:none}.mlv-canvas.is-panning{cursor:grabbing}.mlv-world{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}.mlv-layer{position:absolute;top:0;left:0}.mlv-layer--edges{overflow:visible;pointer-events:none}.mlv-layer--nodes{pointer-events:none}.mlv-layer--nodes>*{pointer-events:auto}.mlv-lane{position:absolute;border:1px solid var(--mlv-border);border-radius:var(--mlv-r-lane);background:color-mix(in srgb,var(--mlv-text) calc(var(--mlv-lane-tint) * 100%),transparent)}.mlv-lane:before{content:"";position:absolute;top:6px;bottom:6px;left:0;width:3px;border-radius:1.5px;background:var(--mlv-stage, var(--mlv-stage-unknown));pointer-events:none}.mlv-lane__header{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;gap:var(--mlv-s4);height:var(--mlv-lane-header-h, 28px);padding:0 var(--mlv-s5);border-bottom:1px solid var(--mlv-border);border-radius:calc(var(--mlv-r-lane) - 1px) calc(var(--mlv-r-lane) - 1px) 0 0;background:color-mix(in srgb,var(--mlv-text) 5%,var(--mlv-surface))}.mlv-lane__header:before{content:"";flex:none;width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-lane__num{flex:none;margin-right:calc(-1 * var(--mlv-s2));color:var(--mlv-text-2);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-lane__label{flex:0 1 auto;min-width:48px;overflow:hidden;color:var(--mlv-text);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);text-overflow:ellipsis;white-space:nowrap}.mlv-lane__count{flex:none;color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums;white-space:nowrap}.mlv-lane__spacer{flex:1 1 auto}.mlv-lane__header .mlv-cluster{flex:none}.mlv-lane__unit:before{content:"\b7 "}.mlv-lane__unit{flex:0 100 auto;min-width:0;overflow:hidden;margin-left:calc(-1 * var(--mlv-s2));color:var(--mlv-text-3);font-size:var(--mlv-fs-10);text-overflow:ellipsis;white-space:nowrap}.mlv-group{position:absolute;border:1px solid var(--mlv-border-strong);border-radius:12px;background:color-mix(in srgb,var(--mlv-text) calc(var(--mlv-group-tint) * 100%),transparent)}.mlv-group[data-basis=inferred]{border:calc(1.5px / var(--mlv-z, 1)) dashed var(--mlv-text-2)}.mlv-group[data-basis=unresolved]{border:calc(1.5px / var(--mlv-z, 1)) dotted var(--mlv-text-2)}.mlv-group__header{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;height:var(--mlv-group-header-h);padding:0 var(--mlv-s4);border:0;border-radius:12px 12px 0 0;background:none;color:var(--mlv-text);cursor:pointer;text-align:left}.mlv-group__header:hover{background:var(--mlv-surface-2)}.mlv-group__chevron{transition:transform var(--mlv-dur-2) var(--mlv-ease);color:var(--mlv-text-2)}.mlv-group.is-collapsed .mlv-group__chevron{transform:rotate(0)}.mlv-group__chevron{transform:rotate(90deg)}.mlv-group__name{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-12);font-weight:var(--mlv-fw-semi)}.mlv-group__count{flex:none;padding:0 var(--mlv-s3);border-radius:var(--mlv-r-pill);background:var(--mlv-surface-2);color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums}.mlv-group.is-dim,.mlv-lane.is-dim{opacity:.5}.mlv-canvas.is-focusing .mlv-node:not(.is-lit){opacity:.22;filter:saturate(.3);pointer-events:none}.mlv-canvas.is-focusing .mlv-edge:not(.is-lit){opacity:.16}.mlv-canvas.is-tracing .mlv-node.is-lit{box-shadow:0 0 0 max(1.5px,calc(1px / var(--mlv-z, 1))) var(--mlv-text-2)}.mlv-tooltip{position:absolute;z-index:50;max-width:320px;padding:var(--mlv-s4) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3);font-size:var(--mlv-fs-12);pointer-events:none}.mlv-tooltip[hidden]{display:none}.mlv-tooltip__title{font-weight:var(--mlv-fw-semi)}.mlv-tooltip__row{margin-top:var(--mlv-s2);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-tooltip__loc{font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);color:var(--mlv-text-3)}.mlv-zoom{display:inline-flex;align-items:center;flex:none;gap:1px}.mlv-zoom__level{min-width:38px;text-align:center;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-variant-numeric:tabular-nums}.mlv-zoom .mlv-zoom__btn{width:20px;height:18px;min-width:0;min-height:0;padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-zoom .mlv-zoom__btn:hover{background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-layer--lanes,.mlv-lane,.mlv-group{pointer-events:none}.mlv-lane__header,.mlv-group__header{pointer-events:auto}.mlv-statehost{position:absolute;inset:0;z-index:30;pointer-events:none}.mlv-statehost>*{pointer-events:auto}.mlv-group__chevron-btn{display:inline-flex;align-items:center;justify-content:center;flex:none;width:24px;height:24px;margin-left:-4px;padding:0;border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer}.mlv-group__chevron-btn:hover{background:var(--mlv-surface-2)}.mlv-node{position:absolute;display:flex;align-items:stretch;gap:0;overflow:visible;border:1px solid var(--mlv-node-edge, var(--mlv-border));border-radius:var(--mlv-r-node);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1);cursor:pointer;text-align:left;padding:0;transition:box-shadow var(--mlv-dur-1) var(--mlv-ease),transform var(--mlv-dur-1) var(--mlv-ease)}.mlv-node__rail{flex:none;width:var(--mlv-rail-w);border-radius:var(--mlv-r-node) 0 0 var(--mlv-r-node);background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-node__main{display:flex;align-items:flex-start;gap:var(--mlv-s4);flex:1 1 auto;min-width:0;padding:10px var(--mlv-s5) 10px var(--mlv-s4);overflow:hidden}.mlv-node__iconbox{flex:none;display:flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:6px;background:var(--mlv-surface-2);color:var(--mlv-text-2)}.mlv-node__text{flex:1 1 auto;min-width:0}.mlv-node__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);line-height:var(--mlv-lh-tight)}.mlv-node__sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);line-height:var(--mlv-lh-tight)}.mlv-node__sub[data-lines="2"]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow-wrap:anywhere;white-space:normal}.mlv-node__loc{display:flex;overflow:hidden;white-space:nowrap;color:var(--mlv-text-3);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-node__chips{display:flex;gap:var(--mlv-s2);margin-top:var(--mlv-s2);overflow:hidden}.mlv-node__chips .mlv-chip{display:inline-block;flex:0 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;line-height:18px}.mlv-node:hover{box-shadow:var(--mlv-sh-2);border-color:var(--mlv-border-strong);transform:translateY(-1px);z-index:3}.mlv-node.is-selected{box-shadow:var(--mlv-sh-2),0 0 0 2px var(--mlv-accent),0 0 0 6px color-mix(in srgb,var(--mlv-accent) 14%,transparent);z-index:4}.mlv-node.is-selected .mlv-node__title{font-weight:700}.mlv-node.has-issues{border-color:color-mix(in srgb,var(--mlv-sev) 55%,var(--mlv-node-edge, var(--mlv-border)));--mlv-basis-ink: color-mix(in srgb, var(--mlv-sev) 70%, var(--mlv-text))}.mlv-node.is-stale{border-color:var(--mlv-stale-ink);--mlv-basis-ink: var(--mlv-stale-ink)}.mlv-node[data-basis=inferred],.mlv-node[data-basis=unresolved]{border-color:transparent}.mlv-node[data-basis=inferred]:after,.mlv-node[data-basis=unresolved]:after{content:"";position:absolute;inset:-1px;border:calc(1.5px / var(--mlv-z, 1)) dashed var(--mlv-basis-ink, var(--mlv-text-2));border-radius:inherit;pointer-events:none}.mlv-node[data-basis=unresolved]:after{border-style:dotted}.mlv-node[data-basis=unresolved]{background-image:repeating-linear-gradient(135deg,transparent 0 7px,color-mix(in srgb,var(--mlv-text) 8%,transparent) 7px 8px)}.mlv-basis-tag{display:inline-flex;align-items:center;height:14px;padding:0 4px;border:1px dashed var(--mlv-basis-ink, var(--mlv-text-2));border-radius:3px;background:var(--mlv-bg);color:var(--mlv-text);font-size:10px;font-weight:var(--mlv-fw-regular);line-height:1;white-space:nowrap}.mlv-basis-tag[data-basis=unresolved]{border-style:dotted}.mlv-node>.mlv-basis-tag{position:absolute;top:100%;left:10px;z-index:5;transform-origin:0 50%;transform:translateY(-50%) scale(calc(1 / var(--mlv-z, 1)));pointer-events:none}.mlv-group__header .mlv-basis-tag{flex:none}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed]{border-color:color-mix(in srgb,var(--mlv-node-edge, var(--mlv-border)) 35%,var(--mlv-bg));background:var(--mlv-bg);box-shadow:none}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed] .mlv-node__rail{background:color-mix(in srgb,var(--mlv-stage, var(--mlv-stage-unknown)) 30%,var(--mlv-bg))}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed] .mlv-node__iconbox{background:transparent}.mlv-node__stale{position:absolute;top:-7px;left:-7px;z-index:5;display:flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:var(--mlv-r-pill);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1);color:var(--mlv-stale-ink)}.mlv-group__header .mlv-node__stale{position:static;box-shadow:none;flex:none}.mlv-node.is-pulse{animation:mlv-pulse .3s var(--mlv-ease) 2}@keyframes mlv-pulse{0%,to{box-shadow:var(--mlv-sh-2),0 0 0 2px var(--mlv-accent)}50%{box-shadow:var(--mlv-sh-2),0 0 0 7px color-mix(in srgb,var(--mlv-accent) 30%,transparent)}}.mlv-canvas[data-lod=compact] .mlv-node__sub,.mlv-canvas[data-lod=compact] .mlv-node__loc,.mlv-canvas[data-lod=compact] .mlv-node__chips,.mlv-canvas[data-lod=compact] .mlv-node__iconbox{display:none}.mlv-canvas[data-lod=compact] .mlv-node__main{container-type:size;align-items:center;padding:2px 8px}.mlv-canvas[data-lod=compact] .mlv-node__title{font-size:min(calc(var(--mlv-compact-title) / var(--mlv-z, 1)),calc(100cqh / 2.3));line-height:1.15}.mlv-canvas[data-lod=compact] .mlv-node>.mlv-basis-tag{transform-origin:0 0;transform:translateY(-1px) scale(calc(1 / var(--mlv-z, 1)))}.mlv-canvas[data-lod=compact] .mlv-edge-label{display:none}.mlv-glyph{display:block;overflow:visible}.mlv-glyph__shape{fill:var(--mlv-sev-marker, var(--mlv-sev-low))}.mlv-glyph__ink{fill:var(--mlv-sev-marker-ink, #FFFFFF)}.mlv-glyph--low{--mlv-sev-marker: var(--mlv-sev-low);--mlv-sev-marker-ink: var(--mlv-sev-low-ink)}.mlv-glyph--medium{--mlv-sev-marker: var(--mlv-sev-medium);--mlv-sev-marker-ink: var(--mlv-sev-medium-ink)}.mlv-glyph--high{--mlv-sev-marker: var(--mlv-sev-high);--mlv-sev-marker-ink: var(--mlv-sev-high-ink)}body.vscode-high-contrast .mlv-glyph__shape,body.vscode-high-contrast-light .mlv-glyph__shape,:root[data-theme=hc] .mlv-glyph__shape,.mlv-root[data-theme=hc] .mlv-glyph__shape{fill:none;stroke:var(--mlv-sev-marker, currentColor);stroke-width:2}body.vscode-high-contrast .mlv-glyph__ink,body.vscode-high-contrast-light .mlv-glyph__ink,:root[data-theme=hc] .mlv-glyph__ink,.mlv-root[data-theme=hc] .mlv-glyph__ink{fill:var(--mlv-sev-marker, currentColor)}.mlv-badge{position:absolute;top:-6px;right:-6px;z-index:5;display:flex;align-items:center;gap:var(--mlv-s1);height:20px;padding:0 var(--mlv-s2);border-radius:var(--mlv-r-pill);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1)}.mlv-badge--high{border:1px solid color-mix(in srgb,var(--mlv-sev-high) 45%,transparent)}.mlv-badge--medium{border:1px solid color-mix(in srgb,var(--mlv-sev-medium) 45%,transparent)}.mlv-badge--low{border:1px solid color-mix(in srgb,var(--mlv-sev-low) 45%,transparent)}.mlv-badge__count,.mlv-badge__ids{color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-badge__ids{padding-right:2px;color:var(--mlv-text);white-space:nowrap}.mlv-cluster{display:inline-flex;align-items:center;gap:var(--mlv-s3)}.mlv-cluster__item{display:inline-flex;align-items:center;gap:2px}.mlv-cluster__count{color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums}[data-stage]{--mlv-stage: var(--mlv-stage-unknown)}[data-phase-tone="0"]{--mlv-stage: var(--mlv-phase-0)}[data-phase-tone="1"]{--mlv-stage: var(--mlv-phase-1)}[data-phase-tone="2"]{--mlv-stage: var(--mlv-phase-2)}[data-phase-tone="3"]{--mlv-stage: var(--mlv-phase-3)}[data-phase-tone="4"]{--mlv-stage: var(--mlv-phase-4)}[data-phase-tone="5"]{--mlv-stage: var(--mlv-phase-5)}[data-phase-tone="6"]{--mlv-stage: var(--mlv-phase-6)}[data-phase-tone="7"]{--mlv-stage: var(--mlv-phase-7)}[data-sev=high]{--mlv-sev: var(--mlv-sev-high)}[data-sev=medium]{--mlv-sev: var(--mlv-sev-medium)}[data-sev=low]{--mlv-sev: var(--mlv-sev-low)}.mlv-node:focus-visible,.mlv-group__header:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px;border-radius:var(--mlv-r-node)}.mlv-node__title--wrap{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;line-clamp:1;white-space:normal;overflow-wrap:anywhere;text-overflow:clip}.mlv-node__title--wrap[data-lines="2"]{-webkit-line-clamp:2;line-clamp:2}.mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3;line-clamp:3}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap{-webkit-line-clamp:2;line-clamp:2}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3;line-clamp:3;font-size:min(calc(var(--mlv-compact-title) / var(--mlv-z, 1)),calc(100cqh / 3.45))}.mlv-edge{pointer-events:none}.mlv-edge__hit{fill:none;stroke:transparent;stroke-width:12;pointer-events:stroke;cursor:pointer}.mlv-edge__path,.mlv-legend__edge{fill:none;stroke:var(--mlv-edge);stroke-width:max(1.5px,calc(1px / var(--mlv-z, 1)));stroke-linecap:round;stroke-linejoin:round;transition:stroke var(--mlv-dur-1) var(--mlv-ease)}.mlv-edge[data-basis=inferred] .mlv-edge__path,.mlv-edge[data-basis=inferred] .mlv-legend__edge{stroke-dasharray:calc(6px / var(--mlv-z, 1)) calc(4px / var(--mlv-z, 1));stroke-linecap:butt}.mlv-edge[data-basis=unresolved] .mlv-edge__path,.mlv-edge[data-basis=unresolved] .mlv-legend__edge{stroke-dasharray:calc(1.5px / var(--mlv-z, 1)) calc(3.5px / var(--mlv-z, 1));stroke-linecap:round}.mlv-arrow{fill:var(--mlv-edge);stroke:none}.mlv-edge.is-hover .mlv-edge__path,.mlv-edge.is-lit .mlv-edge__path{stroke:var(--mlv-text-2);stroke-width:max(2px,calc(1.5px / var(--mlv-z, 1)))}.mlv-edge.is-selected .mlv-edge__path{stroke:var(--mlv-accent);stroke-width:max(2.5px,calc(2px / var(--mlv-z, 1)))}.mlv-edge.has-issue .mlv-edge__path{stroke:var(--mlv-sev, var(--mlv-edge));opacity:.85}.mlv-edge-label{font-family:var(--mlv-font-mono);font-size:10px;fill:var(--mlv-text-2);paint-order:stroke;stroke:var(--mlv-bg);stroke-width:3;stroke-linejoin:round;text-anchor:middle;dominant-baseline:middle;opacity:0;transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-edge.is-hover .mlv-edge-label,.mlv-edge.is-selected .mlv-edge-label,.mlv-edge.is-lit .mlv-edge-label,.mlv-edge.has-label .mlv-edge-label,.mlv-edge--merged .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--data .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--control .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--state .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--loop .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--output .mlv-edge-label{opacity:1}.mlv-canvas[data-lod=compact] .mlv-edge-label{opacity:0}.mlv-edge-marker__disc{fill:var(--mlv-surface);stroke:var(--mlv-sev-marker, var(--mlv-sev-low));stroke-width:1}.mlv-edge-marker--high{--mlv-sev-marker: var(--mlv-sev-high)}.mlv-edge-marker--medium{--mlv-sev-marker: var(--mlv-sev-medium)}.mlv-edge-marker--low{--mlv-sev-marker: var(--mlv-sev-low)}.mlv-connector{fill:none;stroke:var(--mlv-sev, var(--mlv-accent));stroke-width:1.5;stroke-dasharray:2 4}.mlv-connector__badge{fill:var(--mlv-surface);stroke:var(--mlv-sev, var(--mlv-accent));stroke-width:1}.mlv-connector__label{font-family:var(--mlv-font-mono);font-size:9px;fill:var(--mlv-text);text-anchor:middle;dominant-baseline:central}.mlv-bundles{pointer-events:none}.mlv-bundle__trunk,.mlv-bundle__spur{fill:none;stroke:var(--mlv-edge);stroke-linecap:round;stroke-linejoin:round;transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-bundle__trunk{stroke-width:3.5;opacity:.72}.mlv-bundle__spur{stroke-width:1.5;opacity:.45}.mlv-bundle.has-issue .mlv-bundle__trunk,.mlv-bundle.has-issue .mlv-bundle__spur{stroke:var(--mlv-sev, var(--mlv-edge))}.mlv-bundle__badge-box{fill:var(--mlv-surface);stroke:var(--mlv-edge);stroke-width:1;opacity:.95}.mlv-bundle.has-issue .mlv-bundle__badge-box{stroke:var(--mlv-sev, var(--mlv-edge))}.mlv-bundle__badge-text{font-family:var(--mlv-font-mono);font-size:10px;fill:var(--mlv-text-2);text-anchor:middle;dominant-baseline:central}.mlv-bundle__badge{transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-bundle.is-expanded .mlv-bundle__trunk,.mlv-bundle.is-expanded .mlv-bundle__spur,.mlv-bundle.is-expanded .mlv-bundle__badge,.mlv-canvas .mlv-edge.is-bundled .mlv-edge__path,.mlv-canvas .mlv-edge.is-bundled .mlv-edge-label{opacity:0}.mlv-canvas .mlv-edge.is-bundled[data-basis=inferred] .mlv-edge__path,.mlv-canvas .mlv-edge.is-bundled[data-basis=unresolved] .mlv-edge__path{opacity:1}.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=observed]:not(.is-hover):not(.is-selected):not(.is-lit) .mlv-edge__path{stroke-opacity:.3}.mlv-canvas[data-exceptions=on] .mlv-bundle__trunk,.mlv-canvas[data-exceptions=on] .mlv-bundle__spur{stroke-opacity:.3}.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=inferred] .mlv-edge__path,.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=unresolved] .mlv-edge__path{stroke:var(--mlv-text)}.mlv-canvas[data-lod=compact] .mlv-bundle__badge{opacity:0}.mlv-edge__stalebg{fill:var(--mlv-surface);stroke:var(--mlv-stale-ink);stroke-width:1}.mlv-edge__staleicon{stroke:var(--mlv-stale-ink);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}.mlv-edge__stale{pointer-events:none}.mlv-edge[data-stage]:not(.has-issue){--mlv-flow-color: var(--mlv-stage)}.mlv-edge.has-issue{--mlv-flow-color: var(--mlv-sev)}body.vscode-high-contrast .mlv-edge[data-stage],body.vscode-high-contrast .mlv-edge.has-issue,body.vscode-high-contrast-light .mlv-edge[data-stage],body.vscode-high-contrast-light .mlv-edge.has-issue,:root[data-theme=hc] .mlv-edge[data-stage],:root[data-theme=hc] .mlv-edge.has-issue,.mlv-root[data-theme=hc] .mlv-edge[data-stage],.mlv-root[data-theme=hc] .mlv-edge.has-issue{--mlv-flow-color: var(--mlv-text)}.mlv-edge__flow{fill:none;stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:var(--mlv-flow-width, 3px);stroke-linecap:round;pointer-events:none}.mlv-edge.is-flowing--pulse .mlv-edge__flow{stroke-dasharray:none;stroke-opacity:var(--mlv-flow-underlay-opacity, .28)}.mlv-edge__charge{pointer-events:none;animation:mlv-charge-ends var(--mlv-flow-dur, .9s) linear infinite;animation-iteration-count:var(--mlv-flow-iter, infinite);animation-fill-mode:forwards}.mlv-edge__charge--twin{animation-delay:calc(var(--mlv-flow-dur, .9s) / -2)}@keyframes mlv-charge-ends{0%{opacity:0}8%{opacity:1}92%{opacity:1}to{opacity:0}}.mlv-edge__charge-halo{fill:var(--mlv-flow-color, var(--mlv-accent));fill-opacity:var(--mlv-flow-halo-opacity, .18);stroke:none}.mlv-edge__charge-glow{fill:var(--mlv-flow-color, var(--mlv-accent));fill-opacity:var(--mlv-flow-glow-opacity, .42);stroke:none}.mlv-edge__charge-core{fill:var(--mlv-surface);stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:var(--mlv-flow-core-width, 1.6)}body.vscode-high-contrast .mlv-edge__charge-halo,body.vscode-high-contrast-light .mlv-edge__charge-halo,:root[data-theme=hc] .mlv-edge__charge-halo,.mlv-root[data-theme=hc] .mlv-edge__charge-halo{fill:none;stroke:var(--mlv-text);stroke-width:1.2}.mlv-edge.is-flowing .mlv-edge__flow{stroke-dasharray:var(--mlv-flow-head) var(--mlv-flow-gap);animation:mlv-flow-stream var(--mlv-flow-dur-data) linear infinite;animation-delay:var(--mlv-flow-delay, 0ms);animation-iteration-count:var(--mlv-flow-iter, infinite)}.mlv-edge--call.is-flowing .mlv-edge__flow,.mlv-edge--control.is-flowing .mlv-edge__flow{animation-duration:var(--mlv-flow-dur-call)}.mlv-edge--control.mlv-edge--back.is-flowing .mlv-edge__flow{animation-duration:var(--mlv-flow-dur-back)}@keyframes mlv-flow-stream{0%{stroke-dashoffset:0}to{stroke-dashoffset:var(--mlv-flow-end, -20)}}.mlv-edge__port{stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:1.4;pointer-events:none}.mlv-edge__port--out{fill:var(--mlv-surface)}.mlv-edge__port--in{fill:var(--mlv-flow-color, var(--mlv-accent));stroke:var(--mlv-surface);stroke-width:1.4}.mlv-canvas[data-flow=motion] .mlv-edge.is-flowing:not(.is-flow-settled) .mlv-edge__path,.mlv-canvas[data-flow=motion] .mlv-edge.is-flowing--pulse:not(.is-flow-settled) .mlv-edge__path{marker-end:none}.mlv-canvas[data-flow=static] .mlv-edge.is-flowing--pulse .mlv-edge__path,.mlv-edge.is-flowing--pulse.is-flow-settled .mlv-edge__path{stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:max(2.5px,calc(2px / var(--mlv-z, 1)))}.mlv-edge__dir{fill:none;stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}.mlv-node.is-flow-source,.mlv-group.is-flow-source{box-shadow:0 0 0 2px var(--mlv-stage, var(--mlv-accent))}.mlv-node.is-flow-target,.mlv-group.is-flow-target{box-shadow:0 0 0 2px var(--mlv-surface),0 0 0 4px var(--mlv-accent)}@media(prefers-reduced-motion:reduce){.mlv-edge__flow,.mlv-edge__charge{display:none!important}}body.vscode-reduce-motion .mlv-edge__flow,body.vscode-reduce-motion .mlv-edge__charge,body.vscode-using-screen-reader .mlv-edge__flow,body.vscode-using-screen-reader .mlv-edge__charge{display:none!important}.mlv-canvas[data-flow=off] .mlv-edge__flow,.mlv-canvas[data-flow=off] .mlv-edge__charge,.mlv-canvas[data-flow=off] .mlv-edge__port,.mlv-canvas[data-flow=off] .mlv-edge__dir{display:none}.mlv-rail{position:relative;display:flex;flex-direction:column;flex:none;width:var(--mlv-rail-width);min-width:280px;max-width:560px;border-left:1px solid var(--mlv-border);background:var(--mlv-surface)}.mlv-rail[hidden]{display:none}.mlv-rail__grip{position:absolute;top:0;left:-2px;width:5px;height:100%;cursor:col-resize;z-index:5}.mlv-rail__grip:after{content:"";position:absolute;left:2px;top:0;width:1px;height:100%;background:transparent}.mlv-rail__grip:hover:after{background:var(--mlv-accent)}.mlv-rail__bar{display:flex;align-items:stretch;flex:none;border-bottom:1px solid var(--mlv-border)}.mlv-rail__tabs{display:flex;flex:1 1 auto;min-width:0;height:36px}.mlv-rail__tab{flex:1 1 auto;min-width:0;padding:0 var(--mlv-s3);white-space:nowrap;border:0;border-bottom:2px solid transparent;background:none;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);cursor:pointer}.mlv-rail__tab[aria-selected=true]{color:var(--mlv-text);border-bottom-color:var(--mlv-accent);font-weight:var(--mlv-fw-semi)}.mlv-rail__tab:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__tabcount{color:var(--mlv-text-2);font-variant-numeric:tabular-nums}.mlv-rail__chevron{flex:none;align-self:center;width:26px;height:26px;min-height:0;margin:0 var(--mlv-s2);padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-rail__chevron[hidden]{display:none}.mlv-rail__chevron .mlv-uicon{transform:rotate(90deg)}.mlv-rail__chevron[aria-expanded=false] .mlv-uicon{transform:rotate(-90deg)}.mlv-rail__panel:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__count{color:var(--mlv-text-3);font-weight:var(--mlv-fw-regular);text-transform:none;letter-spacing:0}.mlv-body[data-rail=sheet]{flex-direction:column}.mlv-body[data-rail=sheet]>.mlv-main{flex:1 1 0;min-height:0}.mlv-rail[data-mode=sheet]{flex:0 0 calc(var(--mlv-sheet-fraction, .47) * 100%);width:auto;min-width:0;max-width:none;min-height:32px;max-height:calc(100% - min(240px,45%));border-left:0;border-top:1px solid var(--mlv-border-strong)}.mlv-rail[data-mode=sheet][data-expanded=false]{flex-basis:32px;height:32px}.mlv-rail[data-mode=sheet] .mlv-rail__tabs{height:31px}.mlv-rail[data-mode=sheet][data-expanded=false] .mlv-rail__bar{border-bottom:0}.mlv-rail[data-mode=sheet]>.mlv-rail__grip{top:-4px;left:0;width:100%;height:9px;cursor:row-resize}.mlv-rail[data-mode=sheet]>.mlv-rail__grip:after{left:50%;top:2px;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--mlv-border-strong)}.mlv-rail[data-mode=sheet]>.mlv-rail__grip:hover:after,.mlv-rail[data-mode=sheet]>.mlv-rail__grip:focus-visible:after{background:var(--mlv-accent)}.mlv-rail__grip:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__panel{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--mlv-s4)}.mlv-rail__panel[hidden]{display:none}.mlv-rail__section{margin:0 0 var(--mlv-s5)}.mlv-rail__heading{display:flex;align-items:center;gap:var(--mlv-s3);margin:0 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);text-transform:uppercase;letter-spacing:var(--mlv-track-caps)}.mlv-issues{list-style:none;margin:0;padding:0}.mlv-issues__item{display:flex;flex-wrap:wrap;align-items:flex-start}.mlv-issue{display:flex;align-items:flex-start;gap:var(--mlv-s4);flex:1 1 0;min-width:0;padding:var(--mlv-s4);border:1px solid transparent;border-radius:var(--mlv-r-btn);background:none;cursor:pointer;text-align:left}.mlv-issue:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-issue:hover{background:var(--mlv-surface-2)}.mlv-issue.is-selected{border-color:var(--mlv-accent);background:var(--mlv-surface-2)}.mlv-issue__text{flex:1 1 auto;min-width:0}.mlv-issue__short,.mlv-insp__short{flex:none;color:var(--mlv-text);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-issue__title{font-size:12.5px;font-weight:var(--mlv-fw-medium);line-height:var(--mlv-lh-tight)}.mlv-issue__meta{display:flex;align-items:center;gap:var(--mlv-s3);margin-top:var(--mlv-s1);color:var(--mlv-text-3);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-issue__open{flex:none;align-self:center}.mlv-issue__detail{flex:1 0 100%}.mlv-empty-note{padding:var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12);text-align:center}.mlv-sel[data-columns="2"]{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);column-gap:var(--mlv-s7, 20px);align-items:start}.mlv-sel__col{min-width:0}.mlv-insp__eyebrow{display:flex;flex-wrap:wrap;align-items:center;gap:0 var(--mlv-s2);margin:0 0 var(--mlv-s2);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__phase{display:inline-flex;align-items:center;gap:var(--mlv-s2);color:var(--mlv-text);font-weight:var(--mlv-fw-medium)}.mlv-insp__phasedot{width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-insp__sep{color:var(--mlv-text-3)}.mlv-insp__severity{color:var(--mlv-text)}.mlv-insp__title{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi);line-height:var(--mlv-lh-tight);overflow-wrap:anywhere}.mlv-insp__ends{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-12);overflow-wrap:anywhere}.mlv-insp__section{margin:var(--mlv-s5) 0 0}.mlv-sel__col--evidence>.mlv-insp__section:first-child{margin-top:0}.mlv-insp__issue-title{flex:0 1 auto;min-width:0;color:var(--mlv-text);text-align:left;overflow-wrap:anywhere}.mlv-insp__issue-head .mlv-mono{color:var(--mlv-text-3)}.mlv-insp__issue-id{margin:calc(-1 * var(--mlv-s2)) 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-insp__note,.mlv-insp__aside{color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__links,.mlv-insp__sentences{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--mlv-s2);font-size:var(--mlv-fs-12);line-height:1.45}.mlv-insp__flow-basis{color:var(--mlv-text-2)}.mlv-quote{padding:var(--mlv-s3) 0 0;border-top:1px solid var(--mlv-border)}.mlv-quote__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--mlv-s1) var(--mlv-s3);font-size:var(--mlv-fs-11)}.mlv-quote__num{flex:none;min-width:1.4em;color:var(--mlv-text-2);font-variant-numeric:tabular-nums;font-weight:var(--mlv-fw-semi)}.mlv-quote__loc{flex:1 0 calc(100% - 1.4em - var(--mlv-s3));min-width:0;color:var(--mlv-text);font-family:var(--mlv-font-mono);overflow-wrap:anywhere}.mlv-quote__loc+*{margin-left:calc(1.4em + var(--mlv-s3))}.mlv-quote__at{color:var(--mlv-text-2)}.mlv-quote__role{color:var(--mlv-text-2);font-style:italic}.mlv-quote__fresh.is-muted{color:var(--mlv-text-2)}.mlv-quote__fresh.mlv-insp__stale{margin-left:0}.mlv-quote__open{flex:none}.mlv-quote__text{margin:var(--mlv-s2) 0 0;padding:var(--mlv-s2) var(--mlv-s3);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);line-height:1.5;overflow-x:auto;white-space:pre;user-select:text}.mlv-quote__line{display:inline}.mlv-quote__ln{display:inline-block;min-width:3ch;margin-right:var(--mlv-s3);color:var(--mlv-text-3);text-align:right;user-select:none}.mlv-insp__evidence-nav{display:flex;gap:var(--mlv-s2);margin:0 0 var(--mlv-s3)}.mlv-about__section{margin:0 0 var(--mlv-s6);font-size:var(--mlv-fs-12);line-height:1.5}.mlv-about__question{margin:0;color:var(--mlv-text);font-size:13px;white-space:pre-wrap;overflow-wrap:anywhere}.mlv-about__question[data-clamped=true]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden}.mlv-about__more{margin-top:var(--mlv-s2);font-size:var(--mlv-fs-11)}.mlv-about__para,.mlv-about__text,.mlv-about__status{margin:0 0 var(--mlv-s3);color:var(--mlv-text);white-space:pre-wrap;overflow-wrap:anywhere}.mlv-about__lead{font-weight:var(--mlv-fw-semi)}.mlv-about__meta,.mlv-about__note{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__kv{padding:0 3px;border-radius:3px;background:var(--mlv-surface-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__limitations{color:var(--mlv-text)}.mlv-about__limitations summary{cursor:pointer;color:var(--mlv-text);font-weight:var(--mlv-fw-medium)}.mlv-about__limitations ul{margin:var(--mlv-s2) 0 0;padding-left:20px}.mlv-about__limitations li{margin-bottom:var(--mlv-s2);overflow-wrap:anywhere}.mlv-about__files{list-style:none;margin:0 0 var(--mlv-s3);padding:0}.mlv-about__file{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 var(--mlv-s3);padding:2px 0}.mlv-about__path{flex:1 1 auto;min-width:0;font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__file .mlv-quote__fresh{flex:none;font-size:var(--mlv-fs-11)}.mlv-about__trust{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__issue{margin-bottom:var(--mlv-s5);padding:var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface-2)}.mlv-insp__issue-head{display:flex;align-items:center;gap:var(--mlv-s3);margin-bottom:var(--mlv-s3);font-weight:var(--mlv-fw-medium)}.mlv-insp__line{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-11);color:var(--mlv-text-2)}.mlv-insp__fix{margin:0 0 var(--mlv-s3);padding-left:var(--mlv-s4);border-left:3px solid var(--mlv-accent);font-size:var(--mlv-fs-11)}.mlv-insp__fix-label{margin:0 0 2px;color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);letter-spacing:.04em;text-transform:uppercase}.mlv-insp__fix-text{margin:0;color:var(--mlv-text);white-space:pre-wrap;overflow-wrap:anywhere}.mlv-insp__detail{margin:0 0 var(--mlv-s4);color:var(--mlv-text);font-size:var(--mlv-fs-12);line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;user-select:text}.mlv-insp__basis{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__basis .mlv-basis-tag{vertical-align:1px}.mlv-insp__caption{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__limits{margin:var(--mlv-s4) 0 0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__related{list-style:none;margin:var(--mlv-s3) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--mlv-s2)}.mlv-outline,.mlv-outline ul{list-style:none;margin:0;padding:0 0 0 var(--mlv-s5)}.mlv-outline{padding-left:0}.mlv-outline__item{outline:none}.mlv-outline__item:focus>.mlv-outline__row{box-shadow:0 0 0 2px var(--mlv-focus)}.mlv-outline__row{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;padding:2px var(--mlv-s3);border:0;border-radius:var(--mlv-r-chip);background:none;cursor:pointer;text-align:left;font-size:var(--mlv-fs-12)}.mlv-outline__row--lane{font-weight:var(--mlv-fw-semi)}.mlv-outline__chevron{flex:none;width:12px;height:12px;color:var(--mlv-text-3);transform:rotate(90deg);transition:transform var(--mlv-dur-1, .12s) ease}.mlv-outline__item[aria-expanded=false]>.mlv-outline__row .mlv-outline__chevron{transform:rotate(0)}.mlv-outline__chevron--none{transform:none}.mlv-outline__row:hover{background:var(--mlv-surface-2)}.mlv-outline__row.is-selected{background:var(--mlv-surface-2);font-weight:var(--mlv-fw-medium)}.mlv-outline__label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-outline__stage{color:var(--mlv-text-3);font-size:var(--mlv-fs-10);text-transform:uppercase;letter-spacing:var(--mlv-track-caps)}.mlv-outline__row--lane{flex-wrap:wrap}.mlv-outline__row--lane .mlv-outline__label{flex:1 1 0;min-width:0}.mlv-outline__phasecount{flex:1 0 100%;font-weight:var(--mlv-fw-regular);text-transform:none;letter-spacing:normal}.mlv-basis-tag--rail{flex:none;vertical-align:middle}.mlv-relations{margin-top:var(--mlv-s6);padding-top:var(--mlv-s4);border-top:1px solid var(--mlv-border)}.mlv-relations__context{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-relations__views{display:flex;gap:var(--mlv-s2);margin-bottom:var(--mlv-s3)}.mlv-relations__views .mlv-btn[aria-pressed=true]{border-color:var(--mlv-accent);color:var(--mlv-text)}.mlv-relations__list{margin:0;padding:0;list-style:none}.mlv-relations__item+.mlv-relations__item{margin-top:var(--mlv-s2)}.mlv-relations__row{display:block;width:100%;min-height:32px;padding:var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface-2);color:var(--mlv-text);text-align:left;cursor:pointer}.mlv-relations__row:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px}.mlv-relations__path,.mlv-relations__detail{display:block}.mlv-relations__detail{margin-top:var(--mlv-s1);color:var(--mlv-text-3);font-size:var(--mlv-fs-10)}.mlv-insp__actions{display:flex;gap:var(--mlv-s3);flex-wrap:wrap;margin-bottom:var(--mlv-s4)}.mlv-clean__title{color:var(--mlv-text);font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi)}.mlv-clean__detail{font-size:var(--mlv-fs-12);max-width:34ch}.mlv-issue__detail{padding:var(--mlv-s3) var(--mlv-s5) var(--mlv-s5) var(--mlv-s8);border-left:2px solid var(--mlv-accent);margin:0 var(--mlv-s3) var(--mlv-s3);background:var(--mlv-surface-2);border-radius:0 var(--mlv-r-btn) var(--mlv-r-btn) 0}.mlv-issue__detail .mlv-insp__line{font-size:var(--mlv-fs-12)}.mlv-issue__goto{display:flex;flex-wrap:wrap;gap:var(--mlv-s2);margin-top:var(--mlv-s4)}.mlv-empty-note .mlv-btn{margin-top:var(--mlv-s4)}.mlv-insp__issue.is-selected{box-shadow:0 0 0 2px var(--mlv-accent)}.mlv-issue__meta{flex-wrap:wrap;row-gap:var(--mlv-s1);min-width:0}.mlv-issue__meta .mlv-chip{max-width:100%}.mlv-edge--weighted .mlv-edge__path{stroke-width:max(var(--mlv-edge-w, 2px),calc(1px / var(--mlv-z, 1)))}.mlv-edge__weight{pointer-events:none}.mlv-edge__weightbg{fill:var(--mlv-surface);stroke:var(--mlv-border-strong);stroke-width:1}.mlv-edge__weighttext{fill:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:9px;font-variant-numeric:tabular-nums;text-anchor:middle;dominant-baseline:middle}.mlv-edge.has-issue .mlv-edge__weightbg{stroke:var(--mlv-sev, var(--mlv-border-strong))}.mlv-canvas[data-lod=compact] .mlv-edge__weight{display:none}:root[data-theme=hc] .mlv-edge__weightbg,.mlv-root[data-theme=hc] .mlv-edge__weightbg{stroke:currentColor}.mlv-workflow{position:relative;z-index:45;flex:none;height:0}.mlv-workflow__composer{position:absolute;top:4px;right:var(--mlv-s4);display:flex;flex-wrap:wrap;align-items:center;gap:8px;width:min(440px,calc(100vw - 2 * var(--mlv-s4)));padding:10px 12px;border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3)}.mlv-workflow__composer[hidden]{display:none}.mlv-workflow__selection{flex:1 0 100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text);font-size:12px;font-weight:var(--mlv-fw-medium)}.mlv-workflow__intent{flex:0 0 auto}.mlv-workflow__custom{flex:1 1 180px;min-width:0}.mlv-workflow__custom[hidden]{display:none}.mlv-workflow__status{flex:1 0 100%;color:var(--mlv-text);font-size:11px}.mlv-workflow__status:empty{display:none}.mlv-workflow__hint{flex:1 0 100%;margin:0;color:var(--mlv-text-2);font-size:11px}#mlview-authored-error{white-space:pre-wrap;margin:0;padding:6px 12px;flex:none;max-height:20vh;overflow-y:auto;overscroll-behavior:contain;font-family:var(--mlv-font, var(--vscode-font-family, system-ui, sans-serif));font-size:12px;line-height:1.45;color:var(--mlv-text, var(--vscode-foreground, inherit));background:var(--mlv-surface-2, var(--vscode-editorWidget-background, transparent));border-bottom:1px solid var(--mlv-sev-medium, var(--vscode-editorWarning-foreground, currentColor))}.mlv-hostnotice{display:flex;align-items:flex-start;gap:var(--mlv-s3);flex:none;padding:6px 12px;font-size:12px;line-height:1.45;color:var(--mlv-text);background:var(--mlv-surface-2);border-bottom:1px solid var(--mlv-border)}.mlv-hostnotice[hidden]{display:none}.mlv-hostnotice--warn{border-bottom-color:var(--mlv-sev-medium)}.mlv-hostnotice--error{border-bottom-color:var(--mlv-sev-high)}.mlv-hostnotice__icon{flex:none;display:flex;padding-top:2px}.mlv-hostnotice--warn .mlv-hostnotice__icon{color:var(--mlv-sev-medium)}.mlv-hostnotice--error .mlv-hostnotice__icon{color:var(--mlv-sev-high)}.mlv-hostnotice__text{flex:1 1 auto;min-width:0;margin:0;white-space:pre-wrap;overflow-wrap:anywhere;max-height:20vh;overflow-y:auto;overscroll-behavior:contain}.mlv-hostnotice__actions{flex:none;display:flex;flex-wrap:wrap;gap:6px}.mlv-hostnotice__actions[hidden]{display:none}.mlv-status__fresh{display:inline-flex;align-items:center;gap:4px}.mlv-status__fresh.is-warn{color:var(--mlv-text)}.mlv-status__fresh.is-warn .mlv-uicon{color:var(--mlv-stale-ink)}.mlv-status__fresh.is-checking{color:var(--mlv-text-2);font-style:italic}.mlv-status__fresh.is-muted{color:var(--mlv-text-2)}.mlv-insp__stale-note{display:flex;align-items:flex-start;gap:6px;margin:4px 0 6px;font-size:11px;color:var(--mlv-text)}.mlv-insp__stale-note .mlv-uicon,.mlv-insp__stale .mlv-uicon,.mlv-chip--stale .mlv-uicon{flex:none;color:var(--mlv-stale-ink)}.mlv-insp__stale{display:inline-flex;align-items:center;gap:4px;margin-left:6px;font-size:11px;color:var(--mlv-text)}.mlv-link.is-stale,.mlv-btn.is-stale{cursor:not-allowed;color:var(--mlv-text-2);text-decoration:line-through}.mlv-chip--stale{display:inline-flex;align-items:center;gap:3px}.mlv-sheet__note{margin:0 0 8px;font-size:12px;color:var(--mlv-text-2)}.mlv-root--workflow>.mlv-body{flex:1 1 0;min-height:min(320px,50vh)}.mlv-minimap.is-short,.mlv-minimap.is-narrow{display:none}@page{margin:12mm}@media print{.mlv-header,.mlv-workflow,.mlv-hostnotice,.mlv-status,.mlv-rail,.mlv-minimap,.mlv-zoom,.mlv-skiplink,.mlv-legend,.mlv-moremenu,.mlv-statehost,.mlv-tooltip,.mlv-toasts,.mlv-shortcuts{display:none!important}html.mlv-fills-page,html.mlv-fills-page>body,.mlv-root,.mlv-body,.mlv-main,.mlv-canvas{display:block!important;height:auto!important;max-height:none!important;min-height:0!important;overflow:visible!important}.mlv-world{position:static!important;transform:none!important;transform-origin:0 0!important}.mlv-canvas{background-image:none!important;cursor:auto!important}.mlv-canvas[data-lod=compact] .mlv-node__sub,.mlv-canvas[data-lod=compact] .mlv-node__loc{display:block!important}.mlv-canvas[data-lod=compact] .mlv-node__sub[data-lines="2"]{display:-webkit-box!important}.mlv-canvas[data-lod=compact] .mlv-node__chips{display:flex!important}.mlv-canvas[data-lod=compact] .mlv-node__title{font-size:var(--mlv-fs-13)!important;line-height:var(--mlv-lh-tight)!important}.mlv-canvas[data-lod=compact] .mlv-node__iconbox{display:flex!important}.mlv-canvas[data-lod=compact] .mlv-node__main{align-items:flex-start!important;padding:10px var(--mlv-s5) 10px var(--mlv-s4)!important}.mlv-canvas[data-lod=compact] .mlv-node>.mlv-basis-tag{transform-origin:0 50%!important;transform:translateY(-50%) scale(calc(1 / var(--mlv-z, 1)))!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap{-webkit-line-clamp:1!important;line-clamp:1!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="2"]{-webkit-line-clamp:2!important;line-clamp:2!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3!important;line-clamp:3!important}.mlv-canvas[data-lod=compact] .mlv-edge-label{display:inline!important;opacity:0!important}.mlv-canvas[data-lod=compact] .mlv-edge--data .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge--control .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge--merged .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge.has-label .mlv-edge-label{opacity:1!important}.mlv-root{-webkit-print-color-adjust:exact;print-color-adjust:exact}.mlv-edge__flow,.mlv-flow-charge{display:none!important}.mlv-node,.mlv-group,.mlv-lane{break-inside:avoid}} +:root,.mlv-root[data-theme=light]{--mlv-font: var(--vscode-font-family, ui-sans-serif, -apple-system, "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif);--mlv-font-mono: var(--vscode-editor-font-family, "Cascadia Code", "JetBrains Mono", Consolas, ui-monospace, monospace);--mlv-fs-10: 10.5px;--mlv-fs-11: 11px;--mlv-fs-12: 12px;--mlv-fs-13: 13px;--mlv-fs-14: 14px;--mlv-fs-16: 16px;--mlv-fs-20: 20px;--mlv-compact-title: 11.2px;--mlv-lh-tight: 1.35;--mlv-lh-body: 1.5;--mlv-fw-regular: 450;--mlv-fw-medium: 550;--mlv-fw-semi: 650;--mlv-track-caps: .04em;--mlv-s1: 2px;--mlv-s2: 4px;--mlv-s3: 6px;--mlv-s4: 8px;--mlv-s5: 12px;--mlv-s6: 16px;--mlv-s7: 20px;--mlv-s8: 24px;--mlv-s9: 32px;--mlv-s10: 48px;--mlv-r-chip: 4px;--mlv-r-btn: 6px;--mlv-r-node: 10px;--mlv-r-lane: 14px;--mlv-r-panel: 12px;--mlv-r-pill: 999px;--mlv-sh-1: 0 1px 2px rgba(16, 18, 24, .06), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 4px 12px rgba(16, 18, 24, .1), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 16px 40px rgba(16, 18, 24, .18), 0 0 0 1px var(--mlv-border-strong);--mlv-dur-1: .12s;--mlv-dur-2: .18s;--mlv-dur-3: .26s;--mlv-ease: cubic-bezier(.2, .8, .2, 1);--mlv-flow-speed: 220;--mlv-flow-pulse-speed: 320;--mlv-flow-head: 12px;--mlv-flow-gap: 34px;--mlv-flow-gap-call: 96px;--mlv-flow-gap-back: 24px;--mlv-flow-dur-data: .21s;--mlv-flow-dur-call: .49s;--mlv-flow-dur-back: 165ms;--mlv-flow-dur-min: .38s;--mlv-flow-dur-max: 2.2s;--mlv-flow-hop: 90ms;--mlv-flow-width: 3px;--mlv-flow-port-r: 3.5px;--mlv-flow-color: var(--mlv-accent);--mlv-flow-halo-opacity: .18;--mlv-flow-glow-opacity: .42;--mlv-flow-core-width: 1.6;--mlv-flow-underlay-opacity: .28;--mlv-node-w: 216px;--mlv-node-w-lg: 248px;--mlv-node-w-sm: 176px;--mlv-node-h: 72px;--mlv-rail-w: 4px;--mlv-group-header-h: 34px;--mlv-lane-pad: 20px;--mlv-header-h: 36px;--mlv-status-h: 22px;--mlv-rail-width: 360px;--mlv-bg: var(--vscode-editor-background, #FBFBFD);--mlv-surface: var(--vscode-editorWidget-background, #FFFFFF);--mlv-surface-2: var(--vscode-list-hoverBackground, #F3F4F8);--mlv-border: var(--vscode-widget-border, #E3E5EB);--mlv-border-strong: var(--vscode-widget-border, #C9CDD6);--mlv-text: var(--vscode-editor-foreground, #16181D);--mlv-text-2: var(--vscode-descriptionForeground, #5A6070);--mlv-text-3: var(--vscode-descriptionForeground, #676E81);--mlv-link: var(--vscode-textLink-foreground, #2B57C4);--mlv-accent: var(--vscode-focusBorder, #3B6CF6);--mlv-accent-solid: var(--vscode-button-background, #2B57C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #3B6CF6);--mlv-canvas-dot: var(--vscode-widget-border, #E6E9F0);--mlv-edge: color-mix(in srgb, var(--mlv-text) 64%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 58%, var(--mlv-bg));--mlv-scrim: rgba(16, 18, 24, .36);--mlv-phase-0: #00796B;--mlv-phase-1: #6A4FB3;--mlv-phase-2: #0A693C;--mlv-phase-3: #A0388F;--mlv-phase-4: #558B2F;--mlv-phase-5: #0B7F99;--mlv-phase-6: #546E7A;--mlv-phase-7: #1F6FB2;--mlv-stage-unknown: #7A8090;--mlv-lane-tint: .025;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #D0342C));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #E8A317));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #2F5FD0));--mlv-sev-high-ink: var(--vscode-editor-background, #FFFFFF);--mlv-sev-medium-ink: #3A2500;--mlv-sev-low-ink: var(--vscode-editor-background, #FFFFFF)}@media(prefers-color-scheme:dark){:root:not([data-theme=light]):not([data-theme=hc]),.mlv-root:not([data-theme=light]):not([data-theme=hc]){--mlv-bg: var(--vscode-editor-background, #131417);--mlv-surface: var(--vscode-editorWidget-background, #1A1C21);--mlv-surface-2: var(--vscode-list-hoverBackground, #22252C);--mlv-border: var(--vscode-widget-border, #2C3038);--mlv-border-strong: var(--vscode-widget-border, #3B414C);--mlv-text: var(--vscode-editor-foreground, #E6E8EE);--mlv-text-2: var(--vscode-descriptionForeground, #9AA1B1);--mlv-text-3: var(--vscode-descriptionForeground, #969DAD);--mlv-link: var(--vscode-textLink-foreground, #8FB0FF);--mlv-accent: var(--vscode-focusBorder, #6E96FF);--mlv-accent-solid: var(--vscode-button-background, #3355C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #6E96FF);--mlv-canvas-dot: var(--vscode-widget-border, #23262D);--mlv-edge: color-mix(in srgb, var(--mlv-text) 54%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 46%, var(--mlv-bg));--mlv-scrim: rgba(0, 0, 0, .5);--mlv-sh-1: 0 1px 2px rgba(0, 0, 0, .36), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 6px 16px rgba(0, 0, 0, .44), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 20px 48px rgba(0, 0, 0, .55), 0 0 0 1px var(--mlv-border-strong);--mlv-phase-0: #4DB6AC;--mlv-phase-1: #A48BE0;--mlv-phase-2: #4CA871;--mlv-phase-3: #D684C5;--mlv-phase-4: #9CCC65;--mlv-phase-5: #4FC3D9;--mlv-phase-6: #90A4AE;--mlv-phase-7: #6FA8DC;--mlv-stage-unknown: #8B93A7;--mlv-lane-tint: .035;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #FF6169));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #F2B03C));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #6E96FF));--mlv-sev-high-ink: #1A0405;--mlv-sev-medium-ink: #2A1A00;--mlv-sev-low-ink: #0B1020}}body.vscode-dark,:root[data-theme=dark],.mlv-root[data-theme=dark]{--mlv-bg: var(--vscode-editor-background, #131417);--mlv-surface: var(--vscode-editorWidget-background, #1A1C21);--mlv-surface-2: var(--vscode-list-hoverBackground, #22252C);--mlv-border: var(--vscode-widget-border, #2C3038);--mlv-border-strong: var(--vscode-widget-border, #3B414C);--mlv-text: var(--vscode-editor-foreground, #E6E8EE);--mlv-text-2: var(--vscode-descriptionForeground, #9AA1B1);--mlv-text-3: var(--vscode-descriptionForeground, #969DAD);--mlv-link: var(--vscode-textLink-foreground, #8FB0FF);--mlv-accent: var(--vscode-focusBorder, #6E96FF);--mlv-accent-solid: var(--vscode-button-background, #3355C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #6E96FF);--mlv-canvas-dot: var(--vscode-widget-border, #23262D);--mlv-edge: color-mix(in srgb, var(--mlv-text) 54%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 46%, var(--mlv-bg));--mlv-scrim: rgba(0, 0, 0, .5);--mlv-sh-1: 0 1px 2px rgba(0, 0, 0, .36), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 6px 16px rgba(0, 0, 0, .44), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 20px 48px rgba(0, 0, 0, .55), 0 0 0 1px var(--mlv-border-strong);--mlv-phase-0: #4DB6AC;--mlv-phase-1: #A48BE0;--mlv-phase-2: #4CA871;--mlv-phase-3: #D684C5;--mlv-phase-4: #9CCC65;--mlv-phase-5: #4FC3D9;--mlv-phase-6: #90A4AE;--mlv-phase-7: #6FA8DC;--mlv-stage-unknown: #8B93A7;--mlv-lane-tint: .035;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #FF6169));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #F2B03C));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #6E96FF));--mlv-sev-high-ink: #1A0405;--mlv-sev-medium-ink: #2A1A00;--mlv-sev-low-ink: #0B1020}body.vscode-high-contrast,body.vscode-high-contrast-light,:root[data-theme=hc],.mlv-root[data-theme=hc]{--mlv-surface: var(--vscode-editor-background, #000000);--mlv-surface-2: var(--vscode-editor-background, #000000);--mlv-bg: var(--vscode-editor-background, #000000);--mlv-text: var(--vscode-editor-foreground, #FFFFFF);--mlv-text-2: var(--vscode-editor-foreground, #FFFFFF);--mlv-text-3: var(--vscode-editor-foreground, #FFFFFF);--mlv-link: var(--vscode-textLink-foreground, #6BB7FF);--mlv-border: var(--vscode-contrastBorder, #FFFFFF);--mlv-border-strong: var(--vscode-contrastBorder, #FFFFFF);--mlv-edge: var(--vscode-contrastBorder, #FFFFFF);--mlv-node-edge: var(--vscode-contrastBorder, #FFFFFF);--mlv-canvas-dot: transparent;--mlv-lane-tint: 0;--mlv-group-tint: 0;--mlv-phase-0: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-1: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-2: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-3: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-4: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-5: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-6: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-7: var(--vscode-contrastBorder, #FFFFFF);--mlv-sh-1: 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 0 0 2px var(--mlv-border);--mlv-sh-3: 0 0 0 2px var(--mlv-border);--mlv-flow-color: var(--mlv-text);--mlv-flow-width: 4px;--mlv-flow-core-width: 2.2;--mlv-flow-underlay-opacity: .45}.mlv-root{--mlv-stale-ink: color-mix(in srgb, var(--mlv-sev-medium) 70%, var(--mlv-text))}html.mlv-fills-page,html.mlv-fills-page>body{height:100%;margin:0;padding:0;overflow:hidden}html.mlv-fills-page>body>.mlv-root{height:100%}.mlv-root{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:240px;overflow:hidden;font-family:var(--mlv-font);font-size:var(--mlv-fs-13);line-height:var(--mlv-lh-body);color:var(--mlv-text);background:var(--mlv-bg);-webkit-font-smoothing:antialiased}.mlv-root *,.mlv-root *:before,.mlv-root *:after{box-sizing:border-box}.mlv-root button,.mlv-root input,.mlv-root select{font:inherit;color:inherit}.mlv-body{position:relative;display:flex;flex:1 1 auto;min-height:0}.mlv-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.mlv-root :focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px;border-radius:var(--mlv-r-btn)}.mlv-btn{display:inline-flex;align-items:center;gap:var(--mlv-s3);min-height:24px;padding:3px var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);cursor:pointer;transition:background var(--mlv-dur-1) var(--mlv-ease),border-color var(--mlv-dur-1) var(--mlv-ease)}.mlv-btn:hover{background:var(--mlv-surface-2);border-color:var(--mlv-border-strong)}.mlv-btn--primary{background:var(--mlv-accent-solid);border-color:var(--mlv-accent-solid);color:var(--mlv-on-accent)}.mlv-root .mlv-btn--primary{color:var(--mlv-on-accent)}.mlv-btn--primary:hover{background:var(--mlv-accent-solid);filter:brightness(1.08)}body.vscode-high-contrast .mlv-root .mlv-btn--primary,body.vscode-high-contrast-light .mlv-root .mlv-btn--primary,:root[data-theme=hc] .mlv-root .mlv-btn--primary,.mlv-root[data-theme=hc] .mlv-btn--primary{border-color:var(--vscode-button-border, var(--vscode-contrastBorder, var(--mlv-text)))}.mlv-btn--icon{min-width:26px;justify-content:center;padding:3px var(--mlv-s3)}.mlv-btn[aria-pressed=true]{background:var(--mlv-surface-2);border-color:var(--mlv-border-strong)}.mlv-btn[hidden]{display:none}.mlv-chip{display:inline-flex;align-items:center;gap:var(--mlv-s2);min-height:20px;padding:1px var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-medium);white-space:nowrap}.mlv-chip[hidden]{display:none}.mlv-chip--btn{cursor:pointer;min-height:24px;padding:2px var(--mlv-s4)}.mlv-chip--btn[aria-pressed=false]{border-style:dashed;border-color:var(--mlv-text-3);text-decoration:line-through}.mlv-mono{font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11)}.mlv-link{color:var(--mlv-link);background:none;border:0;padding:0;font:inherit;text-align:left;cursor:pointer;text-decoration:underline;text-underline-offset:2px}.mlv-link--inline{padding:0;border:0;background:none;color:var(--mlv-link);font:inherit;cursor:pointer;text-decoration:underline}.mlv-input{min-width:0;padding:3px var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12)}.mlv-input::placeholder{color:var(--mlv-text-3)}.mlv-uicon,.mlv-icon{flex:none;display:block}@media(prefers-reduced-motion:reduce){.mlv-root *,.mlv-root *:before,.mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}body.vscode-reduce-motion .mlv-root *,body.vscode-reduce-motion .mlv-root *:before,body.vscode-reduce-motion .mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}body.vscode-using-screen-reader .mlv-root *,body.vscode-using-screen-reader .mlv-root *:before,body.vscode-using-screen-reader .mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}.mlv-skiplink{position:absolute;left:-9999px;top:0;z-index:100;padding:var(--mlv-s3) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);text-decoration:none}.mlv-skiplink:focus,.mlv-skiplink:focus-visible{left:var(--mlv-s4);top:var(--mlv-s4)}.mlv-main{position:relative;display:flex;flex:1 1 auto;flex-direction:column;min-width:0;min-height:0}.mlv-main>.mlv-canvas{min-height:0}.mlv-loc{display:inline-flex;align-items:baseline;min-width:0;max-width:100%}.mlv-loc__file{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.mlv-loc__at{flex:0 0 auto;white-space:pre}.mlv-header{position:relative;display:flex;align-items:center;gap:var(--mlv-s4);flex:none;min-width:0;height:var(--mlv-header-h);padding:0 var(--mlv-s4) 0 var(--mlv-s5);border-bottom:1px solid var(--mlv-border);background:var(--mlv-surface);white-space:nowrap}.mlv-header__title{flex:0 1 auto;min-width:48px;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);line-height:1.3}.mlv-header__bar{display:flex;align-items:center;gap:var(--mlv-s3);flex:1 0 auto;min-width:0}.mlv-header__spacer{flex:1 1 0;min-width:var(--mlv-s2)}.mlv-header__prov{flex:0 1 auto;min-width:0;max-width:38ch;height:22px;gap:5px;overflow:hidden;cursor:pointer;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-regular);background:transparent}.mlv-header__prov:hover{color:var(--mlv-text);border-color:var(--mlv-border-strong)}.mlv-header__prov[aria-expanded=true]{color:var(--mlv-text);border-color:var(--mlv-border-strong);background:var(--mlv-surface-2)}.mlv-header__rev{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-header__host{flex:none}.mlv-header__dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--mlv-text-3);opacity:.6}.mlv-header__prov[data-stale=true] .mlv-header__dot{background:var(--mlv-stale-ink);opacity:1}.mlv-header .mlv-chip--btn{height:24px;gap:var(--mlv-s2);padding:0 var(--mlv-s3);color:var(--mlv-text);font-size:var(--mlv-fs-12);font-variant-numeric:tabular-nums;background:transparent}.mlv-header .mlv-chip--btn:hover{background:var(--mlv-surface-2)}.mlv-chip--exceptions{white-space:nowrap}.mlv-chip--exceptions[hidden]{display:none}.mlv-chip--exceptions .mlv-uicon{color:var(--mlv-text-2)}.mlv-chip--exceptions[aria-pressed=false]{border-style:solid;border-color:var(--mlv-border);text-decoration:none}.mlv-chip--exceptions[aria-pressed=true]{border-color:var(--mlv-text-2);color:var(--mlv-text);background:var(--mlv-surface-2)}.mlv-chip--exceptions .mlv-chip__detail{color:var(--mlv-text-2)}.mlv-header__unit{flex:none;margin-left:calc(-1 * var(--mlv-s2));color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-header[data-fit="3"] .mlv-header__title,.mlv-header[data-fit="4"] .mlv-header__title{position:absolute;width:1px;min-width:0;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.mlv-header .mlv-btn--icon{width:26px;height:26px;min-height:0;padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-header .mlv-btn--icon:hover,.mlv-header .mlv-btn--icon[aria-expanded=true]{border-color:transparent;background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-header__refine{display:inline-flex;flex:none}.mlv-header .mlv-workflow__refine{height:24px;min-height:0;padding:0 var(--mlv-s4);font-weight:var(--mlv-fw-medium)}.mlv-header__searchbtn{display:none}.mlv-search{display:flex;align-items:center;flex:0 1 260px;min-width:150px;height:24px}.mlv-header[data-layout=mid] .mlv-header__host,.mlv-header[data-layout=narrow] .mlv-header__host,.mlv-header[data-layout=mid][data-search=closed] .mlv-search{display:none}.mlv-header[data-layout=mid][data-search=closed] .mlv-header__searchbtn{display:inline-flex}.mlv-header[data-layout=narrow] .mlv-header__prov,.mlv-header[data-layout=narrow][data-search=closed] .mlv-search,.mlv-header[data-layout=narrow][data-search=open] .mlv-header__title,.mlv-header[data-layout=narrow][data-search=open] .mlv-header__spacer{display:none}.mlv-header[data-layout=narrow] .mlv-search{flex:1 1 auto;min-width:96px}.mlv-header[data-fit="3"],.mlv-header[data-fit="4"]{gap:var(--mlv-s2);padding:0 var(--mlv-s3)}.mlv-header[data-fit="3"] .mlv-header__bar,.mlv-header[data-fit="4"] .mlv-header__bar{gap:var(--mlv-s2)}.mlv-search__glyph{position:relative;left:22px;margin-left:-16px;color:var(--mlv-text-3);pointer-events:none}.mlv-search .mlv-input{flex:1 1 auto;height:24px;padding-left:26px}.mlv-search__results{position:absolute;top:calc(100% + 2px);right:var(--mlv-s4);z-index:50;width:min(460px,calc(100% - 2 * var(--mlv-s4)));max-height:min(380px,60vh);overflow-y:auto;padding:var(--mlv-s2);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3);list-style:none;margin:0;white-space:normal}.mlv-search__results[hidden]{display:none}.mlv-result__count{padding:var(--mlv-s2) var(--mlv-s4) var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-result{display:grid;grid-template-columns:14px minmax(0,1fr);column-gap:var(--mlv-s3);row-gap:1px;align-items:start;width:100%;padding:var(--mlv-s3) var(--mlv-s4);border:0;border-radius:var(--mlv-r-btn);background:none;cursor:pointer;text-align:left}.mlv-result>.mlv-sev-glyph,.mlv-result>svg{grid-column:1;margin-top:2px}.mlv-result:hover,.mlv-result[aria-selected=true]{background:var(--mlv-surface-2)}.mlv-result__label{grid-column:2;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;overflow-wrap:anywhere;font-size:var(--mlv-fs-12);line-height:1.35}.mlv-result__meta{grid-column:2;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-result__jump{grid-column:2;justify-self:start}.mlv-result__empty{padding:var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-moremenu{position:fixed;z-index:60;display:flex;flex-direction:column;min-width:240px;max-width:calc(100vw - 16px);max-height:calc(100vh - 48px);overflow-y:auto;padding:var(--mlv-s2);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--vscode-menu-background, var(--mlv-surface));box-shadow:var(--mlv-sh-3);color:var(--vscode-menu-foreground, var(--mlv-text))}.mlv-moremenu[hidden],.mlv-moremenu__item[hidden],.mlv-moremenu__sep[hidden]{display:none}.mlv-moremenu__sep{height:1px;margin:var(--mlv-s2) var(--mlv-s3);background:var(--vscode-menu-separatorBackground, var(--mlv-border))}.mlv-moremenu__item{display:flex;align-items:center;gap:var(--mlv-s4);width:100%;min-height:26px;padding:2px var(--mlv-s4);border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer;font-size:var(--mlv-fs-12);text-align:left}.mlv-moremenu__item .mlv-uicon{color:var(--mlv-text-2)}.mlv-moremenu__item:hover,.mlv-moremenu__item:focus-visible{background:var(--vscode-menu-selectionBackground, var(--mlv-surface-2));color:var(--vscode-menu-selectionForeground, var(--mlv-text));outline-offset:-2px}.mlv-moremenu__item[disabled]{cursor:default;opacity:.45}.mlv-moremenu__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-moremenu__item[role=menuitemcheckbox]:after{content:"";flex:none;width:14px;text-align:center}.mlv-moremenu__item[aria-checked=true]:after{content:"\2713"}.mlv-moremenu__keys{flex:none;color:var(--mlv-text-2);font-family:inherit;font-size:var(--mlv-fs-11)}.mlv-moremenu__item[data-more-item=phaseindex]{flex-wrap:wrap;row-gap:0}.mlv-moremenu__item[data-more-item=phaseindex]:after{order:2}.mlv-moremenu__item[data-more-item=phaseindex][disabled]{opacity:1}.mlv-moremenu__item[data-more-item=phaseindex][disabled] .mlv-moremenu__label,.mlv-moremenu__note{color:var(--mlv-text-2)}.mlv-moremenu__note{order:3;flex:1 0 100%;padding-left:calc(14px + var(--mlv-s4));font-size:var(--mlv-fs-11);white-space:normal}.mlv-moremenu__note[hidden]{display:none}.mlv-banner__detail{margin-top:var(--mlv-s2);color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);white-space:pre-wrap;max-height:120px;overflow:auto}.mlv-status{display:flex;align-items:center;gap:var(--mlv-s5);flex:none;min-width:0;height:var(--mlv-status-h);padding:0 var(--mlv-s3) 0 var(--mlv-s5);border-top:1px solid var(--mlv-border);background:var(--mlv-surface);color:var(--mlv-text-2);font-size:var(--mlv-fs-11);white-space:nowrap;overflow:hidden}.mlv-status__counts{flex:0 0 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-status__coverage{flex:0 1 auto;min-width:0;height:18px;padding:0 var(--mlv-s2);overflow:hidden;text-overflow:ellipsis;border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer;font:inherit}.mlv-status__coverage[hidden]{display:none}.mlv-status__coverage:hover,.mlv-status__coverage[aria-expanded=true]{background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-status__spacer{flex:1 1 0}.mlv-status__freshness{display:inline-flex;align-items:center;gap:var(--mlv-s4);flex:0 100 auto;min-width:0;overflow:hidden}.mlv-status__fresh{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-status__fresh.is-warn,.mlv-status__freshness[data-stale=true]{flex:none}.mlv-status[data-layout=narrow] .mlv-status__coverage{flex-shrink:1000}.mlv-status[data-layout=narrow] .mlv-status__counts{flex-shrink:1}.mlv-status[data-layout=narrow]{gap:var(--mlv-s4)}.mlv-status[data-layout=narrow] .mlv-zoom__btn{display:none}.mlv-toasts{position:absolute;left:50%;bottom:var(--mlv-s7);z-index:60;display:flex;flex-direction:column;gap:var(--mlv-s3);transform:translate(-50%);pointer-events:none}.mlv-toast{min-width:240px;max-width:360px;padding:var(--mlv-s4) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);font-size:var(--mlv-fs-12);text-align:center}.mlv-state{position:absolute;inset:0;z-index:30;display:flex;align-items:center;justify-content:center;padding:var(--mlv-s9);background:var(--mlv-bg)}.mlv-state[hidden]{display:none}.mlv-state__inner{width:min(460px,100%);text-align:center}.mlv-state__title{margin:0 0 var(--mlv-s4);font-size:var(--mlv-fs-16);font-weight:var(--mlv-fw-semi)}.mlv-state__body{margin:0 0 var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-state__list{margin:0 auto var(--mlv-s5);padding:0;list-style:none;text-align:left;max-width:420px;color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-state__actions{display:flex;gap:var(--mlv-s4);justify-content:center}.mlv-btn[disabled]{opacity:.4;cursor:default}.mlv-sheet{position:absolute;inset:0;z-index:60;display:flex;align-items:center;justify-content:center}.mlv-sheet[hidden]{display:none}.mlv-sheet__scrim{position:absolute;inset:0;background:var(--mlv-scrim)}.mlv-sheet__panel{position:relative;width:min(560px,92%);max-height:min(70vh,620px);overflow-y:auto;padding:var(--mlv-s5) var(--mlv-s6) var(--mlv-s6);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3)}.mlv-sheet__head{display:flex;align-items:center;gap:var(--mlv-s4);margin-bottom:var(--mlv-s5)}.mlv-sheet__title{flex:1 1 auto;margin:0;font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi)}.mlv-sheet__list{display:grid;grid-template-columns:minmax(140px,auto) 1fr;gap:var(--mlv-s3) var(--mlv-s5);margin:0}.mlv-sheet__keys{display:flex;flex-wrap:wrap;gap:var(--mlv-s2);margin:0}.mlv-sheet__desc{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);align-self:center}.mlv-kbd{display:inline-block;padding:1px var(--mlv-s3);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);white-space:nowrap}.mlv-result__foot{display:flex;align-items:center;justify-content:space-between;gap:var(--mlv-s4);padding:var(--mlv-s3) var(--mlv-s4);border-top:1px solid var(--mlv-border);color:var(--mlv-text-3);font-size:var(--mlv-fs-10)}.mlv-result__jump{flex:none;padding:1px var(--mlv-s3);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-accent);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-medium)}.mlv-legend{position:absolute;right:var(--mlv-s5);bottom:var(--mlv-s5);z-index:45;display:flex;flex-direction:column;width:min(320px,calc(100% - 2 * var(--mlv-s5)));max-height:min(560px,calc(100% - 2 * var(--mlv-s5)));border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-shadow-panel, 0 8px 24px rgba(0, 0, 0, .18));overflow:hidden}.mlv-legend[hidden]{display:none}.mlv-legend__head{display:flex;align-items:center;justify-content:space-between;padding:var(--mlv-s4) var(--mlv-s5);border-bottom:1px solid var(--mlv-border)}.mlv-legend__title{margin:0;font-size:var(--mlv-fs-12);font-weight:var(--mlv-fw-semi)}.mlv-legend__body{overflow-y:auto;padding:var(--mlv-s4) var(--mlv-s5) var(--mlv-s5)}.mlv-legend__section+.mlv-legend__section{margin-top:var(--mlv-s5)}.mlv-legend__heading{margin:0 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);letter-spacing:var(--mlv-track-caps);text-transform:uppercase}.mlv-legend__list{display:grid;grid-template-columns:minmax(0,auto) minmax(0,1fr);gap:var(--mlv-s2) var(--mlv-s4);margin:0}.mlv-legend__term{display:flex;align-items:center;gap:var(--mlv-s3);color:var(--mlv-text);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-medium)}.mlv-legend__desc{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);line-height:var(--mlv-lh-tight)}.mlv-legend__swatch{flex:none;overflow:visible}.mlv-legend__basis{display:inline-flex;flex-direction:column;align-items:flex-start;gap:var(--mlv-s3)}.mlv-legend__desc strong{color:var(--mlv-text);font-weight:var(--mlv-fw-semi)}.mlv-legend__card{position:relative;display:inline-flex;align-items:center;min-width:44px;height:20px;padding:0 3px;box-sizing:border-box;border:1px solid var(--mlv-node-edge, var(--mlv-border));border-radius:4px;background:var(--mlv-surface)}.mlv-legend__card[data-basis=inferred]{border:1.5px dashed var(--mlv-text-2)}.mlv-legend__card[data-basis=unresolved]{border:1.5px dotted var(--mlv-text-2);background-image:repeating-linear-gradient(135deg,transparent 0 4px,color-mix(in srgb,var(--mlv-text) 8%,transparent) 4px 5px)}.mlv-legend__tag{padding:0 3px;border:1px dashed var(--mlv-text-2);border-radius:3px;background:var(--mlv-bg);color:var(--mlv-text);font-size:9px;line-height:11px;white-space:nowrap}.mlv-legend__tag[data-basis=unresolved]{border-style:dotted}.mlv-legend__phases{display:inline-flex;gap:2px}.mlv-legend__phase{width:3px;height:14px;border-radius:1.5px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-legend__short{color:var(--mlv-text);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi)}@media(max-width:900px){.mlv-legend{right:var(--mlv-s3);bottom:var(--mlv-s3);width:calc(100% - 2 * var(--mlv-s3))}}.mlv-legend__body{background-color:var(--mlv-surface);background-image:linear-gradient(to bottom,var(--mlv-surface) 30%,transparent),linear-gradient(to top,var(--mlv-surface) 30%,transparent),linear-gradient(to bottom,var(--mlv-border-strong),transparent),linear-gradient(to top,var(--mlv-border-strong),transparent);background-repeat:no-repeat;background-size:100% 24px,100% 24px,100% 8px,100% 8px;background-position:top,bottom,top,bottom;background-attachment:local,local,scroll,scroll;scrollbar-gutter:stable}.mlv-canvas{position:relative;flex:1 1 auto;min-width:0;overflow:hidden;background-color:var(--mlv-bg);background-image:radial-gradient(var(--mlv-canvas-dot) 1px,transparent 1px);background-size:20px 20px;cursor:grab;touch-action:none}.mlv-canvas[data-lod=compact]{background-image:none}.mlv-canvas.is-panning{cursor:grabbing}.mlv-world{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}.mlv-layer{position:absolute;top:0;left:0}.mlv-layer--edges{overflow:visible;pointer-events:none}.mlv-layer--nodes{pointer-events:none}.mlv-layer--nodes>*{pointer-events:auto}.mlv-lane{position:absolute;border:1px solid var(--mlv-border);border-radius:var(--mlv-r-lane);background:color-mix(in srgb,var(--mlv-text) calc(var(--mlv-lane-tint) * 100%),transparent)}.mlv-lane:before{content:"";position:absolute;top:6px;bottom:6px;left:0;width:3px;border-radius:1.5px;background:var(--mlv-stage, var(--mlv-stage-unknown));pointer-events:none}.mlv-lane__header{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;gap:var(--mlv-s4);height:var(--mlv-lane-header-h, 28px);padding:0 var(--mlv-s5);border-bottom:1px solid var(--mlv-border);border-radius:calc(var(--mlv-r-lane) - 1px) calc(var(--mlv-r-lane) - 1px) 0 0;background:color-mix(in srgb,var(--mlv-text) 5%,var(--mlv-surface))}.mlv-lane__header:before{content:"";flex:none;width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-lane__num{flex:none;margin-right:calc(-1 * var(--mlv-s2));color:var(--mlv-text-2);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-lane__label{flex:0 1 auto;min-width:48px;overflow:hidden;color:var(--mlv-text);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);text-overflow:ellipsis;white-space:nowrap}.mlv-lane__count{flex:none;color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums;white-space:nowrap}.mlv-lane__spacer{flex:1 1 auto}.mlv-lane__header .mlv-cluster{flex:none}.mlv-lane__unit:before{content:"\b7 "}.mlv-lane__unit{flex:0 100 auto;min-width:0;overflow:hidden;margin-left:calc(-1 * var(--mlv-s2));color:var(--mlv-text-3);font-size:var(--mlv-fs-10);text-overflow:ellipsis;white-space:nowrap}.mlv-group{position:absolute;border:1px solid var(--mlv-border-strong);border-radius:12px;background:color-mix(in srgb,var(--mlv-text) calc(var(--mlv-group-tint) * 100%),transparent)}.mlv-group[data-basis=inferred]{border:calc(1.5px / var(--mlv-z, 1)) dashed var(--mlv-text-2)}.mlv-group[data-basis=unresolved]{border:calc(1.5px / var(--mlv-z, 1)) dotted var(--mlv-text-2)}.mlv-group__header{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;height:var(--mlv-group-header-h);padding:0 var(--mlv-s4);border:0;border-radius:12px 12px 0 0;background:none;color:var(--mlv-text);cursor:pointer;text-align:left}.mlv-group__header:hover{background:var(--mlv-surface-2)}.mlv-group__chevron{transition:transform var(--mlv-dur-2) var(--mlv-ease);color:var(--mlv-text-2)}.mlv-group.is-collapsed .mlv-group__chevron{transform:rotate(0)}.mlv-group__chevron{transform:rotate(90deg)}.mlv-group__name{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-12);font-weight:var(--mlv-fw-semi)}.mlv-group__count{flex:none;padding:0 var(--mlv-s3);border-radius:var(--mlv-r-pill);background:var(--mlv-surface-2);color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums}.mlv-group.is-dim,.mlv-lane.is-dim{opacity:.5}.mlv-canvas.is-focusing .mlv-node:not(.is-lit){opacity:.22;filter:saturate(.3);pointer-events:none}.mlv-canvas.is-focusing .mlv-edge:not(.is-lit){opacity:.16}.mlv-canvas.is-tracing .mlv-node.is-lit{box-shadow:0 0 0 max(1.5px,calc(1px / var(--mlv-z, 1))) var(--mlv-text-2)}.mlv-tooltip{position:absolute;z-index:50;max-width:320px;padding:var(--mlv-s4) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3);font-size:var(--mlv-fs-12);pointer-events:none}.mlv-tooltip[hidden]{display:none}.mlv-tooltip__title{font-weight:var(--mlv-fw-semi)}.mlv-tooltip__row{margin-top:var(--mlv-s2);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-tooltip__loc{font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);color:var(--mlv-text-3)}.mlv-zoom{display:inline-flex;align-items:center;flex:none;gap:1px}.mlv-zoom__level{min-width:38px;text-align:center;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-variant-numeric:tabular-nums}.mlv-zoom .mlv-zoom__btn{width:20px;height:18px;min-width:0;min-height:0;padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-zoom .mlv-zoom__btn:hover{background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-layer--lanes,.mlv-lane,.mlv-group{pointer-events:none}.mlv-lane__header,.mlv-group__header{pointer-events:auto}.mlv-statehost{position:absolute;inset:0;z-index:30;pointer-events:none}.mlv-statehost>*{pointer-events:auto}.mlv-group__chevron-btn{display:inline-flex;align-items:center;justify-content:center;flex:none;width:24px;height:24px;margin-left:-4px;padding:0;border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer}.mlv-group__chevron-btn:hover{background:var(--mlv-surface-2)}.mlv-node{position:absolute;display:flex;align-items:stretch;gap:0;overflow:visible;border:1px solid var(--mlv-node-edge, var(--mlv-border));border-radius:var(--mlv-r-node);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1);cursor:pointer;text-align:left;padding:0;transition:box-shadow var(--mlv-dur-1) var(--mlv-ease),transform var(--mlv-dur-1) var(--mlv-ease)}.mlv-node__rail{flex:none;width:var(--mlv-rail-w);border-radius:var(--mlv-r-node) 0 0 var(--mlv-r-node);background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-node__main{display:flex;align-items:flex-start;gap:var(--mlv-s4);flex:1 1 auto;min-width:0;padding:10px var(--mlv-s5) 10px var(--mlv-s4);overflow:hidden}.mlv-node__iconbox{flex:none;display:flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:6px;background:var(--mlv-surface-2);color:var(--mlv-text-2)}.mlv-node__text{flex:1 1 auto;min-width:0}.mlv-node__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);line-height:var(--mlv-lh-tight)}.mlv-node__sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);line-height:var(--mlv-lh-tight)}.mlv-node__sub[data-lines="2"]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow-wrap:anywhere;white-space:normal}.mlv-node__loc{display:flex;overflow:hidden;white-space:nowrap;color:var(--mlv-text-3);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-node__chips{display:flex;gap:var(--mlv-s2);margin-top:var(--mlv-s2);overflow:hidden}.mlv-node__chips .mlv-chip{display:inline-block;flex:0 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;line-height:18px}.mlv-node:hover{box-shadow:var(--mlv-sh-2);border-color:var(--mlv-border-strong);transform:translateY(-1px);z-index:3}.mlv-node.is-selected{box-shadow:var(--mlv-sh-2),0 0 0 2px var(--mlv-accent),0 0 0 6px color-mix(in srgb,var(--mlv-accent) 14%,transparent);z-index:4}.mlv-node.is-selected .mlv-node__title{font-weight:700}.mlv-node.has-issues{border-color:color-mix(in srgb,var(--mlv-sev) 55%,var(--mlv-node-edge, var(--mlv-border)));--mlv-basis-ink: color-mix(in srgb, var(--mlv-sev) 70%, var(--mlv-text))}.mlv-node.is-stale{border-color:var(--mlv-stale-ink);--mlv-basis-ink: var(--mlv-stale-ink)}.mlv-node[data-basis=inferred],.mlv-node[data-basis=unresolved]{border-color:transparent}.mlv-node[data-basis=inferred]:after,.mlv-node[data-basis=unresolved]:after{content:"";position:absolute;inset:-1px;border:calc(1.5px / var(--mlv-z, 1)) dashed var(--mlv-basis-ink, var(--mlv-text-2));border-radius:inherit;pointer-events:none}.mlv-node[data-basis=unresolved]:after{border-style:dotted}.mlv-node[data-basis=unresolved]{background-image:repeating-linear-gradient(135deg,transparent 0 7px,color-mix(in srgb,var(--mlv-text) 8%,transparent) 7px 8px)}.mlv-basis-tag{display:inline-flex;align-items:center;height:14px;padding:0 4px;border:1px dashed var(--mlv-basis-ink, var(--mlv-text-2));border-radius:3px;background:var(--mlv-bg);color:var(--mlv-text);font-size:10px;font-weight:var(--mlv-fw-regular);line-height:1;white-space:nowrap}.mlv-basis-tag[data-basis=unresolved]{border-style:dotted}.mlv-node>.mlv-basis-tag{position:absolute;top:100%;left:10px;z-index:5;transform-origin:0 50%;transform:translateY(-50%) scale(calc(1 / var(--mlv-z, 1)));pointer-events:none}.mlv-group__header .mlv-basis-tag{flex:none}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed]{border-color:color-mix(in srgb,var(--mlv-node-edge, var(--mlv-border)) 35%,var(--mlv-bg));background:var(--mlv-bg);box-shadow:none}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed] .mlv-node__rail{background:color-mix(in srgb,var(--mlv-stage, var(--mlv-stage-unknown)) 30%,var(--mlv-bg))}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed] .mlv-node__iconbox{background:transparent}.mlv-node__stale{position:absolute;top:-7px;left:-7px;z-index:5;display:flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:var(--mlv-r-pill);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1);color:var(--mlv-stale-ink)}.mlv-group__header .mlv-node__stale{position:static;box-shadow:none;flex:none}.mlv-node.is-pulse{animation:mlv-pulse .3s var(--mlv-ease) 2}@keyframes mlv-pulse{0%,to{box-shadow:var(--mlv-sh-2),0 0 0 2px var(--mlv-accent)}50%{box-shadow:var(--mlv-sh-2),0 0 0 7px color-mix(in srgb,var(--mlv-accent) 30%,transparent)}}.mlv-canvas[data-lod=compact] .mlv-node__sub,.mlv-canvas[data-lod=compact] .mlv-node__loc,.mlv-canvas[data-lod=compact] .mlv-node__chips,.mlv-canvas[data-lod=compact] .mlv-node__iconbox{display:none}.mlv-canvas[data-lod=compact] .mlv-node__main{container-type:size;align-items:center;padding:2px 8px}.mlv-canvas[data-lod=compact] .mlv-node__title{font-size:min(calc(var(--mlv-compact-title) / var(--mlv-z, 1)),calc(100cqh / 2.3));line-height:1.15}.mlv-canvas[data-lod=compact] .mlv-node>.mlv-basis-tag{transform-origin:0 0;transform:translateY(-1px) scale(calc(1 / var(--mlv-z, 1)))}.mlv-canvas[data-lod=compact] .mlv-edge-label{display:none}.mlv-glyph{display:block;overflow:visible}.mlv-glyph__shape{fill:var(--mlv-sev-marker, var(--mlv-sev-low))}.mlv-glyph__ink{fill:var(--mlv-sev-marker-ink, #FFFFFF)}.mlv-glyph--low{--mlv-sev-marker: var(--mlv-sev-low);--mlv-sev-marker-ink: var(--mlv-sev-low-ink)}.mlv-glyph--medium{--mlv-sev-marker: var(--mlv-sev-medium);--mlv-sev-marker-ink: var(--mlv-sev-medium-ink)}.mlv-glyph--high{--mlv-sev-marker: var(--mlv-sev-high);--mlv-sev-marker-ink: var(--mlv-sev-high-ink)}body.vscode-high-contrast .mlv-glyph__shape,body.vscode-high-contrast-light .mlv-glyph__shape,:root[data-theme=hc] .mlv-glyph__shape,.mlv-root[data-theme=hc] .mlv-glyph__shape{fill:none;stroke:var(--mlv-sev-marker, currentColor);stroke-width:2}body.vscode-high-contrast .mlv-glyph__ink,body.vscode-high-contrast-light .mlv-glyph__ink,:root[data-theme=hc] .mlv-glyph__ink,.mlv-root[data-theme=hc] .mlv-glyph__ink{fill:var(--mlv-sev-marker, currentColor)}.mlv-badge{position:absolute;top:-6px;right:-6px;z-index:5;display:flex;align-items:center;gap:var(--mlv-s1);height:20px;padding:0 var(--mlv-s2);border-radius:var(--mlv-r-pill);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1)}.mlv-badge--high{border:1px solid color-mix(in srgb,var(--mlv-sev-high) 45%,transparent)}.mlv-badge--medium{border:1px solid color-mix(in srgb,var(--mlv-sev-medium) 45%,transparent)}.mlv-badge--low{border:1px solid color-mix(in srgb,var(--mlv-sev-low) 45%,transparent)}.mlv-badge__count,.mlv-badge__ids{color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-badge__ids{padding-right:2px;color:var(--mlv-text);white-space:nowrap}.mlv-cluster{display:inline-flex;align-items:center;gap:var(--mlv-s3)}.mlv-cluster__item{display:inline-flex;align-items:center;gap:2px}.mlv-cluster__count{color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums}[data-stage]{--mlv-stage: var(--mlv-stage-unknown)}[data-phase-tone="0"]{--mlv-stage: var(--mlv-phase-0)}[data-phase-tone="1"]{--mlv-stage: var(--mlv-phase-1)}[data-phase-tone="2"]{--mlv-stage: var(--mlv-phase-2)}[data-phase-tone="3"]{--mlv-stage: var(--mlv-phase-3)}[data-phase-tone="4"]{--mlv-stage: var(--mlv-phase-4)}[data-phase-tone="5"]{--mlv-stage: var(--mlv-phase-5)}[data-phase-tone="6"]{--mlv-stage: var(--mlv-phase-6)}[data-phase-tone="7"]{--mlv-stage: var(--mlv-phase-7)}[data-sev=high]{--mlv-sev: var(--mlv-sev-high)}[data-sev=medium]{--mlv-sev: var(--mlv-sev-medium)}[data-sev=low]{--mlv-sev: var(--mlv-sev-low)}.mlv-node:focus-visible,.mlv-group__header:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px;border-radius:var(--mlv-r-node)}.mlv-node__title--wrap{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;line-clamp:1;white-space:normal;overflow-wrap:anywhere;text-overflow:clip}.mlv-node__title--wrap[data-lines="2"]{-webkit-line-clamp:2;line-clamp:2}.mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3;line-clamp:3}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap{-webkit-line-clamp:2;line-clamp:2}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3;line-clamp:3;font-size:min(calc(var(--mlv-compact-title) / var(--mlv-z, 1)),calc(100cqh / 3.45))}.mlv-edge{pointer-events:none}.mlv-edge__hit{fill:none;stroke:transparent;stroke-width:12;pointer-events:stroke;cursor:pointer}.mlv-edge__path,.mlv-legend__edge{fill:none;stroke:var(--mlv-edge);stroke-width:max(1.5px,calc(1px / var(--mlv-z, 1)));stroke-linecap:round;stroke-linejoin:round;transition:stroke var(--mlv-dur-1) var(--mlv-ease)}.mlv-edge[data-basis=inferred] .mlv-edge__path,.mlv-edge[data-basis=inferred] .mlv-legend__edge{stroke-dasharray:calc(6px / var(--mlv-z, 1)) calc(4px / var(--mlv-z, 1));stroke-linecap:butt}.mlv-edge[data-basis=unresolved] .mlv-edge__path,.mlv-edge[data-basis=unresolved] .mlv-legend__edge{stroke-dasharray:calc(1.5px / var(--mlv-z, 1)) calc(3.5px / var(--mlv-z, 1));stroke-linecap:round}.mlv-arrow{fill:var(--mlv-edge);stroke:none}.mlv-edge.is-hover .mlv-edge__path,.mlv-edge.is-lit .mlv-edge__path{stroke:var(--mlv-text-2);stroke-width:max(2px,calc(1.5px / var(--mlv-z, 1)))}.mlv-edge.is-selected .mlv-edge__path{stroke:var(--mlv-accent);stroke-width:max(2.5px,calc(2px / var(--mlv-z, 1)))}.mlv-edge.has-issue .mlv-edge__path{stroke:var(--mlv-sev, var(--mlv-edge));opacity:.85}.mlv-edge-label{font-family:var(--mlv-font-mono);font-size:10px;fill:var(--mlv-text-2);paint-order:stroke;stroke:var(--mlv-bg);stroke-width:3;stroke-linejoin:round;text-anchor:middle;dominant-baseline:middle;opacity:0;transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-edge.is-hover .mlv-edge-label,.mlv-edge.is-selected .mlv-edge-label,.mlv-edge.is-lit .mlv-edge-label,.mlv-edge.has-label .mlv-edge-label,.mlv-edge--merged .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--data .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--control .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--state .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--loop .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--output .mlv-edge-label{opacity:1}.mlv-canvas[data-lod=compact] .mlv-edge-label{opacity:0}.mlv-edge-marker__disc{fill:var(--mlv-surface);stroke:var(--mlv-sev-marker, var(--mlv-sev-low));stroke-width:1}.mlv-edge-marker--high{--mlv-sev-marker: var(--mlv-sev-high)}.mlv-edge-marker--medium{--mlv-sev-marker: var(--mlv-sev-medium)}.mlv-edge-marker--low{--mlv-sev-marker: var(--mlv-sev-low)}.mlv-connector{fill:none;stroke:var(--mlv-sev, var(--mlv-accent));stroke-width:1.5;stroke-dasharray:2 4}.mlv-connector__badge{fill:var(--mlv-surface);stroke:var(--mlv-sev, var(--mlv-accent));stroke-width:1}.mlv-connector__label{font-family:var(--mlv-font-mono);font-size:9px;fill:var(--mlv-text);text-anchor:middle;dominant-baseline:central}.mlv-bundles{pointer-events:none}.mlv-bundle__trunk,.mlv-bundle__spur{fill:none;stroke:var(--mlv-edge);stroke-linecap:round;stroke-linejoin:round;transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-bundle__trunk{stroke-width:3.5;opacity:.72}.mlv-bundle__spur{stroke-width:1.5;opacity:.45}.mlv-bundle.has-issue .mlv-bundle__trunk,.mlv-bundle.has-issue .mlv-bundle__spur{stroke:var(--mlv-sev, var(--mlv-edge))}.mlv-bundle__badge-box{fill:var(--mlv-surface);stroke:var(--mlv-edge);stroke-width:1;opacity:.95}.mlv-bundle.has-issue .mlv-bundle__badge-box{stroke:var(--mlv-sev, var(--mlv-edge))}.mlv-bundle__badge-text{font-family:var(--mlv-font-mono);font-size:10px;fill:var(--mlv-text-2);text-anchor:middle;dominant-baseline:central}.mlv-bundle__badge{transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-bundle.is-expanded .mlv-bundle__trunk,.mlv-bundle.is-expanded .mlv-bundle__spur,.mlv-bundle.is-expanded .mlv-bundle__badge,.mlv-canvas .mlv-edge.is-bundled .mlv-edge__path,.mlv-canvas .mlv-edge.is-bundled .mlv-edge-label{opacity:0}.mlv-canvas .mlv-edge.is-bundled[data-basis=inferred] .mlv-edge__path,.mlv-canvas .mlv-edge.is-bundled[data-basis=unresolved] .mlv-edge__path{opacity:1}.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=observed]:not(.is-hover):not(.is-selected):not(.is-lit) .mlv-edge__path{stroke-opacity:.3}.mlv-canvas[data-exceptions=on] .mlv-bundle__trunk,.mlv-canvas[data-exceptions=on] .mlv-bundle__spur{stroke-opacity:.3}.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=inferred] .mlv-edge__path,.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=unresolved] .mlv-edge__path{stroke:var(--mlv-text)}.mlv-canvas[data-lod=compact] .mlv-bundle__badge{opacity:0}.mlv-edge__stalebg{fill:var(--mlv-surface);stroke:var(--mlv-stale-ink);stroke-width:1}.mlv-edge__staleicon{stroke:var(--mlv-stale-ink);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}.mlv-edge__stale{pointer-events:none}.mlv-edge[data-stage]:not(.has-issue){--mlv-flow-color: var(--mlv-stage)}.mlv-edge.has-issue{--mlv-flow-color: var(--mlv-sev)}body.vscode-high-contrast .mlv-edge[data-stage],body.vscode-high-contrast .mlv-edge.has-issue,body.vscode-high-contrast-light .mlv-edge[data-stage],body.vscode-high-contrast-light .mlv-edge.has-issue,:root[data-theme=hc] .mlv-edge[data-stage],:root[data-theme=hc] .mlv-edge.has-issue,.mlv-root[data-theme=hc] .mlv-edge[data-stage],.mlv-root[data-theme=hc] .mlv-edge.has-issue{--mlv-flow-color: var(--mlv-text)}.mlv-edge__flow{fill:none;stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:var(--mlv-flow-width, 3px);stroke-linecap:round;pointer-events:none}.mlv-edge.is-flowing--pulse .mlv-edge__flow{stroke-dasharray:none;stroke-opacity:var(--mlv-flow-underlay-opacity, .28)}.mlv-edge__charge{pointer-events:none;animation:mlv-charge-ends var(--mlv-flow-dur, .9s) linear infinite;animation-iteration-count:var(--mlv-flow-iter, infinite);animation-fill-mode:forwards}.mlv-edge__charge--twin{animation-delay:calc(var(--mlv-flow-dur, .9s) / -2)}@keyframes mlv-charge-ends{0%{opacity:0}8%{opacity:1}92%{opacity:1}to{opacity:0}}.mlv-edge__charge-halo{fill:var(--mlv-flow-color, var(--mlv-accent));fill-opacity:var(--mlv-flow-halo-opacity, .18);stroke:none}.mlv-edge__charge-glow{fill:var(--mlv-flow-color, var(--mlv-accent));fill-opacity:var(--mlv-flow-glow-opacity, .42);stroke:none}.mlv-edge__charge-core{fill:var(--mlv-surface);stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:var(--mlv-flow-core-width, 1.6)}body.vscode-high-contrast .mlv-edge__charge-halo,body.vscode-high-contrast-light .mlv-edge__charge-halo,:root[data-theme=hc] .mlv-edge__charge-halo,.mlv-root[data-theme=hc] .mlv-edge__charge-halo{fill:none;stroke:var(--mlv-text);stroke-width:1.2}.mlv-edge.is-flowing .mlv-edge__flow{stroke-dasharray:var(--mlv-flow-head) var(--mlv-flow-gap);animation:mlv-flow-stream var(--mlv-flow-dur-data) linear infinite;animation-delay:var(--mlv-flow-delay, 0ms);animation-iteration-count:var(--mlv-flow-iter, infinite)}.mlv-edge--call.is-flowing .mlv-edge__flow,.mlv-edge--control.is-flowing .mlv-edge__flow{animation-duration:var(--mlv-flow-dur-call)}.mlv-edge--control.mlv-edge--back.is-flowing .mlv-edge__flow{animation-duration:var(--mlv-flow-dur-back)}@keyframes mlv-flow-stream{0%{stroke-dashoffset:0}to{stroke-dashoffset:var(--mlv-flow-end, -20)}}.mlv-edge__port{stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:1.4;pointer-events:none}.mlv-edge__port--out{fill:var(--mlv-surface)}.mlv-edge__port--in{fill:var(--mlv-flow-color, var(--mlv-accent));stroke:var(--mlv-surface);stroke-width:1.4}.mlv-canvas[data-flow=motion] .mlv-edge.is-flowing:not(.is-flow-settled) .mlv-edge__path,.mlv-canvas[data-flow=motion] .mlv-edge.is-flowing--pulse:not(.is-flow-settled) .mlv-edge__path{marker-end:none}.mlv-canvas[data-flow=static] .mlv-edge.is-flowing--pulse .mlv-edge__path,.mlv-edge.is-flowing--pulse.is-flow-settled .mlv-edge__path{stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:max(2.5px,calc(2px / var(--mlv-z, 1)))}.mlv-edge__dir{fill:none;stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}.mlv-node.is-flow-source,.mlv-group.is-flow-source{box-shadow:0 0 0 2px var(--mlv-stage, var(--mlv-accent))}.mlv-node.is-flow-target,.mlv-group.is-flow-target{box-shadow:0 0 0 2px var(--mlv-surface),0 0 0 4px var(--mlv-accent)}@media(prefers-reduced-motion:reduce){.mlv-edge__flow,.mlv-edge__charge{display:none!important}}body.vscode-reduce-motion .mlv-edge__flow,body.vscode-reduce-motion .mlv-edge__charge,body.vscode-using-screen-reader .mlv-edge__flow,body.vscode-using-screen-reader .mlv-edge__charge{display:none!important}.mlv-canvas[data-flow=off] .mlv-edge__flow,.mlv-canvas[data-flow=off] .mlv-edge__charge,.mlv-canvas[data-flow=off] .mlv-edge__port,.mlv-canvas[data-flow=off] .mlv-edge__dir{display:none}.mlv-rail{position:relative;display:flex;flex-direction:column;flex:none;width:var(--mlv-rail-width);min-width:280px;max-width:560px;border-left:1px solid var(--mlv-border);background:var(--mlv-surface)}.mlv-rail[hidden]{display:none}.mlv-rail__grip{position:absolute;top:0;left:-2px;width:5px;height:100%;cursor:col-resize;z-index:5}.mlv-rail__grip:after{content:"";position:absolute;left:2px;top:0;width:1px;height:100%;background:transparent}.mlv-rail__grip:hover:after{background:var(--mlv-accent)}.mlv-rail__bar{display:flex;align-items:stretch;flex:none;border-bottom:1px solid var(--mlv-border)}.mlv-rail__tabs{display:flex;flex:1 1 auto;min-width:0;height:36px}.mlv-rail__tab{flex:1 1 auto;min-width:0;padding:0 var(--mlv-s3);white-space:nowrap;border:0;border-bottom:2px solid transparent;background:none;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);cursor:pointer}.mlv-rail__tab[aria-selected=true]{color:var(--mlv-text);border-bottom-color:var(--mlv-accent);font-weight:var(--mlv-fw-semi)}.mlv-rail__tab:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__tabcount{color:var(--mlv-text-2);font-variant-numeric:tabular-nums}.mlv-rail__chevron{flex:none;align-self:center;width:26px;height:26px;min-height:0;margin:0 var(--mlv-s2);padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-rail__chevron[hidden]{display:none}.mlv-rail__chevron .mlv-uicon{transform:rotate(90deg)}.mlv-rail__chevron[aria-expanded=false] .mlv-uicon{transform:rotate(-90deg)}.mlv-rail__panel:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__count{color:var(--mlv-text-3);font-weight:var(--mlv-fw-regular);text-transform:none;letter-spacing:0}.mlv-body[data-rail=sheet]{flex-direction:column}.mlv-body[data-rail=sheet]>.mlv-main{flex:1 1 0;min-height:0}.mlv-rail[data-mode=sheet]{flex:0 0 calc(var(--mlv-sheet-fraction, .47) * 100%);width:auto;min-width:0;max-width:none;min-height:32px;max-height:calc(100% - min(240px,45%));border-left:0;border-top:1px solid var(--mlv-border-strong)}.mlv-rail[data-mode=sheet][data-expanded=false]{flex-basis:32px;height:32px}.mlv-rail[data-mode=sheet] .mlv-rail__tabs{height:31px}.mlv-rail[data-mode=sheet][data-expanded=false] .mlv-rail__bar{border-bottom:0}.mlv-rail[data-mode=sheet]>.mlv-rail__grip{top:-4px;left:0;width:100%;height:9px;cursor:row-resize}.mlv-rail[data-mode=sheet]>.mlv-rail__grip:after{left:50%;top:2px;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--mlv-border-strong)}.mlv-rail[data-mode=sheet]>.mlv-rail__grip:hover:after,.mlv-rail[data-mode=sheet]>.mlv-rail__grip:focus-visible:after{background:var(--mlv-accent)}.mlv-rail__grip:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__panel{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--mlv-s4)}.mlv-rail__panel[hidden]{display:none}.mlv-rail__section{margin:0 0 var(--mlv-s5)}.mlv-rail__heading{display:flex;align-items:center;gap:var(--mlv-s3);margin:0 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);text-transform:uppercase;letter-spacing:var(--mlv-track-caps)}.mlv-issues{list-style:none;margin:0;padding:0}.mlv-issues__item{display:flex;flex-wrap:wrap;align-items:flex-start}.mlv-issue{display:flex;align-items:flex-start;gap:var(--mlv-s4);flex:1 1 0;min-width:0;padding:var(--mlv-s4);border:1px solid transparent;border-radius:var(--mlv-r-btn);background:none;cursor:pointer;text-align:left}.mlv-issue:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-issue:hover{background:var(--mlv-surface-2)}.mlv-issue.is-selected{border-color:var(--mlv-accent);background:var(--mlv-surface-2)}.mlv-issue__text{flex:1 1 auto;min-width:0}.mlv-issue__short,.mlv-insp__short{flex:none;color:var(--mlv-text);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-issue__title{font-size:12.5px;font-weight:var(--mlv-fw-medium);line-height:var(--mlv-lh-tight)}.mlv-issue__meta{display:flex;align-items:center;gap:var(--mlv-s3);margin-top:var(--mlv-s1);color:var(--mlv-text-3);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-issue__open{flex:none;align-self:center}.mlv-issue__detail{flex:1 0 100%}.mlv-empty-note{padding:var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12);text-align:center}.mlv-sel[data-columns="2"]{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);column-gap:var(--mlv-s7, 20px);align-items:start}.mlv-sel__col{min-width:0}.mlv-insp__eyebrow{display:flex;flex-wrap:wrap;align-items:center;gap:0 var(--mlv-s2);margin:0 0 var(--mlv-s2);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__phase{display:inline-flex;align-items:center;gap:var(--mlv-s2);color:var(--mlv-text);font-weight:var(--mlv-fw-medium)}.mlv-insp__phasedot{width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-insp__sep{color:var(--mlv-text-3)}.mlv-insp__severity{color:var(--mlv-text)}.mlv-insp__title{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi);line-height:var(--mlv-lh-tight);overflow-wrap:anywhere}.mlv-insp__ends{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-12);overflow-wrap:anywhere}.mlv-insp__section{margin:var(--mlv-s5) 0 0}.mlv-sel__col--evidence>.mlv-insp__section:first-child{margin-top:0}.mlv-insp__issue-title{flex:0 1 auto;min-width:0;color:var(--mlv-text);text-align:left;overflow-wrap:anywhere}.mlv-insp__issue-head .mlv-mono{color:var(--mlv-text-3)}.mlv-insp__issue-id{margin:calc(-1 * var(--mlv-s2)) 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-insp__note,.mlv-insp__aside{color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__links,.mlv-insp__sentences{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--mlv-s2);font-size:var(--mlv-fs-12);line-height:1.45}.mlv-insp__flow-basis{color:var(--mlv-text-2)}.mlv-quote{padding:var(--mlv-s3) 0 0;border-top:1px solid var(--mlv-border)}.mlv-quote__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--mlv-s1) var(--mlv-s3);font-size:var(--mlv-fs-11)}.mlv-quote__num{flex:none;min-width:1.4em;color:var(--mlv-text-2);font-variant-numeric:tabular-nums;font-weight:var(--mlv-fw-semi)}.mlv-quote__loc{flex:1 0 calc(100% - 1.4em - var(--mlv-s3));min-width:0;color:var(--mlv-text);font-family:var(--mlv-font-mono);overflow-wrap:anywhere}.mlv-quote__loc+*{margin-left:calc(1.4em + var(--mlv-s3))}.mlv-quote__at{color:var(--mlv-text-2)}.mlv-quote__role{color:var(--mlv-text-2);font-style:italic}.mlv-quote__fresh.is-muted{color:var(--mlv-text-2)}.mlv-quote__fresh.mlv-insp__stale{margin-left:0}.mlv-quote__open{flex:none}.mlv-quote__text{margin:var(--mlv-s2) 0 0;padding:var(--mlv-s2) var(--mlv-s3);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);line-height:1.5;overflow-x:auto;white-space:pre;user-select:text}.mlv-quote__line{display:inline}.mlv-quote__ln{display:inline-block;min-width:3ch;margin-right:var(--mlv-s3);color:var(--mlv-text-3);text-align:right;user-select:none}.mlv-insp__evidence-nav{display:flex;gap:var(--mlv-s2);margin:0 0 var(--mlv-s3)}.mlv-about__section{margin:0 0 var(--mlv-s6);font-size:var(--mlv-fs-12);line-height:1.5}.mlv-about__question{margin:0;color:var(--mlv-text);font-size:13px;white-space:pre-wrap;overflow-wrap:anywhere}.mlv-about__question[data-clamped=true]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden}.mlv-about__more{margin-top:var(--mlv-s2);font-size:var(--mlv-fs-11)}.mlv-about__para,.mlv-about__text,.mlv-about__status{margin:0 0 var(--mlv-s3);color:var(--mlv-text);white-space:pre-wrap;overflow-wrap:anywhere}.mlv-about__lead{font-weight:var(--mlv-fw-semi)}.mlv-about__meta,.mlv-about__note{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__kv{padding:0 3px;border-radius:3px;background:var(--mlv-surface-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__limitations{color:var(--mlv-text)}.mlv-about__limitations summary{cursor:pointer;color:var(--mlv-text);font-weight:var(--mlv-fw-medium)}.mlv-about__limitations ul{margin:var(--mlv-s2) 0 0;padding-left:20px}.mlv-about__limitations li{margin-bottom:var(--mlv-s2);overflow-wrap:anywhere}.mlv-about__files{list-style:none;margin:0 0 var(--mlv-s3);padding:0}.mlv-about__file{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 var(--mlv-s3);padding:2px 0}.mlv-about__path{flex:1 1 auto;min-width:0;font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__file .mlv-quote__fresh{flex:none;font-size:var(--mlv-fs-11)}.mlv-about__trust{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__issue{margin-bottom:var(--mlv-s5);padding:var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface-2)}.mlv-insp__issue-head{display:flex;align-items:center;gap:var(--mlv-s3);margin-bottom:var(--mlv-s3);font-weight:var(--mlv-fw-medium)}.mlv-insp__line{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-11);color:var(--mlv-text-2)}.mlv-insp__fix{margin:0 0 var(--mlv-s3);padding-left:var(--mlv-s4);border-left:3px solid var(--mlv-accent);font-size:var(--mlv-fs-11)}.mlv-insp__fix-label{margin:0 0 2px;color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);letter-spacing:.04em;text-transform:uppercase}.mlv-insp__fix-text{margin:0;color:var(--mlv-text);white-space:pre-wrap;overflow-wrap:anywhere}.mlv-insp__detail{margin:0 0 var(--mlv-s4);color:var(--mlv-text);font-size:var(--mlv-fs-12);line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;user-select:text}.mlv-insp__basis{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__basis .mlv-basis-tag{vertical-align:1px}.mlv-insp__caption{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__limits{margin:var(--mlv-s4) 0 0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__related{list-style:none;margin:var(--mlv-s3) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--mlv-s2)}.mlv-outline,.mlv-outline ul{list-style:none;margin:0;padding:0 0 0 var(--mlv-s5)}.mlv-outline{padding-left:0}.mlv-outline__item{outline:none}.mlv-outline__item:focus>.mlv-outline__row{box-shadow:0 0 0 2px var(--mlv-focus)}.mlv-outline__row{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;padding:2px var(--mlv-s3);border:0;border-radius:var(--mlv-r-chip);background:none;cursor:pointer;text-align:left;font-size:var(--mlv-fs-12)}.mlv-outline__row--lane{font-weight:var(--mlv-fw-semi)}.mlv-outline__chevron{flex:none;width:12px;height:12px;color:var(--mlv-text-3);transform:rotate(90deg);transition:transform var(--mlv-dur-1, .12s) ease}.mlv-outline__item[aria-expanded=false]>.mlv-outline__row .mlv-outline__chevron{transform:rotate(0)}.mlv-outline__chevron--none{transform:none}.mlv-outline__row:hover{background:var(--mlv-surface-2)}.mlv-outline__row.is-selected{background:var(--mlv-surface-2);font-weight:var(--mlv-fw-medium)}.mlv-outline__label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-outline__stage{color:var(--mlv-text-3);font-size:var(--mlv-fs-10);text-transform:uppercase;letter-spacing:var(--mlv-track-caps)}.mlv-outline__row--lane{flex-wrap:wrap}.mlv-outline__row--lane .mlv-outline__label{flex:1 1 0;min-width:0}.mlv-outline__phasecount{flex:1 0 100%;font-weight:var(--mlv-fw-regular);text-transform:none;letter-spacing:normal}.mlv-basis-tag--rail{flex:none;vertical-align:middle}.mlv-relations{margin-top:var(--mlv-s6);padding-top:var(--mlv-s4);border-top:1px solid var(--mlv-border)}.mlv-relations__context{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-relations__views{display:flex;gap:var(--mlv-s2);margin-bottom:var(--mlv-s3)}.mlv-relations__views .mlv-btn[aria-pressed=true]{border-color:var(--mlv-accent);color:var(--mlv-text)}.mlv-relations__list{margin:0;padding:0;list-style:none}.mlv-relations__item+.mlv-relations__item{margin-top:var(--mlv-s2)}.mlv-relations__row{display:block;width:100%;min-height:32px;padding:var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface-2);color:var(--mlv-text);text-align:left;cursor:pointer}.mlv-relations__row:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px}.mlv-relations__path,.mlv-relations__detail{display:block}.mlv-relations__detail{margin-top:var(--mlv-s1);color:var(--mlv-text-3);font-size:var(--mlv-fs-10)}.mlv-insp__actions{display:flex;gap:var(--mlv-s3);flex-wrap:wrap;margin-bottom:var(--mlv-s4)}.mlv-clean__title{color:var(--mlv-text);font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi)}.mlv-clean__detail{font-size:var(--mlv-fs-12);max-width:34ch}.mlv-issue__detail{padding:var(--mlv-s3) var(--mlv-s5) var(--mlv-s5) var(--mlv-s8);border-left:2px solid var(--mlv-accent);margin:0 var(--mlv-s3) var(--mlv-s3);background:var(--mlv-surface-2);border-radius:0 var(--mlv-r-btn) var(--mlv-r-btn) 0}.mlv-issue__detail .mlv-insp__line{font-size:var(--mlv-fs-12)}.mlv-issue__goto{display:flex;flex-wrap:wrap;gap:var(--mlv-s2);margin-top:var(--mlv-s4)}.mlv-empty-note .mlv-btn{margin-top:var(--mlv-s4)}.mlv-insp__issue.is-selected{box-shadow:0 0 0 2px var(--mlv-accent)}.mlv-issue__meta{flex-wrap:wrap;row-gap:var(--mlv-s1);min-width:0}.mlv-issue__meta .mlv-chip{max-width:100%}.mlv-phaseindex{position:absolute;right:12px;bottom:12px;z-index:20;display:flex;flex-direction:column;align-items:flex-end;gap:4px;max-width:calc(100% - 24px);max-height:calc(100% - 24px);pointer-events:none}.mlv-phaseindex[hidden]{display:none}.mlv-phaseindex__panel,.mlv-phaseindex__pill{pointer-events:auto}.mlv-phaseindex__panel{display:flex;flex-direction:column;width:max-content;min-width:min(288px,100%);max-width:min(360px,100%);min-height:0;padding:4px 0;border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);overflow:hidden}.mlv-phaseindex__panel[hidden]{display:none}.mlv-phaseindex__head{display:flex;align-items:center;gap:var(--mlv-s3);flex:none;height:26px;padding:0 4px 0 10px;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-phaseindex__title{color:var(--mlv-text);font-weight:var(--mlv-fw-semi)}.mlv-phaseindex__total{flex:1 1 auto;font-variant-numeric:tabular-nums}.mlv-phaseindex__fold{flex:none;width:22px;height:22px;min-height:0;padding:0;color:var(--mlv-text-2)}.mlv-phaseindex__fold .mlv-uicon{transform:rotate(90deg)}.mlv-phaseindex__rows{flex:1 1 auto;min-height:0;margin:0;padding:0;overflow-y:auto;list-style:none}.mlv-phaseindex__row{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;height:24px;padding:0 10px 0 7px;border:0;border-left:3px solid transparent;background:none;color:var(--mlv-text);font:inherit;font-size:var(--mlv-fs-12);text-align:left;cursor:pointer}.mlv-phaseindex__row:hover{background:var(--mlv-surface-2)}.mlv-phaseindex__row:focus-visible{outline:1px solid var(--mlv-focus);outline-offset:-1px}.mlv-phaseindex__row[data-in-view=true]{border-left-color:var(--mlv-accent);background:color-mix(in srgb,var(--mlv-accent) 12%,transparent)}.mlv-phaseindex__num,.mlv-phaseindex__pillnum{flex:none;color:var(--mlv-text-2);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-phaseindex__num{min-width:14px}.mlv-phaseindex__row:before{content:"";flex:none;width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-phaseindex__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-phaseindex__sev{flex:none}.mlv-phaseindex__steps{flex:none;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-variant-numeric:tabular-nums}.mlv-phaseindex__pill{display:flex;align-items:center;gap:var(--mlv-s3);max-width:min(360px,100%);height:28px;padding:0 8px 0 10px;border:1px solid var(--mlv-border-strong);border-radius:14px;background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);color:var(--mlv-text);font:inherit;font-size:var(--mlv-fs-12);cursor:pointer}.mlv-phaseindex__pill[hidden]{display:none}.mlv-phaseindex__pill:hover{background:var(--mlv-surface-2)}.mlv-phaseindex__pill:focus-visible{outline:1px solid var(--mlv-focus);outline-offset:1px}.mlv-phaseindex__pilllabel{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-phaseindex__pillchev{flex:none;color:var(--mlv-text-2);transform:rotate(-90deg)}.mlv-phaseindex[data-open=true] .mlv-phaseindex__pillchev{transform:rotate(90deg)}.mlv-phaseindex[data-form=pill] .mlv-phaseindex__panel{max-height:calc(100% - 32px)}.mlv-phaseindex[data-form=pill] .mlv-phaseindex__fold{display:none}.mlv-overview{position:absolute;inset:0;z-index:35;overflow:auto;overscroll-behavior:contain;touch-action:pan-x pan-y;background:var(--mlv-bg);cursor:default}.mlv-overview[hidden]{display:none}.mlv-overview__content{position:relative}.mlv-overview__header{position:relative;z-index:2;box-sizing:border-box;background:var(--mlv-bg)}.mlv-overview__header[data-sticky=true]{position:sticky;top:0;box-shadow:0 1px 0 var(--mlv-border)}.mlv-overview__top{display:flex;align-items:center;gap:var(--mlv-s4);height:24px}.mlv-overview__title{flex:1 1 auto;min-width:0;margin:0;overflow:hidden;color:var(--mlv-text);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);text-overflow:ellipsis;white-space:nowrap}.mlv-overview__back{display:inline-flex;align-items:center;gap:var(--mlv-s2);flex:none;height:22px;min-height:0;padding:0 6px;color:var(--mlv-text);font-size:var(--mlv-fs-12)}.mlv-overview__kbd{padding:0 4px;border:1px solid var(--mlv-border-strong);border-radius:3px;color:var(--mlv-text-2);font-family:inherit;font-size:var(--mlv-fs-10)}.mlv-overview__summary,.mlv-overview__key{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);line-height:17px}.mlv-overview__summary{color:var(--mlv-text)}.mlv-overview__links{position:absolute;top:0;left:0;overflow:visible;pointer-events:none}.mlv-overview__arrow,.mlv-overview__bracket{fill:none;stroke:var(--mlv-edge, var(--mlv-text-2));stroke-width:1.5}.mlv-overview__arrow{stroke-width:2}.mlv-overview__bracket[data-back=true]{stroke-dasharray:5 3}.mlv-overview__head{fill:var(--mlv-edge, var(--mlv-text-2))}.mlv-overview__arrowlabel{position:absolute;transform:translateY(-50%);color:var(--mlv-text-2);font-size:var(--mlv-fs-12);white-space:nowrap;pointer-events:none}.mlv-overview__count{position:absolute;min-width:14px;padding:0 3px;border:1px solid var(--mlv-border-strong);border-radius:7px;background:var(--mlv-bg);color:var(--mlv-text);font-size:var(--mlv-fs-10);line-height:13px;text-align:center;font-variant-numeric:tabular-nums;transform:translate(-50%,-50%)}.mlv-overview__count[data-back=true]{border-style:dashed}.mlv-overview__blocks,.mlv-overview__item{margin:0;padding:0;list-style:none}.mlv-ovblock{position:absolute;box-sizing:border-box;overflow:hidden;border:1px solid color-mix(in srgb,var(--mlv-stage, var(--mlv-text-2)) 45%,var(--mlv-border-strong));border-radius:8px;background:color-mix(in srgb,var(--mlv-stage, var(--mlv-text-2)) 7%,var(--mlv-surface));cursor:pointer}.mlv-ovblock:before{content:"";position:absolute;top:0;bottom:0;left:0;width:4px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-ovblock:hover{border-color:var(--mlv-stage, var(--mlv-border-strong))}.mlv-ovblock:focus-visible,.mlv-ovblock:focus{outline:2px solid var(--mlv-focus);outline-offset:1px}.mlv-ovblock__head{display:flex;align-items:center;gap:var(--mlv-s5);box-sizing:border-box;padding:0 12px 0 16px}.mlv-ovblock[data-head=two-lines] .mlv-ovblock__head{flex-direction:column;align-items:stretch;justify-content:center;gap:2px}.mlv-ovblock[data-head=two-lines] .mlv-ovblock__name{flex:none}.mlv-ovblock__name{display:flex;align-items:baseline;gap:var(--mlv-s3);flex:1 1 auto;min-width:0}.mlv-ovblock__num{flex:none;color:var(--mlv-text-2);font-size:15px;font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-ovblock__label{min-width:0;overflow:hidden;color:var(--mlv-text);font-size:15px;font-weight:var(--mlv-fw-semi);line-height:20px;text-overflow:ellipsis;white-space:nowrap}.mlv-ovblock__meta{display:flex;align-items:center;gap:var(--mlv-s3);flex:0 1 auto;min-width:0;overflow:hidden;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);line-height:18px;white-space:nowrap}.mlv-ovblock__steps,.mlv-ovblock__meta .mlv-cluster{flex:none}.mlv-ovblock__unit{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-ovblock__unit:before{content:"\b7 "}.mlv-ovblock__list{margin:0;padding:0;list-style:none}.mlv-ovitem{position:absolute;display:flex;align-items:center;gap:2px;box-sizing:border-box;height:18px;color:var(--mlv-text);font-size:12.5px;line-height:18px;white-space:nowrap}.mlv-ovitem__mark{flex:none;width:14px;color:var(--mlv-text-2);text-align:center}.mlv-ovitem[data-basis] .mlv-ovitem__mark{color:var(--mlv-text);font-weight:var(--mlv-fw-semi)}.mlv-ovmark{display:inline-block;box-sizing:border-box;width:9px;height:9px;border:1.5px dotted currentColor;border-radius:50%;vertical-align:-1px}.mlv-overview__key .mlv-ovmark{margin-right:3px}.mlv-ovitem__title{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-ovitem__tags{display:inline-flex;align-items:center;gap:var(--mlv-s2);flex:none;margin-left:var(--mlv-s3)}.mlv-ovitem__tag{display:inline-flex;align-items:center;gap:1px;font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-ovitem--more{padding-left:16px;color:var(--mlv-text-2)}.mlv-root[data-theme=hc] .mlv-ovblock{border-color:var(--vscode-contrastBorder, var(--mlv-border-strong));background:var(--mlv-surface)}.mlv-root[data-theme=hc] .mlv-phaseindex__row[data-in-view=true]{background:none;outline:1px dashed var(--mlv-accent);outline-offset:-2px}.mlv-edge--weighted .mlv-edge__path{stroke-width:max(var(--mlv-edge-w, 2px),calc(1px / var(--mlv-z, 1)))}.mlv-edge__weight{pointer-events:none}.mlv-edge__weightbg{fill:var(--mlv-surface);stroke:var(--mlv-border-strong);stroke-width:1}.mlv-edge__weighttext{fill:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:9px;font-variant-numeric:tabular-nums;text-anchor:middle;dominant-baseline:middle}.mlv-edge.has-issue .mlv-edge__weightbg{stroke:var(--mlv-sev, var(--mlv-border-strong))}.mlv-canvas[data-lod=compact] .mlv-edge__weight{display:none}:root[data-theme=hc] .mlv-edge__weightbg,.mlv-root[data-theme=hc] .mlv-edge__weightbg{stroke:currentColor}.mlv-workflow{position:relative;z-index:45;flex:none;height:0}.mlv-workflow__composer{position:absolute;top:4px;right:var(--mlv-s4);display:flex;flex-wrap:wrap;align-items:center;gap:8px;width:min(440px,calc(100vw - 2 * var(--mlv-s4)));padding:10px 12px;border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3)}.mlv-workflow__composer[hidden]{display:none}.mlv-workflow__selection{flex:1 0 100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text);font-size:12px;font-weight:var(--mlv-fw-medium)}.mlv-workflow__intent{flex:0 0 auto}.mlv-workflow__custom{flex:1 1 180px;min-width:0}.mlv-workflow__custom[hidden]{display:none}.mlv-workflow__status{flex:1 0 100%;color:var(--mlv-text);font-size:11px}.mlv-workflow__status:empty{display:none}.mlv-workflow__hint{flex:1 0 100%;margin:0;color:var(--mlv-text-2);font-size:11px}#mlview-authored-error{white-space:pre-wrap;margin:0;padding:6px 12px;flex:none;max-height:20vh;overflow-y:auto;overscroll-behavior:contain;font-family:var(--mlv-font, var(--vscode-font-family, system-ui, sans-serif));font-size:12px;line-height:1.45;color:var(--mlv-text, var(--vscode-foreground, inherit));background:var(--mlv-surface-2, var(--vscode-editorWidget-background, transparent));border-bottom:1px solid var(--mlv-sev-medium, var(--vscode-editorWarning-foreground, currentColor))}.mlv-hostnotice{display:flex;align-items:flex-start;gap:var(--mlv-s3);flex:none;padding:6px 12px;font-size:12px;line-height:1.45;color:var(--mlv-text);background:var(--mlv-surface-2);border-bottom:1px solid var(--mlv-border)}.mlv-hostnotice[hidden]{display:none}.mlv-hostnotice--warn{border-bottom-color:var(--mlv-sev-medium)}.mlv-hostnotice--error{border-bottom-color:var(--mlv-sev-high)}.mlv-hostnotice__icon{flex:none;display:flex;padding-top:2px}.mlv-hostnotice--warn .mlv-hostnotice__icon{color:var(--mlv-sev-medium)}.mlv-hostnotice--error .mlv-hostnotice__icon{color:var(--mlv-sev-high)}.mlv-hostnotice__text{flex:1 1 auto;min-width:0;margin:0;white-space:pre-wrap;overflow-wrap:anywhere;max-height:20vh;overflow-y:auto;overscroll-behavior:contain}.mlv-hostnotice__actions{flex:none;display:flex;flex-wrap:wrap;gap:6px}.mlv-hostnotice__actions[hidden]{display:none}.mlv-status__fresh{display:inline-flex;align-items:center;gap:4px}.mlv-status__fresh.is-warn{color:var(--mlv-text)}.mlv-status__fresh.is-warn .mlv-uicon{color:var(--mlv-stale-ink)}.mlv-status__fresh.is-checking{color:var(--mlv-text-2);font-style:italic}.mlv-status__fresh.is-muted{color:var(--mlv-text-2)}.mlv-insp__stale-note{display:flex;align-items:flex-start;gap:6px;margin:4px 0 6px;font-size:11px;color:var(--mlv-text)}.mlv-insp__stale-note .mlv-uicon,.mlv-insp__stale .mlv-uicon,.mlv-chip--stale .mlv-uicon{flex:none;color:var(--mlv-stale-ink)}.mlv-insp__stale{display:inline-flex;align-items:center;gap:4px;margin-left:6px;font-size:11px;color:var(--mlv-text)}.mlv-link.is-stale,.mlv-btn.is-stale{cursor:not-allowed;color:var(--mlv-text-2);text-decoration:line-through}.mlv-chip--stale{display:inline-flex;align-items:center;gap:3px}.mlv-sheet__note{margin:0 0 8px;font-size:12px;color:var(--mlv-text-2)}.mlv-root--workflow>.mlv-body{flex:1 1 0;min-height:min(320px,50vh)}.mlv-walkbar{flex:none;display:flex;flex-direction:column;gap:2px;padding:4px var(--mlv-s4) 5px var(--mlv-s5);border-top:1px solid var(--mlv-border);background:var(--mlv-surface-2);color:var(--mlv-text);font-size:var(--mlv-fs-12);line-height:1.4}.mlv-walkbar[hidden]{display:none}.mlv-walkbar__row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--mlv-s2) var(--mlv-s4);min-width:0}.mlv-walkbar__pos{display:inline-flex;flex:none;align-items:center;gap:4px;margin:0;font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums;white-space:nowrap}.mlv-walkbar__mark{display:inline-flex;color:var(--mlv-stale-ink)}.mlv-walkbar__mark[hidden]{display:none}.mlv-walkbar__tools{display:flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:var(--mlv-s2) var(--mlv-s4);min-width:0}.mlv-walkbar__steps{display:inline-flex;flex:none;gap:2px}.mlv-walkbar__step{width:24px;min-width:0;height:22px;min-height:0;padding:0}.mlv-walkbar__step--prev .mlv-uicon{transform:rotate(-90deg)}.mlv-walkbar__step--next .mlv-uicon{transform:rotate(90deg)}.mlv-walkbar__filters{display:inline-flex;flex-wrap:wrap;gap:var(--mlv-s2)}.mlv-walkbar__filter{display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-chip);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);white-space:nowrap;cursor:pointer}.mlv-walkbar__filter[hidden]{display:none}.mlv-walkbar__filter:hover{border-color:var(--mlv-border-strong)}.mlv-walkbar__filter[aria-pressed=true]{border-color:var(--mlv-text-2);font-weight:var(--mlv-fw-semi)}.mlv-walkbar__fcount{color:var(--mlv-text-2);font-variant-numeric:tabular-nums}.mlv-walkbar__filter[aria-pressed=true] .mlv-walkbar__fcount{color:var(--mlv-text)}.mlv-walkbar__keys{display:inline-flex;flex-wrap:wrap;gap:var(--mlv-s2) var(--mlv-s4);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-walkbar__keys[hidden]{display:none}.mlv-walkbar__key{display:inline-flex;align-items:center;gap:2px;white-space:nowrap}.mlv-walkbar__exit{margin-left:auto;min-height:22px;padding:0 var(--mlv-s3)}.mlv-walkbar__editor{display:flex;align-items:flex-start;gap:5px;margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-walkbar__editor[hidden]{display:none}.mlv-walkbar__editor[data-walk-status=blocked],.mlv-walkbar__editor[data-walk-status=failed]{color:var(--mlv-text)}.mlv-walkbar__editoricon{display:inline-flex;flex:none;padding-top:1px;color:var(--mlv-stale-ink)}.mlv-walkbar__editoricon[hidden]{display:none}.mlv-walkbar[data-layout=narrow]{padding:3px var(--mlv-s3)}.mlv-walkbar[data-layout=narrow] .mlv-walkbar__tools{flex:1 1 0}.mlv-walkbar[data-layout=narrow] .mlv-walkbar__filters{flex:1 1 0;min-width:0}.mlv-walkbar[data-layout=narrow] .mlv-walkbar__exitlabel{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.mlv-header__review{flex:none;height:24px;min-height:0;padding:0 var(--mlv-s3);gap:5px}.mlv-header__review[aria-pressed=true]{border-color:var(--mlv-text-2)}.mlv-quote.is-walk{margin-left:calc(-1 * var(--mlv-s3));padding-left:calc(var(--mlv-s3) - 2px);border-left:2px solid var(--mlv-focus)}.mlv-quote__walk{display:flex;align-items:flex-start;gap:5px;margin:3px 0 0 calc(1.4em + var(--mlv-s3));color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-quote.is-walk[data-walk-status=blocked] .mlv-quote__walk,.mlv-quote.is-walk[data-walk-status=failed] .mlv-quote__walk{color:var(--mlv-text)}.mlv-quote__walk .mlv-uicon{flex:none;margin-top:1px;color:var(--mlv-stale-ink)}@page{margin:12mm}@media print{.mlv-header,.mlv-workflow,.mlv-hostnotice,.mlv-walkbar,.mlv-status,.mlv-rail,.mlv-phaseindex,.mlv-overview,.mlv-zoom,.mlv-skiplink,.mlv-legend,.mlv-moremenu,.mlv-statehost,.mlv-tooltip,.mlv-toasts,.mlv-shortcuts{display:none!important}html.mlv-fills-page,html.mlv-fills-page>body,.mlv-root,.mlv-body,.mlv-main,.mlv-canvas{display:block!important;height:auto!important;max-height:none!important;min-height:0!important;overflow:visible!important}.mlv-world{position:static!important;transform:none!important;transform-origin:0 0!important}.mlv-canvas{background-image:none!important;cursor:auto!important}.mlv-canvas[data-lod=compact] .mlv-node__sub,.mlv-canvas[data-lod=compact] .mlv-node__loc{display:block!important}.mlv-canvas[data-lod=compact] .mlv-node__sub[data-lines="2"]{display:-webkit-box!important}.mlv-canvas[data-lod=compact] .mlv-node__chips{display:flex!important}.mlv-canvas[data-lod=compact] .mlv-node__title{font-size:var(--mlv-fs-13)!important;line-height:var(--mlv-lh-tight)!important}.mlv-canvas[data-lod=compact] .mlv-node__iconbox{display:flex!important}.mlv-canvas[data-lod=compact] .mlv-node__main{align-items:flex-start!important;padding:10px var(--mlv-s5) 10px var(--mlv-s4)!important}.mlv-canvas[data-lod=compact] .mlv-node>.mlv-basis-tag{transform-origin:0 50%!important;transform:translateY(-50%) scale(calc(1 / var(--mlv-z, 1)))!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap{-webkit-line-clamp:1!important;line-clamp:1!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="2"]{-webkit-line-clamp:2!important;line-clamp:2!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3!important;line-clamp:3!important}.mlv-canvas[data-lod=compact] .mlv-edge-label{display:inline!important;opacity:0!important}.mlv-canvas[data-lod=compact] .mlv-edge--data .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge--control .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge--merged .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge.has-label .mlv-edge-label{opacity:1!important}.mlv-root{-webkit-print-color-adjust:exact;print-color-adjust:exact}.mlv-edge__flow,.mlv-flow-charge{display:none!important}.mlv-node,.mlv-group,.mlv-lane{break-inside:avoid}} diff --git a/vscode-extension/media/mlview.js b/vscode-extension/media/mlview.js index f79d6be9..abe9e200 100644 --- a/vscode-extension/media/mlview.js +++ b/vscode-extension/media/mlview.js @@ -1,9 +1,9 @@ -"use strict";var MLView=(()=>{var Qn=Object.defineProperty;var Qa=Object.getOwnPropertyDescriptor;var $a=Object.getOwnPropertyNames;var Ja=Object.prototype.hasOwnProperty;var ed=(t,e)=>{for(var n in e)Qn(t,n,{get:e[n],enumerable:!0})},td=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of $a(e))!Ja.call(t,i)&&i!==n&&Qn(t,i,{get:()=>e[i],enumerable:!(o=Qa(e,i))||o.enumerable});return t};var nd=t=>td(Qn({},"__esModule",{value:!0}),t);var fg={};ed(fg,{bridges:()=>hg,mountWorkflow:()=>ug,normalizeWorkflow:()=>Zt,version:()=>cg});function Te(t){if(!t||typeof t.evidenceId!="string")return null;let e=t.cell;return typeof e!="number"||!isFinite(e)||e<0||Math.trunc(e)!==e?null:{cell:e,line:t.line}}function Ke(t){let e=lt(t);return e.head+e.tail}function lt(t){let e=Te(t);return e?{head:t.file,tail:" › cell "+e.cell+", line "+e.line}:{head:t.file,tail:":"+t.line}}function Rt(t){let e=Te(t);return e?t.file+" cell "+e.cell+" line "+e.line:t.file+" line "+t.line}function Re(t){let e=Te(t);return e?"cell "+e.cell+", counted from 0 as the artifact records it (markdown cells count too); line "+e.line+" of that cell":""}var he=["http","//www.w3.org/2000/svg"].join(":"),Bi=["http","//www.w3.org/1999/xlink"].join(":");function f(t,e,n){let o=document.createElement(t);return e&&(o.className=e),n!==void 0&&(o.textContent=n),o}function R(t,e){let n=document.createElementNS(he,t);return e&&fe(n,e),n}function fe(t,e){for(let n of Object.keys(e))t.setAttribute(n,String(e[n]))}function H(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function g(t,e){return t.appendChild(e),e}function W(t,e,n){let o=f("button",t,e);return o.type="button",o.setAttribute("aria-label",n||e),n&&(o.title=n),o}function ne(t,e){let n=f("button",t);return n.type="button",n.title=e,n.setAttribute("aria-label",e),n}function _(t,e,n,o){return t.addEventListener(e,n,o),()=>t.removeEventListener(e,n,o)}function Di(t,e){let n=null,o=((...i)=>{n!==null&&clearTimeout(n),n=setTimeout(()=>{n=null,t(...i)},e)});return o.cancel=()=>{n!==null&&clearTimeout(n),n=null},o}function de(t){return Ke(t)}function Oe(t,e,n="span"){let o=f(n,t+(t?" ":"")+"mlv-loc"),i=lt(e);g(o,f("span","mlv-loc__file",i.head)),g(o,f("span","mlv-loc__at",i.tail));let s=Te(e);return s&&(o.setAttribute("data-cell",String(s.cell)),o.title=Re(e)),o}var Ot={low:"M9 2A7 7 0 1 0 9 16A7 7 0 0 0 9 2Z",medium:"M9.9 2.75a1.04 1.04 0 0 0-1.8 0L1.5 14.6a1.04 1.04 0 0 0 .9 1.56h13.2a1.04 1.04 0 0 0 .9-1.56Z",high:"M6.05 1.4h5.9L16.6 6.05v5.9L11.95 16.6h-5.9L1.4 11.95v-5.9Z"},$n={low:"M9 4.3a1.08 1.08 0 1 0 0 2.16A1.08 1.08 0 0 0 9 4.3ZM7.94 7.65h2.12v5.7H7.94Z",medium:"M7.95 6.45h2.1l-.28 4.62h-1.54ZM9 12.1a1.12 1.12 0 1 0 0 2.24A1.12 1.12 0 0 0 9 12.1Z",high:"M7.94 4.5h2.12l-.3 6.05h-1.52ZM9 11.7a1.15 1.15 0 1 0 0 2.3A1.15 1.15 0 0 0 9 11.7Z"},Z=["high","medium","low"],ye={high:"high",medium:"medium",low:"low"};function od(t){return t==="low"||t==="medium"||t==="high"}function $(t){return od(t)?t:"low"}function pe(){return{low:0,medium:0,high:0}}function J(t){return t.low+t.medium+t.high}function Y(t){return t.high>0?"high":t.medium>0?"medium":t.low>0?"low":null}function U(t,e=18,n){let o=$(t),i=R("svg",{class:"mlv-glyph mlv-glyph--"+o,viewBox:"0 0 18 18",width:e,height:e,role:"img","aria-label":n||ye[o]+" severity"}),s=document.createElementNS(he,"path");fe(s,{class:"mlv-glyph__shape",d:Ot[o],"vector-effect":"non-scaling-stroke"});let r=document.createElementNS(he,"path");return fe(r,{class:"mlv-glyph__ink",d:$n[o]}),i.appendChild(s),i.appendChild(r),i}function Gi(t){let e=J(t),n=Y(t);return e+(e===1?" finding":" findings")+", highest severity "+(n?ye[n]:"none")}function id(t){let e=J(t),n=Z.filter(o=>t[o]>0).map(o=>t[o]+" "+ye[o]);return e+(e===1?" finding":" findings")+": "+n.join(", ")}var ji=2;function Jn(t){let e=t.slice(0,ji).join(" "),n=t.length-ji;return n>0?e+" +"+n:e}function zi(t){return t.length?t.length===1?"finding "+t[0]:"findings "+t.slice(0,-1).join(", ")+" and "+t[t.length-1]:""}function Vi(t,e=18,n=[]){let o=Y(t);if(!o)return null;let i=J(t),s=f("div","mlv-badge mlv-badge--"+o);return s.setAttribute("role","img"),s.setAttribute("aria-label",n.length?zi(n).replace(/^f/,"F")+", highest severity "+ye[o]:Gi(t)),s.appendChild(U(o,e,"")),n.length?s.appendChild(f("span","mlv-badge__ids",Jn(n))):i>1&&s.appendChild(f("span","mlv-badge__count",String(i))),s}function Ht(t,e=14,n){if(J(t)===0)return null;let o=f("div","mlv-cluster");o.setAttribute("role","img"),o.setAttribute("aria-label",n||Gi(t)),o.title=n||id(t);for(let i of Z){let s=t[i];if(s<=0)continue;let r=f("span","mlv-cluster__item mlv-cluster__item--"+i);r.appendChild(U(i,e,"")),r.appendChild(f("span","mlv-cluster__count",String(s))),o.appendChild(r)}return o}function eo(t){return t/2+1.5}function Wi(t,e=16,n=[]){let o=$(t),i=R("g",{class:"mlv-edge-marker mlv-edge-marker--"+o,role:"img"}),s=n.length?zi(n)+" on this connection, highest severity "+ye[o]:ye[o]+" severity finding on this connection";if(i.setAttribute("aria-label",s.charAt(0).toUpperCase()+s.slice(1)),n.length){let a=R("title");a.textContent=i.getAttribute("aria-label")||"",i.appendChild(a)}let r=R("circle",{class:"mlv-edge-marker__disc",r:eo(e),cx:0,cy:0});i.appendChild(r);let l=U(o,e,"");return l.setAttribute("x",String(-e/2)),l.setAttribute("y",String(-e/2)),l.removeAttribute("role"),l.removeAttribute("aria-label"),l.setAttribute("aria-hidden","true"),i.appendChild(l),i}var sd=["changed","missing","unreadable","too-large","elsewhere"];var me={changed:"changed since publishing",missing:"file missing",unreadable:"file unreadable","too-large":"file too large to check",elsewhere:"in another folder; see the notice above"};function ie(t){return t.length>0&&t.every(e=>e==="elsewhere")}function Ki(t){if(!Array.isArray(t))return[];let e=[],n=new Set;for(let o of t){if(e.length>=2e3)break;if(!o||typeof o!="object")continue;let i=o.path,s=o.reason;typeof i!="string"||!i||i.length>500||n.has(i)||typeof s!="string"||sd.indexOf(s)<0||(n.add(i),e.push({path:i,reason:s}))}return e}var Nt=class{constructor(){this.reasons=new Map;this.checking=!1}set(e){let n=new Map(e.map(i=>[i.path,i.reason])),o=n.size===this.reasons.size;if(o)for(let[i,s]of n)this.reasons.get(i)!==s&&(o=!1);return this.reasons=n,!o}reasonOf(e){return e?this.reasons.get(e):void 0}get size(){return this.reasons.size}paths(){return Array.from(this.reasons.keys())}list(){return Array.from(this.reasons,([e,n])=>({path:e,reason:n}))}};function qi(t,e){let n=t||[],o=0;for(let i of n)i.file&&e(i.file)&&o++;return{stale:o,total:n.length}}function rd(t){if(ie(t))return"in another folder";let e=new Set(t);return e.size===1&&e.has("changed")?"changed":e.has("changed")?"changed or missing":"missing"}function ld(t,e){if(!t||e.size===0)return null;let n=Array.from(new Set((t.evidence||[]).map(c=>c.file))),o=n.filter(c=>e.reasonOf(c)),i=e.paths().filter(c=>n.indexOf(c)<0),s=rd(e.list().map(c=>c.reason)),r;o.length?(r=o.length+" of "+n.length+" cited files "+s,i.length&&(r+=" · "+i.length+" inspected")):r=i.length+" inspected "+(i.length===1?"file":"files")+" "+s;let l=e.list().slice(0,20).map(c=>c.path+" — "+me[c.reason]);e.size>20&&l.push("and "+(e.size-20)+" more");let d=(ie(e.list().map(c=>c.reason))?"Source files that are not under the workspace root but are unchanged in another folder; the notice above names it. Jumps into them are blocked.":"Source files that no longer match the published revision. Jumps into them are blocked.")+` -`+l.join(` -`);return{text:r,title:d}}function Ui(t,e){if(!t)return null;let n=ld(t,e);if(n)return{state:"stale",text:n.text,title:n.title};if(!t.verification)return{state:"unverified",text:"Freshness not checked",title:"This revision was published without source hashes, so MLView cannot tell whether the cited files changed since."};let o=new Set((t.evidence||[]).map(i=>i.file)).size;return{state:"unchanged",text:o+(o===1?" cited file unchanged":" cited files unchanged"),title:"The "+(o===1?"file":o+" files")+" the quotes cite still match the hashes published with revision "+t.revision.id+". Unchanged means the quoted lines still exist as published; it does not mean they support the claims."}}function Xi(t,e){if(!(!t||typeof t!="object"||t.v!==1))switch(t.type){case"init":e.init(t.theme,t.capabilities);return;case"workflow":e.workflow(t.document);return;case"workflowError":{let n=Array.isArray(t.codes)?t.codes.filter(o=>typeof o=="string"):void 0;e.workflowStatus(n),e.hostNotice(typeof t.message=="string"?t.message:"",n);return}case"stale":e.stale(Ki(t.files));return;case"actionResult":e.actionResult(t);return;case"theme":e.theme(t.kind);return;case"revealNode":e.revealNode(t.nodeId,t.center!==!1);return;case"revealIssue":e.revealIssue(t.issueId);return;case"restoreState":e.restoreState(t.state);return;default:e.onUnknown(String(t.type))}}function Yi(t,e){return!t||typeof t!="object"?{...e,severities:e.severities.slice()}:{severities:Array.isArray(t.severities)?t.severities.slice():e.severities.slice(),query:typeof t.query=="string"?t.query:""}}var ad=["low","medium","high"];function to(){return{severities:ad.slice(),query:""}}var Pt=class{constructor(){this.current=to();this.keep=e=>this.current.severities.indexOf($(e.severity))>=0}get value(){return this.current}get query(){return this.current.query}patch(e){let n=this.snapshot();e.severities!==void 0&&(n.severities=e.severities.slice()),e.query!==void 0&&(n.query=String(e.query)),this.current=n}reset(){this.current=to()}restore(e){this.current=Yi(e,to())}snapshot(){return{severities:this.current.severities.slice(),query:this.current.query}}toggleSeverity(e){let n=this.current.severities.slice(),o=n.indexOf(e);o>=0?n.splice(o,1):n.push(e),this.patch({severities:n})}};function Zi(t){return t==="light"||t==="dark"||t==="hc"}var Ft=class{constructor(e,n){this.root=e,this.current=Zi(n)?n:"light"}get kind(){return this.current}apply(e){Zi(e)&&(this.current=e,this.root.setAttribute("data-theme",e))}destroy(){this.root.removeAttribute("data-theme")}};var dd='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function cd(t){let e=[];for(let n of Array.from(t.querySelectorAll(dd))){if(n.tabIndex<0)continue;let o=!0;for(let i=n;i&&i!==t.parentElement;i=i.parentElement)if(i.hidden){o=!1;break}o&&e.push(n)}return e}function Bt(t,e){if(e.key!=="Tab"||e.altKey||e.ctrlKey||e.metaKey)return!1;let n=cd(t),o=t.ownerDocument?t.ownerDocument.activeElement:null;if(!n.length)return e.preventDefault(),!0;let i=n[0],s=n[n.length-1],r=!!o&&t.contains(o),l=null;if(e.shiftKey&&(!r||o===i)?l=s:!e.shiftKey&&(!r||o===s)&&(l=i),!l)return!1;e.preventDefault();try{l.focus()}catch{}return!0}function at(t,e){let n=i=>!!i&&i.isConnected&&!i.disabled&&typeof i.focus=="function",o=i=>{if(!n(i)||i===i.ownerDocument.body)return!1;try{i.focus()}catch{return!1}return i.ownerDocument.activeElement===i};o(t)||o(e)}var ut=8;function Qi(t){return(t%ut+ut)%ut}var Dt=class{constructor(e){this.nodeById=new Map;this.edgeById=new Map;this.issueById=new Map;this.issueRank=new Map;this.childrenOf=new Map;this.lexicalParentOf=new Map;this.laneChildrenOf=new Map;this.parentOf=new Map;this.rootsByLane=new Map;this.outEdges=new Map;this.inEdges=new Map;this.lanes=[];this.absentStages=[];this.phaseOrder=new Map;this.graph=e;for(let l of e.nodes||[])this.nodeById.set(l.id,l);for(let l of e.edges||[])this.edgeById.set(l.id,l);for(let l of e.issues||[])this.issueById.set(l.id,l),this.issueRank.has(l.id)||this.issueRank.set(l.id,this.issueRank.size);for(let l of e.nodes||[]){let a=l.parent&&this.nodeById.has(l.parent)?l.parent:null;this.lexicalParentOf.set(l.id,a),a&&ct(this.childrenOf,a,l.id)}for(let l of e.nodes||[])this.hasCycle(l.id)&&this.lexicalParentOf.set(l.id,null);for(let l of e.nodes||[]){let a=this.lexicalParentOf.get(l.id)||null,d=a?this.nodeById.get(a):void 0,c=!!d&&d.stage===l.stage;this.parentOf.set(l.id,c?a:null),c?ct(this.laneChildrenOf,a,l.id):ct(this.rootsByLane,l.stage,l.id)}for(let l of e.edges||[])!this.nodeById.has(l.source)||!this.nodeById.has(l.target)||(ct(this.outEdges,l.source,l),ct(this.inEdges,l.target,l));let n=new Map;for(let l of e.stages||[])n.set(l.id,l);let o=(e.stages||[]).slice().sort((l,a)=>l.order-a.order||ud(l.id,a.id));for(let l of o)(this.rootsByLane.get(l.id)||[]).length>0||l.present?this.lanes.push(l):this.absentStages.push(l);let i=new Set(this.lanes.map(l=>l.id)),s=[];for(let l of e.nodes||[])!n.has(l.stage)&&!i.has(l.stage)&&(i.add(l.stage),s.push(l.stage));s.sort();for(let l of o)this.phaseOrder.has(l.id)||this.phaseOrder.set(l.id,this.phaseOrder.size);for(let l of s)this.phaseOrder.set(l,this.phaseOrder.size);let r=this.lanes.length?this.lanes[this.lanes.length-1].order+1:0;for(let l of s)this.lanes.push({id:l,label:l,order:r++,present:!0,nodeCount:(this.rootsByLane.get(l)||[]).length,issueCounts:pe(),maxSeverity:null})}phaseIndexOf(e){return this.phaseOrder.get(e)??0}hasCycle(e){let n=this.lexicalParentOf.get(e)||null,o=0;for(;n;){if(n===e||++o>4096)return!0;n=this.lexicalParentOf.get(n)||null}return!1}children(e){return this.childrenOf.get(e)||[]}laneChildren(e){return this.laneChildrenOf.get(e)||[]}roots(e){return this.rootsByLane.get(e)||[]}ancestors(e){let n=[],o=this.parentOf.get(e)||null;for(;o;)n.push(o),o=this.parentOf.get(o)||null;return n}isGroup(e){return this.laneChildren(e).length>0}issuesOf(e,n){let o=this.nodeById.get(e);if(!o)return[];let i=[];for(let s of o.issueIds){let r=this.issueById.get(s);r&&n(r)&&i.push(r)}return i}issuesOfEdge(e,n){let o=this.edgeById.get(e);if(!o)return[];let i=[];for(let s of o.issueIds){let r=this.issueById.get(s);r&&n(r)&&i.push(r)}return i}issuesOfEdges(e,n){if(e.length===1)return this.issuesOfEdge(e[0],n);let o=new Set,i=[];for(let s of e)dt(i,o,this.issuesOfEdge(s,n));return this.inDocumentOrder(i)}inDocumentOrder(e){return e.sort((n,o)=>(this.issueRank.get(n.id)??0)-(this.issueRank.get(o.id)??0))}countsFor(e){let n=pe();for(let o of e)n[$(o.severity)]++;return n}ownCounts(e,n){return this.countsFor(this.issuesOf(e,n))}subtreeIssues(e,n){let o=[],i=a=>{o.push(a);for(let d of this.laneChildren(a))i(d)};i(e);let s=new Set(o),r=new Set,l=[];for(let a of o){dt(l,r,this.issuesOf(a,n));for(let d of this.outEdges.get(a)||[])!s.has(d.target)||a===e&&d.target===e||dt(l,r,this.issuesOfEdge(d.id,n))}return this.inDocumentOrder(l)}subtreeCounts(e,n){return this.countsFor(this.subtreeIssues(e,n))}descendantCount(e){let n=0;for(let o of this.laneChildren(e))n+=1+this.descendantCount(o);return n}laneCounts(e,n){let o=new Set,i=[];for(let s of this.graph.nodes||[])s.stage===e&&dt(i,o,this.issuesOf(s.id,n));for(let s of this.graph.edges||[]){let r=this.nodeById.get(s.source);!r||r.stage!==e||dt(i,o,this.issuesOfEdge(s.id,n))}return this.countsFor(i)}isHidden(e,n){let o=this.parentOf.get(e)||null;for(;o;){if(n.has(o))return!0;o=this.parentOf.get(o)||null}return!1}visibleRepresentative(e,n){let o=this.ancestors(e);for(let i=o.length-1;i>=0;i--)if(n.has(o[i]))return o[i];return e}defaultCollapsed(){let e=[],n=(this.graph.nodes||[]).length>120,o=this.findingTargetAncestors();for(let i of this.graph.nodes||[]){if(!this.isGroup(i.id)||o.has(i.id))continue;let s=this.descendantCount(i.id);(s>12||n&&s>4)&&e.push(i.id)}return e.sort()}findingTargetAncestors(){let e=new Set;for(let o of this.graph.issues||[]){for(let i of o.nodeIds||[])e.add(i);for(let i of o.edgeIds||[]){let s=this.edgeById.get(i);s&&(e.add(s.source),e.add(s.target))}}let n=new Set;for(let o of e)for(let i of this.ancestors(o))n.add(i);return n}};function dt(t,e,n){for(let o of n)e.has(o.id)||(e.add(o.id),t.push(o))}function ct(t,e,n){let o=t.get(e);o?o.push(n):t.set(e,[n])}function ud(t,e){return te?1:0}function se(t,e){t.setAttribute("data-phase-index",String(e)),t.setAttribute("data-phase-tone",String(Qi(e)))}function Ji(t){return t.length||1}function jt(t){if(t<2)return 1.5;let e=1.5+Math.log2(t)*.75;return Math.round(Math.min(5,e)*100)/100}function ht(t){return"×"+t}var $i=13;function Gt(t,e,n){if(!n)return{x:t.x,y:t.y};let o=typeof e=="number"&&isFinite(e)?e:0;return{x:t.x+Math.cos(o+Math.PI/2)*$i,y:t.y+Math.sin(o+Math.PI/2)*$i}}function zt(t){return Math.round(ht(t).length*5.6+10)}var xe=13;var es={rankdir:"LR",ranksep:72,nodesep:24,edgesep:12,marginx:16,marginy:16,ranker:"network-simplex"},hd={data:4,call:2,control:2,config:1};function ts(t){let e=hd[t];return typeof e=="number"?e:1}var ns=26,no=264,Vt=24,oo=28,io=2e3,os=20,so=8,fd=14,le=8,Wt=16,ro=12,lo=fd,is=112,ss=4,rs=13,ls=6,ao=28;var pd=6.02,Ut=14,md=1,gd=4,Kt=8,as=2,ds=20,cs=10,vd=3,co=14,bd=.7,qt=7,we=96;function ho(t){return t.label?t.back?"↻ "+t.label:t.label:""}function yd(t){return Ee(t.length*pd+2*gd)}function xd(t){return t.back||t.count>1||wd.indexOf(t.kind)>=0}var wd=["data","control","state","loop","output"];function uo(t,e,n){return t.x-ne.x&&t.y-ne.y}var Xt=class{constructor(){this.cells=new Map}add(e){let n=Math.floor(e.x/we),o=Math.floor((e.x+e.w)/we),i=Math.floor(e.y/we),s=Math.floor((e.y+e.h)/we);for(let r=n;r<=o;r++)for(let l=i;l<=s;l++){let a=r+":"+l,d=this.cells.get(a);d?d.push(e):this.cells.set(a,[e])}}hits(e,n){let o=Math.floor((e.x-n)/we),i=Math.floor((e.x+e.w+n)/we),s=Math.floor((e.y-n)/we),r=Math.floor((e.y+e.h+n)/we);for(let l=o;l<=i;l++)for(let a=s;a<=r;a++){let d=this.cells.get(l+":"+a);if(d){for(let c of d)if(uo(e,c,n))return!0}}return!1}};function Ed(t){let e=[];for(let n of t.boxes.values()){if(!n.isGroup||n.collapsed){e.push({x:n.x,y:n.y,w:n.w,h:n.h});continue}e.push({x:n.x,y:n.y,w:n.w,h:Math.max(n.headerH,1)})}return e}function _d(t){let e=[];for(let n of t){let o=n.y+28,i=n.y+n.h-28;i-o=n.top&&e.y+e.h<=n.bottom)return n;return null}function Md(t,e){return e.x>=0&&e.y>=0&&e.x+e.w<=t.width&&e.y+e.h<=t.height}function kd(t,e){let n=[];for(let o=1;o0?le:0,l=o.51){let c=s.x>i.x,u=Math.min(i.x,s.x)+(c?r:l),h=Math.max(i.x,s.x)-(c?l:r);if(h-um.bottom||n.push({axis:"h",at:p,a:u,b:h,laneId:m.id,score:h-u,order:o})}else if(a<.51&&d>.51){let c=s.y>i.y,u=Math.min(i.y,s.y)+(c?r:l),h=Math.max(i.y,s.y)-(c?l:r);if(h-u.001?e.score-t.score:t.order-e.order}function Ad(t){if(t.length<2)return null;let e=t[0],n=t[1],o=Math.abs(n.x-e.x),i=Math.abs(n.y-e.y);return i<.51&&o>.51?{axis:"h",at:(e.y+n.y)/2,a:Math.min(e.x,n.x),b:Math.max(e.x,n.x),laneId:"",score:o,order:1}:o<.51&&i>.51?{axis:"v",at:(e.x+n.x)/2,a:Math.min(e.y,n.y),b:Math.max(e.y,n.y),laneId:"",score:i,order:1}:null}function Id(t,e){let n=e/2,o=t.axis==="h"?n:Ut/2,i=(t.a+t.b)/2,s=t.a+o,r=t.b-o,l=r>s?fo(i,s,r):i,a=r>s?(r-s)/2:0,d=[0,a*.6,-a*.6,a,-a],c=[];for(let u of d){if(c.length>=cs)break;if(u!==0&&Math.abs(u)<1)continue;let h=l+u;for(let p of[!1,!0]){if(c.length>=cs)break;let m,v;t.axis==="h"?(m=h,v=p?t.at+Kt:t.at-Kt):(m=p?t.at-Kt-n:t.at+Kt+n,v=h),c.push({x:Ee(m),y:Ee(v),rect:{x:Ee(m-n),y:Ee(v-md-Ut/2),w:e,h:Ut},flipped:p,shift:Math.abs(Ee(u))})}}return c}function us(t){return{x:t.x-qt,y:t.y-qt,w:qt*2,h:qt*2}}function Cd(t){let e=0;for(let n=1;n=e||o===t.length-1){let s=i===0?0:fo((e-n)/i,0,1);return{x:Ee(t[o-1].x+(t[o].x-t[o-1].x)*s),y:Ee(t[o-1].y+(t[o].y-t[o-1].y)*s)}}n+=i}return t[t.length-1]}function Rd(t,e,n){if(!n||!uo(us(e),n,1))return{point:e,moved:!1};let o=Cd(t),i=o/2;for(let s of[18,-18,30,-30,44,-44]){let r=fo(i+s,Math.min(8,o),Math.max(o-8,0)),l=Td(t,r);if(!uo(us(l),n,1))return{point:l,moved:!0}}return{point:e,moved:!1}}function hs(t,e){let n=new Map,o={labels:0,always:0,placed:0,hidden:0,flipped:0,shifted:0,fallback:0,nudged:0},i=_d(t.lanes),s=new Xt;for(let a of Ed(t))s.add(a);let r=new Xt,l=[[],[]];for(let a of e){if(!a.label)continue;o.labels++;let d=xd(a);d&&o.always++,l[d?0:1].push(a)}for(let a=0;a({run:C,fallback:!1}));m&&v.push({run:m,fallback:!0});let y=null,S=null,x=!1,w=null,E=null,b=null,M=null,T=0;for(let C of v){if(y||T>=ds)break;for(let N of Id(C.run,h)){if(T>=ds)break;if(T++,!Md(t,N.rect))continue;let O=Sd(i,N.rect);if(O&&(E||(E=N,b=C.run,M=O),!s.hits(N.rect,as)&&!r.hits(N.rect,as))){y=N,S=C.run,x=C.fallback,w=O;break}}}if(!y&&!d&&E&&(y=E,S=b,w=M),!y){o.hidden++,n.set(c.id,{id:c.id,text:u,x:c.mid.x,y:c.mid.y,rect:{x:c.mid.x,y:c.mid.y,w:0,h:0},hidden:!0,flipped:!1,axis:v.length?v[0].run.axis:"h",laneId:null,fallback:x,shifted:0,marker:c.mid,always:d});continue}let L=Rd(c.points,c.mid,y.rect);L.moved&&o.nudged++,o.placed++,y.flipped&&o.flipped++,y.shift>0&&o.shifted++,x&&o.fallback++,r.add(y.rect),n.set(c.id,{id:c.id,text:u,x:y.x,y:y.y,rect:y.rect,hidden:!1,flipped:y.flipped,axis:S?S.axis:"h",laneId:w?w.id:null,fallback:x,shifted:y.shift,marker:L.point,always:d})}}return{placements:n,stats:o}}function fo(t,e,n){return tn?n:t}function Ee(t){return Math.round(t*100)/100}var Od=["data","call","control","config","state","loop","output"],Hd={data:"data",dataflow:"data","data flow":"data",flow:"data",control:"control","control flow":"control",branch:"control","conditional call":"control",decision:"control",state:"state","state update":"state",update:"state","state read":"state","state copy":"state",gradient:"state","gradient flow":"state",optimizes:"state",ownership:"state","state ownership":"state",config:"config",configuration:"config","configuration flow":"config",call:"call",calls:"call",loop:"loop",loops:"loop",output:"output",outputs:"output"};function mo(t){let e=String(t||"").trim();if(!e)return"unknown";let n=e.toLowerCase().replace(/[\s_-]+/g," ");return Hd[n]||e}function ft(t){return t&&t!=="unknown"?t:"kind not specified"}var fs=0;function ps(){return fs++,fs}function Nd(t,e){let n="";for(let o=0;o=0?t:"unknown"}function Fd(t,e,n,o){let i=R("marker",{id:t,viewBox:"0 0 10 10",refX:8.5,refY:5,markerWidth:9,markerHeight:9,markerUnits:"userSpaceOnUse",orient:"auto"}),s=R("path",{class:e+(o?"":" mlv-arrow--open"),d:n});return o||fe(s,{fill:"none"}),i.appendChild(s),i}var pt={d:"M0.5 1 L9 5 L0.5 9 Z"},ms="mlv-arrow";function gs(){let t=R("defs");return t.appendChild(Fd(ms,"mlv-arrow",pt.d,!0)),t}var vs=14,qe=eo(vs);function go(t){return t.placement?t.placement.marker:t.route.mid}function bs(t){let e=t.route,n=Pd(e.kind),o=t.weight&&t.weight>1?Math.floor(t.weight):1,i=o>=2,s=R("g",{class:"mlv-edge mlv-edge--"+n+(e.count>1?" mlv-edge--merged":"")+(i?" mlv-edge--weighted":"")+(e.back?" mlv-edge--back":""),"data-edge-id":e.id,"data-edge-kind":e.kind});s.setAttribute("data-edge-ids",e.ids.join(" ")),i&&(s.setAttribute("data-edge-weight",String(o)),s.style.setProperty("--mlv-edge-w",jt(o)+"px")),t.stage&&s.setAttribute("data-stage",t.stage),t.phase!==void 0&&se(s,t.phase),t.basis&&s.setAttribute("data-basis",t.basis),t.severity&&(s.setAttribute("data-sev",t.severity),s.classList.add("has-issue")),t.stale&&(s.classList.add("is-stale"),s.setAttribute("data-stale","1"));let r=R("path",{class:"mlv-edge__hit",d:e.d});r.setAttribute("tabindex","-1"),r.setAttribute("role","button");let l=t.staleElsewhere?" Its evidence cites a file in another folder.":" Its evidence cites a changed or missing file.";r.setAttribute("aria-label",jd(e,t.sourceLabel,t.targetLabel,t.basis)+(t.stale?l:"")),s.appendChild(r);let a=R("path",{class:"mlv-edge__path",d:e.d});a.setAttribute("marker-end","url(#"+ms+")"),a.setAttribute("id",Nd(t.mountSerial,e.id)),s.appendChild(a);let d=t.placement;if(e.label&&d&&d.hidden)s.setAttribute("data-label-hidden","1");else if(e.label){let u=d?d.x:e.mid.x,h=d?d.y:e.mid.y-8,p=R("text",{class:"mlv-edge-label",x:u,y:h});p.textContent=ho(e),d&&(p.setAttribute("data-label-axis",d.axis),d.flipped&&p.setAttribute("data-label-flipped","1"),d.laneId&&p.setAttribute("data-label-lane",d.laneId)),s.appendChild(p),t.labelVisible&&s.classList.add("has-label")}let c=go(t);if(t.severity){let u=Wi(t.severity,vs,t.findings||[]);u.setAttribute("transform","translate("+c.x+","+c.y+")"),s.appendChild(u)}else if(e.back){let u=R("path",{class:"mlv-edge__loopmark",d:"M4 -4 L0 0 L4 4",fill:"none",stroke:"var(--mlv-edge)","stroke-width":1.3,"stroke-linecap":"round","stroke-linejoin":"round",transform:"translate("+c.x+","+c.y+")"});s.appendChild(u)}return i&&s.appendChild(Dd(o,c,e.midAngle,!!t.severity||e.back)),t.stale&&s.appendChild(Bd(c,!!t.severity||e.back||i)),s}function Bd(t,e){let n=t.x+(e?qe+10:0),o=R("g",{class:"mlv-edge__stale",transform:"translate("+Yt(n)+","+Yt(t.y)+")"});o.setAttribute("data-stale","1"),o.appendChild(R("circle",{class:"mlv-edge__stalebg",r:7.5})),o.appendChild(R("path",{class:"mlv-edge__staleicon",d:"M0 -4.6 4.9 3.9H-4.9ZM0 -1.5v2.6M0 2.7v.1",fill:"none"}));let i=R("title");return i.textContent="This connection cites a file that changed or is missing since publishing. Its jumps are blocked.",o.appendChild(i),o}function Dd(t,e,n,o){let i=ht(t),s=zt(t),r=Gt(e,n,o),l=R("g",{class:"mlv-edge__weight",transform:"translate("+Yt(r.x)+","+Yt(r.y)+")"});l.setAttribute("data-weight",String(t)),l.appendChild(R("rect",{class:"mlv-edge__weightbg",x:-s/2,y:-xe/2,width:s,height:xe,rx:xe/2}));let a=R("text",{class:"mlv-edge__weighttext",x:0,y:0});a.textContent=i,l.appendChild(a);let d=R("title");return d.textContent=t+" connections merged into this edge.",l.appendChild(d),l}function Yt(t){return Math.round(t*100)/100}function jd(t,e,n,o){let i=t.back?"loop-back connection":t.kind&&t.kind!=="unknown"?t.kind+" connection":"connection",s=t.label?" labelled "+t.label:"",r=e&&n?", flows from "+e+" to "+n:"",l=t.count>1?", "+t.count+" merged connections":"";return i+s+r+l+He(o)+". Press Enter to open the cited source."}function He(t){return t==="inferred"?", inferred, not observed":t==="unresolved"?", unresolved":""}function ys(t,e,n,o,i){let s=R("g",{class:"mlv-connector-group","data-sev":o}),r=(t.x+e.x)/2,l=(t.y+e.y)/2,a="M "+t.x+" "+t.y+" Q "+r+" "+(l-28)+" "+e.x+" "+e.y;s.appendChild(R("path",{class:"mlv-connector",d:a}));let d=R("circle",{class:"mlv-connector__badge",cx:r,cy:l-14,r:8});s.appendChild(d);let c=R("text",{class:"mlv-connector__label",x:r,y:l-14});c.textContent=String(n),s.appendChild(c);let u=R("title");return u.textContent=i,s.appendChild(u),s}function Ue(t,e){let n=e.map(o=>t.get(o)).find(Boolean);return n?{file:n.file,absFile:"",line:n.line,col:0,endLine:n.endLine,endCol:0,snippet:n.quote,cell:n.cell,evidenceId:n.id}:{file:"",absFile:"",line:1,col:0,endLine:1,endCol:0}}function Gd(t){return"F"+(t+1)}var zd="unspecified model";function Zt(t){if(!t||t.workflowVersion!=="1.0")throw new Error("MLView.mountWorkflow: workflowVersion must be 1.0");let e=new Map((t.evidence||[]).map(u=>[u.id,u])),n=new Map,o=new Map;for(let u of t.findings||[])for(let h of u.nodeIds||[]){let p=n.get(h)||[];p.push(u.id),n.set(h,p)}for(let u of t.findings||[])for(let h of u.edgeIds||[]){let p=o.get(h)||[];p.push(u.id),o.set(h,p)}let i=new Map((t.phases||[]).map(u=>[u.id,u.label])),s=(t.nodes||[]).map(u=>({...typeof u.detail=="string"&&u.detail.trim()?{detail:u.detail}:{},...i.has(u.phase)?{phaseLabel:i.get(u.phase)}:{},id:u.id,kind:u.kind||"unknown",level:u.parent?"op":"unit",stage:u.phase,label:u.label,sublabel:u.detail||"",qualname:u.label,loc:Ue(e,u.evidence),parent:u.parent||null,basis:u.basis,evidenceLocs:u.evidence.map(h=>Ue(e,[h])).filter(h=>!!h.file),issueIds:n.get(u.id)||[]})),r=(t.edges||[]).map(u=>({id:u.id,kind:mo(u.kind),source:u.source,target:u.target,...u.kind&&mo(u.kind)!==u.kind.trim()?{authoredKind:u.kind}:{},label:u.label,loc:Ue(e,u.evidence),basis:u.basis,evidenceLocs:u.evidence.map(h=>Ue(e,[h])).filter(h=>!!h.file),issueIds:o.get(u.id)||[]})),l=(t.findings||[]).map((u,h)=>{let p=Ue(e,u.evidence),m=(u.evidence||[]).map(v=>({item:e.get(v),role:"Supporting evidence"})).concat((u.counterEvidence||[]).map(v=>({item:e.get(v),role:"Counter-evidence"}))).filter(v=>!!v.item).map(({item:v,role:y})=>({...Ue(e,[v.id]),role:y}));return{id:u.id,code:u.id,short:Gd(h),severity:u.severity,basis:u.basis,title:u.title,message:u.message,fixHint:u.suggestion||"",loc:p,relatedLocs:m,nodeIds:u.nodeIds||[],edgeIds:u.edgeIds||[],stage:s.find(v=>u.nodeIds.includes(v.id))?.stage||""}}),a=new Map,d=new Map;for(let u of s)a.set(u.stage,(a.get(u.stage)||0)+1);for(let u of l){let h=d.get(u.stage)||pe();h[u.severity]++,d.set(u.stage,h)}let c=(t.phases||[]).map((u,h)=>{let p=d.get(u.id)||pe();return{id:u.id,label:u.label,order:h,present:!0,nodeCount:a.get(u.id)||0,issueCounts:p,maxSeverity:["high","medium","low"].find(m=>p[m])||null}});return{schemaVersion:"workflow-view/1",generator:{name:t.producer.host,version:t.producer.model||zd,rendererSha:t.revision.id,generatedAt:t.verification?.publishedAt||""},workspace:{root:t.title,entrypoints:t.request.entrypoints||[],filesAnalyzed:t.coverage.inspectedFiles.length},stages:c,nodes:s,edges:r,issues:l,diagnostics:t.coverage.limitations.map(u=>({kind:"workflow_limitation",message:u})),stats:{nodes:s.length,edges:r.length,issues:l.reduce((u,h)=>(u[h.severity]++,u),pe()),durationMs:0},authoredCoverage:{status:t.coverage.status,limitations:t.coverage.limitations.length}}}var vo=[["explain","Explain"],["expand","Expand"],["challenge","Challenge"],["trace","Trace"],["custom","Custom…"]];function xs(t){let e=_s(t);if(!e)return;let n=ws(e.intent);if(!(!e.open&&n==="explain"&&!e.custom))return{open:e.open,intent:n,custom:e.custom}}var ws=t=>vo.find(([e])=>e===t)?.[0]??"explain";function Es(t){if(!t||typeof t!="object")return null;let e=t;return{open:e.open===!0,intent:ws(e.intent),custom:typeof e.custom=="string"?e.custom.slice(0,500):""}}function _s(t){let e=t.querySelector(".mlv-workflow"),n=t.querySelector(".mlv-workflow__composer"),o=t.querySelector(".mlv-workflow__intent"),i=t.querySelector(".mlv-workflow__custom"),s=t.querySelector(".mlv-workflow__refine");if(!e||!n||!o||!i)return null;let r=t.ownerDocument.activeElement,l=null;r&&r===s?l="refine":r&&n.contains(r)&&(l=r===o?"intent":r===i?"custom":"submit");let a=n.getAttribute("data-selection-kind"),d=n.getAttribute("data-selection-id");return{revision:e.getAttribute("data-revision"),open:!n.hidden,intent:o.value,custom:i.value,focus:l,selection:(a==="node"||a==="edge"||a==="issue")&&d?{kind:a,id:d}:void 0}}var Vd=new Set(["checking","missing","unreadable","parse","invalid"]);function Ss(t,e){if(!e||e.some(o=>Vd.has(o)))return;let n=t.root.querySelector(".mlv-workflow__status");!n||!n.textContent||(n.textContent="",t.saveSoon())}function Wd(t,e){let n=t.root.querySelector(".mlv-workflow__composer"),o=t.root.querySelector(".mlv-workflow__refine"),i=t.root.querySelector(".mlv-workflow__custom"),s=t.root.querySelector(".mlv-workflow__status");if(!(!n||!o||!i||!s)){if(e.outcome==="done"){n.hidden=!0,o.setAttribute("aria-expanded","false"),i.value="",s.textContent="";let r=_e.get(n)||null;_e.delete(n),at(r,o),t.saveSoon();return}n.hidden=!1,o.setAttribute("aria-expanded","true"),s.textContent=e.message||"The refinement prompt was not copied.",t.saveSoon()}}var _e=new WeakMap;function Kd(t,e){if(!e)return"Whole diagram";let n=t.index;if(e.kind==="node"){let i=n?n.nodeById.get(e.id):void 0;return"Step: "+(i?i.label||i.id:e.id)}if(e.kind==="edge"){let i=n?n.edgeById.get(e.id):void 0;if(!i)return"Connection: "+e.id;let s=n.nodeById.get(i.source),r=n.nodeById.get(i.target);return"Connection: "+(i.label||(s?s.label:i.source)+" → "+(r?r.label:i.target))}let o=n?n.issueById.get(e.id):void 0;return"Finding: "+(o?(o.short?o.short+" · ":"")+o.title:e.id)}function Ms(t,e,n){t.root.classList.add("mlv-root--workflow"),t.root.setAttribute("data-workflow-revision",e.revision.id);let o=t.root.querySelector(".mlv-workflow");if(!o){o=f("section","mlv-workflow");let b=t.chrome.header;t.root.insertBefore(o,b.nextSibling)}let i=_s(t.root),s=i??(n?{revision:e.revision.id,open:n.open,intent:n.intent,custom:n.custom,focus:null,selection:void 0}:null),r=!i&&!!n,l=o.querySelector(".mlv-workflow__composer"),a=l&&_e.get(l)||null;H(o),o.setAttribute("data-revision",e.revision.id),o.setAttribute("aria-label","Refinement");let d=f("button","mlv-btn mlv-btn--primary mlv-workflow__refine","Refine…");d.type="button",d.title="Copy a refinement prompt for your assistant about the selection or the whole diagram. MLView calls no model.",d.setAttribute("aria-expanded","false"),d.setAttribute("aria-haspopup","dialog");let c=t.chrome.refineSlot;H(c),c.appendChild(d),t.chrome.syncRoving();let u=g(o,f("form","mlv-workflow__composer"));u.hidden=!0,u.setAttribute("role","dialog"),u.setAttribute("aria-modal","true"),u.setAttribute("aria-label","Refine"),a&&_e.set(u,a);let h=g(u,f("span","mlv-workflow__selection")),p=g(u,f("select","mlv-input mlv-workflow__intent"));p.setAttribute("aria-label","Refinement intent");for(let[b,M]of vo){let T=p.ownerDocument.createElement("option");T.value=b,T.textContent=M,p.appendChild(T)}let m=g(u,f("input","mlv-input mlv-workflow__custom"));m.type="text",m.maxLength=500,m.placeholder="What should the assistant refine?",m.setAttribute("aria-label","Custom refinement intent"),m.hidden=!0;let v=g(u,f("button","mlv-btn","Copy prompt"));v.type="submit";let y=g(u,f("span","mlv-workflow__status"));y.setAttribute("role","status"),g(u,f("p","mlv-workflow__hint","Copies a prompt for your assistant to paste. MLView calls no model."));let S=()=>t.selection?{kind:t.selection.kind,id:t.selection.id}:void 0,x,w=b=>{x=b,h.textContent=Kd(t,b),b?(h.title=b.kind+" id: "+b.id,u.setAttribute("data-selection-kind",b.kind),u.setAttribute("data-selection-id",b.id)):(h.removeAttribute("title"),u.removeAttribute("data-selection-kind"),u.removeAttribute("data-selection-id"))},E=()=>w(S());_(d,"click",()=>{if(u.hidden){let b=d.ownerDocument.activeElement;b&&!u.contains(b)&&b!==d.ownerDocument.body?_e.set(u,b):_e.delete(u)}u.hidden=!u.hidden,d.setAttribute("aria-expanded",u.hidden?"false":"true"),u.hidden||(E(),p.focus()),t.saveSoon()}),_(u,"keydown",b=>{if(Bt(u,b)){b.stopPropagation();return}b.key==="Escape"&&(b.preventDefault(),b.stopPropagation(),yo(t,!0))}),_(p,"change",()=>{m.hidden=p.value!=="custom",m.hidden||m.focus(),t.saveSoon()}),_(m,"input",()=>t.saveSoon()),_(u,"submit",b=>{b.preventDefault();let M=p.value,T=m.value.trim();if(M==="custom"&&!T){m.focus();return}y.textContent="";let L={v:1,type:"refineWorkflow",revisionId:e.revision.id,intent:M};M==="custom"&&(L.customText=T),x&&(L.selection=x),t.postRequest(L,C=>Wd(t,C))}),s&&(vo.some(([b])=>b===s.intent)&&(p.value=s.intent),m.value=s.custom,m.hidden=p.value!=="custom",s.open&&(u.hidden=!1,d.setAttribute("aria-expanded","true"),s.revision===e.revision.id&&!r?w(s.selection):E()),s.focus==="refine"?d.focus():s.focus==="intent"&&!u.hidden?p.focus():s.focus==="custom"&&!u.hidden&&!m.hidden?m.focus():s.focus==="submit"&&!u.hidden&&v.focus())}function bo(t){let e=t.root.querySelector(".mlv-workflow__composer");return!!e&&!e.hidden}function yo(t,e=!1){let n=t.root.querySelector(".mlv-workflow__composer"),o=t.root.querySelector(".mlv-workflow__refine");if(!n||n.hidden)return!1;n.hidden=!0;let i=_e.get(n)||null;return _e.delete(n),o&&o.setAttribute("aria-expanded","false"),e&&at(i,o),t.saveSoon(),!0}var Qt=class{constructor(e){this.armed=null;this.consumed=!1;this.disposers=[];this.disposers.push(_(e,"mousedown",n=>{n.detail>=2&&(this.armed||this.consumed)&&n.preventDefault()},!0),_(e,"click",n=>this.onClick(n),!0),_(e,"dblclick",n=>{this.consumed&&(n.preventDefault(),n.stopPropagation())},!0))}arm(e,n){!e||e.detail!==1||(this.armed=n,this.consumed=!1)}dispose(){for(let e of this.disposers)e();this.disposers=[],this.armed=null}onClick(e){if(e.detail>=2&&(this.armed||this.consumed)){e.preventDefault(),e.stopPropagation();let n=this.armed;this.armed=null,this.consumed=!0,n&&n();return}this.armed=null,this.consumed=!1}};var Ks=Object.defineProperty,qd=(t,e,n)=>e in t?Ks(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Ud=(t,e)=>{for(var n in e)Ks(t,n,{get:e[n],enumerable:!0})},Xd=(t,e,n)=>qd(t,typeof e!="symbol"?e+"":e,n),qs={};Ud(qs,{Graph:()=>ae,alg:()=>Ao});var Yd=Object.defineProperty,Us=(t,e)=>{for(var n in e)Yd(t,n,{get:e[n],enumerable:!0})},Lo=class{constructor(t){this._isDirected=!0,this._isMultigraph=!1,this._isCompound=!1,this._nodes={},this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={},this._nodeCount=0,this._edgeCount=0,this._defaultNodeLabelFn=()=>{},this._defaultEdgeLabelFn=()=>{},t&&(this._isDirected="directed"in t?t.directed:!0,this._isMultigraph="multigraph"in t?t.multigraph:!1,this._isCompound="compound"in t?t.compound:!1),this._isCompound&&(this._parent={},this._children={},this._children["\0"]={})}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(t){return this._label=t,this}graph(){return this._label}setDefaultNodeLabel(t){return typeof t!="function"?this._defaultNodeLabelFn=()=>t:this._defaultNodeLabelFn=t,this}nodeCount(){return this._nodeCount}nodes(){return Object.keys(this._nodes)}sources(){return this.nodes().filter(t=>Object.keys(this._in[t]).length===0)}sinks(){return this.nodes().filter(t=>Object.keys(this._out[t]).length===0)}setNodes(t,e){return t.forEach(n=>{e!==void 0?this.setNode(n,e):this.setNode(n)}),this}setNode(t,e){return t in this._nodes?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]="\0",this._children[t]={},this._children["\0"][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)}node(t){return this._nodes[t]}hasNode(t){return t in this._nodes}removeNode(t){if(t in this._nodes){let e=n=>this.removeEdge(this._edgeObjs[n]);delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],this.children(t).forEach(n=>{this.setParent(n)}),delete this._children[t]),Object.keys(this._in[t]).forEach(e),delete this._in[t],delete this._preds[t],Object.keys(this._out[t]).forEach(e),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this}setParent(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(e===void 0)e="\0";else{e+="";for(let n=e;n!==void 0;n=this.parent(n))if(n===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this}parent(t){if(this._isCompound){let e=this._parent[t];if(e!=="\0")return e}}children(t="\0"){if(this._isCompound){let e=this._children[t];if(e)return Object.keys(e)}else{if(t==="\0")return this.nodes();if(this.hasNode(t))return[]}return[]}predecessors(t){let e=this._preds[t];if(e)return Object.keys(e)}successors(t){let e=this._sucs[t];if(e)return Object.keys(e)}neighbors(t){let e=this.predecessors(t);if(e){let n=new Set(e),o=this.successors(t);if(o)for(let i of o)n.add(i);return Array.from(n.values())}}isLeaf(t){var e;let n;return this.isDirected()?n=this.successors(t):n=this.neighbors(t),((e=n?.length)!=null?e:0)===0}filterNodes(t){let e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph()),Object.entries(this._nodes).forEach(([i,s])=>{t(i)&&e.setNode(i,s)}),Object.values(this._edgeObjs).forEach(i=>{e.hasNode(i.v)&&e.hasNode(i.w)&&e.setEdge(i,this.edge(i))});let n={},o=i=>{let s=this.parent(i);return!s||e.hasNode(s)?(n[i]=s,s):s in n?n[s]:o(s)};return this._isCompound&&e.nodes().forEach(i=>e.setParent(i,o(i))),e}setDefaultEdgeLabel(t){return typeof t!="function"?this._defaultEdgeLabelFn=()=>t:this._defaultEdgeLabelFn=t,this}edgeCount(){return this._edgeCount}edges(){return Object.values(this._edgeObjs)}setPath(t,e){return t.reduce((n,o)=>(e!==void 0?this.setEdge(n,o,e):this.setEdge(n,o),o)),this}setEdge(t,e,n,o){let i,s,r,l,a=!1;typeof t=="object"&&t!==null&&"v"in t?(i=t.v,s=t.w,r=t.name,arguments.length===2&&(l=e,a=!0)):(i=t,s=e,r=o,arguments.length>2&&(l=n,a=!0)),i=""+i,s=""+s,r!==void 0&&(r=""+r);let d=mt(this._isDirected,i,s,r);if(d in this._edgeLabels)return a&&(this._edgeLabels[d]=l),this;if(r!==void 0&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(i),this.setNode(s),this._edgeLabels[d]=a?l:this._defaultEdgeLabelFn(i,s,r);let c=Zd(this._isDirected,i,s,r);return i=c.v,s=c.w,Object.freeze(c),this._edgeObjs[d]=c,ks(this._preds[s],i),ks(this._sucs[i],s),this._in[s][d]=c,this._out[i][d]=c,this._edgeCount++,this}edge(t,e,n){let o=arguments.length===1?xo(this._isDirected,t):mt(this._isDirected,t,e,n);return this._edgeLabels[o]}edgeAsObj(t,e,n){let o=arguments.length===1?this.edge(t):this.edge(t,e,n);return typeof o!="object"||o===null?{label:o}:o}hasEdge(t,e,n){return(arguments.length===1?xo(this._isDirected,t):mt(this._isDirected,t,e,n))in this._edgeLabels}removeEdge(t,e,n){let o=arguments.length===1?xo(this._isDirected,t):mt(this._isDirected,t,e,n),i=this._edgeObjs[o];if(i){let s=i.v,r=i.w;delete this._edgeLabels[o],delete this._edgeObjs[o],Ls(this._preds[r],s),Ls(this._sucs[s],r),delete this._in[r][o],delete this._out[s][o],this._edgeCount--}return this}inEdges(t,e){return this.isDirected()?this.filterEdges(this._in[t],t,e):this.nodeEdges(t,e)}outEdges(t,e){return this.isDirected()?this.filterEdges(this._out[t],t,e):this.nodeEdges(t,e)}nodeEdges(t,e){if(t in this._nodes)return this.filterEdges({...this._in[t],...this._out[t]},t,e)}_removeFromParentsChildList(t){delete this._children[this._parent[t]][t]}filterEdges(t,e,n){if(!t)return;let o=Object.values(t);return n?o.filter(i=>i.v===e&&i.w===n||i.v===n&&i.w===e):o}};function ks(t,e){t[e]?t[e]++:t[e]=1}function Ls(t,e){t[e]!==void 0&&!--t[e]&&delete t[e]}function mt(t,e,n,o){let i=""+e,s=""+n;if(!t&&i>s){let r=i;i=s,s=r}return i+""+s+""+(o===void 0?"\0":o)}function Zd(t,e,n,o){let i=""+e,s=""+n;if(!t&&i>s){let l=i;i=s,s=l}let r={v:i,w:s};return o&&(r.name=o),r}function xo(t,e){return mt(t,e.v,e.w,e.name)}var Qd={};Us(Qd,{read:()=>tc,write:()=>$d});function $d(t){let e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:Jd(t),edges:ec(t)},n=t.graph();return n!==void 0&&(e.value=structuredClone(n)),e}function Jd(t){return t.nodes().map(e=>{let n=t.node(e),o=t.parent(e),i={v:e};return n!==void 0&&(i.value=n),o!==void 0&&(i.parent=o),i})}function ec(t){return t.edges().map(e=>{let n=t.edge(e),o={v:e.v,w:e.w};return e.name!==void 0&&(o.name=e.name),n!==void 0&&(o.value=n),o})}function tc(t){let e=new Lo(t.options);return t.value!==void 0&&e.setGraph(t.value),t.nodes.forEach(n=>{e.setNode(n.v,n.value),n.parent&&e.setParent(n.v,n.parent)}),t.edges.forEach(n=>{e.setEdge({v:n.v,w:n.w,name:n.name},n.value)}),e}var Ao={};Us(Ao,{CycleException:()=>Jt,bellmanFord:()=>Xs,components:()=>ic,dijkstra:()=>$t,dijkstraAll:()=>lc,findCycles:()=>ac,floydWarshall:()=>cc,isAcyclic:()=>hc,postorder:()=>pc,preorder:()=>mc,prim:()=>gc,shortestPaths:()=>vc,tarjan:()=>Zs,topsort:()=>Qs});var nc=()=>1;function Xs(t,e,n,o){return oc(t,String(e),n||nc,o||function(i){var s;return(s=t.outEdges(i))!=null?s:[]})}function oc(t,e,n,o){let i={},s,r=0,l=t.nodes(),a=function(u){let h=i[u.v],p=i[u.w];if(!h||!p)return;let m=n(u);h.distance+mt.key)}has(t){return t in this._keyIndices}priority(t){let e=this._keyIndices[t];if(e!==void 0)return this._arr[e].priority}min(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key}add(t,e){let n=this._keyIndices,o=String(t);if(!(o in n)){let i=this._arr,s=i.length;return n[o]=s,i.push({key:o,priority:e}),this._decrease(s),!0}return!1}removeMin(){if(this.size()===0)throw new Error("Queue underflow");this._swap(0,this._arr.length-1);let t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key}decrease(t,e){let n=this._keyIndices[t];if(n===void 0)throw new Error(`Key not found: ${t}`);let o=this._arr[n].priority;if(e>o)throw new Error(`New priority is greater than current priority. Key: ${t} Old: ${o} New: ${e}`);this._arr[n].priority=e,this._decrease(n)}_heapify(t){let e=this._arr,n=2*t,o=n+1,i=t;n>1,!(e[o].priority1;function $t(t,e,n,o){let i=function(s){var r;return(r=t.outEdges(s))!=null?r:[]};return rc(t,String(e),n||sc,o||i)}function rc(t,e,n,o){let i={},s=new Ys,r,l,a=function(d){let c=d.v!==r?d.v:d.w,u=i[c];if(!u)return;let h=n(d),p=l.distance+h;if(h<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+d+" Weight: "+h);p0;){r=s.removeMin();let d=i[r];if(!d||d.distance===Number.POSITIVE_INFINITY)break;l=d,o(r).forEach(a)}return i}function lc(t,e,n){return t.nodes().reduce(function(o,i){return o[i]=$t(t,i,e,n),o},{})}function Zs(t){let e=0,n=[],o={},i=[];function s(r){var l;let a=o[r]={onStack:!0,lowlink:e,index:e++};if(n.push(r),(l=t.successors(r))==null||l.forEach(function(d){if(d in o){let c=o[d];c!=null&&c.onStack&&(a.lowlink=Math.min(a.lowlink,c.index))}else{s(d);let c=o[d];c&&(a.lowlink=Math.min(a.lowlink,c.lowlink))}}),a.lowlink===a.index){let d=[],c;do{c=n.pop();let u=o[c];u&&(u.onStack=!1),d.push(c)}while(r!==c);i.push(d)}}return t.nodes().forEach(function(r){r in o||s(r)}),i}function ac(t){return Zs(t).filter(function(e){var n;let o=e[0];return o?e.length>1||e.length===1&&((n=t.outEdges(o,o))!=null?n:[]).length>0:!1})}var dc=()=>1;function cc(t,e,n){return uc(t,e||dc,n||function(o){var i;return(i=t.outEdges(o))!=null?i:[]})}function uc(t,e,n){let o={},i=t.nodes();return i.forEach(function(s){let r={};o[s]=r,r[s]={distance:0,predecessor:""},i.forEach(function(l){s!==l&&(r[l]={distance:Number.POSITIVE_INFINITY,predecessor:""})}),n(s).forEach(function(l){let a=l.v===s?l.w:l.v,d=e(l);r[a]={distance:d,predecessor:s}})}),i.forEach(function(s){let r=o[s];r&&i.forEach(function(l){let a=o[l];a&&i.forEach(function(d){let c=a[s],u=r[d],h=a[d];if(c&&u&&h){let p=c.distance+u.distance;p{var a;return(a=t.isDirected()?t.successors(l):t.neighbors(l))!=null?a:[]}),r={};return e.forEach(function(l){if(!t.hasNode(l))throw new Error("Graph does not have node: "+l);i=$s(t,l,n==="post",r,s,o,i)}),i}function $s(t,e,n,o,i,s,r){return e in o||(o[e]=!0,n||(r=s(r,e)),i(e).forEach(function(l){r=$s(t,l,n,o,i,s,r)}),n&&(r=s(r,e))),r}function Js(t,e,n){return fc(t,e,n,function(o,i){return o.push(i),o},[])}function pc(t,e){return Js(t,e,"post")}function mc(t,e){return Js(t,e,"pre")}function gc(t,e){var n;let o=new Lo,i={},s=new Ys,r;function l(c){let u=c.v===r?c.w:c.v,h=s.priority(u);if(h!==void 0){let p=e(c);p0;){if(r=s.removeMin(),r in i)o.setEdge(r,i[r]);else{if(d)throw new Error("Input graph is not connected: "+t);d=!0}(n=t.nodeEdges(r))==null||n.forEach(l)}return o}function vc(t,e,n,o){return bc(t,e,n,o??(i=>{var s;return(s=t.outEdges(i))!=null?s:[]}))}function bc(t,e,n,o){if(n===void 0)return $t(t,e,n,o);let i=!1,s=t.nodes();for(let r=0;re.setNode(n,t.node(n))),t.edges().forEach(n=>{let o=e.edge(n.v,n.w)||{weight:0,minlen:1},i=t.edge(n);e.setEdge(n.v,n.w,{weight:o.weight+i.weight,minlen:Math.max(o.minlen,i.minlen)})}),e}function er(t){let e=new ae({multigraph:t.isMultigraph()}).setGraph(t.graph());return t.nodes().forEach(n=>{t.children(n).length||e.setNode(n,t.node(n))}),t.edges().forEach(n=>{e.setEdge(n,t.edge(n))}),e}function As(t,e){let n=t.x,o=t.y,i=e.x-n,s=e.y-o,r=t.width/2,l=t.height/2;if(!i&&!s)throw new Error("Not possible to find intersection inside of the rectangle");let a,d;return Math.abs(s)*r>Math.abs(i)*l?(s<0&&(l=-l),a=l*i/s,d=l):(i<0&&(r=-r),a=r,d=r*s/i),{x:n+a,y:o+d}}function vt(t){let e=gt(nr(t)+1).map(()=>[]);return t.nodes().forEach(n=>{let o=t.node(n),i=o.rank;i!==void 0&&(e[i]||(e[i]=[]),e[i][o.order]=n)}),e}function xc(t){let e=t.nodes().map(o=>{let i=t.node(o).rank;return i===void 0?Number.MAX_VALUE:i}),n=ce(Math.min,e);t.nodes().forEach(o=>{let i=t.node(o);Object.hasOwn(i,"rank")&&(i.rank-=n)})}function wc(t){let e=t.nodes().map(r=>t.node(r).rank).filter(r=>r!==void 0),n=ce(Math.min,e),o=[];t.nodes().forEach(r=>{let l=t.node(r).rank-n;o[l]||(o[l]=[]),o[l].push(r)});let i=0,s=t.graph().nodeRankFactor;Array.from(o).forEach((r,l)=>{r===void 0&&l%s!==0?--i:r!==void 0&&i&&r.forEach(a=>t.node(a).rank+=i)})}function Is(t,e,n,o){let i={width:0,height:0};return arguments.length>=4&&(i.rank=n,i.order=o),Ye(t,"border",i,e)}function Ec(t,e=tr){let n=[];for(let o=0;otr){let n=Ec(e);return t(...n.map(o=>t(...o)))}else return t(...e)}function nr(t){let e=t.nodes().map(n=>{let o=t.node(n).rank;return o===void 0?Number.MIN_VALUE:o});return ce(Math.max,e)}function _c(t,e){let n={lhs:[],rhs:[]};return t.forEach(o=>{e(o)?n.lhs.push(o):n.rhs.push(o)}),n}function Sc(t,e){let n=Date.now();try{return e()}finally{console.log(t+" time: "+(Date.now()-n)+"ms")}}function or(t,e){return e()}var Mc=0;function Io(t){let e=++Mc;return t+(""+e)}function gt(t,e,n=1){e==null&&(e=t,t=0);let o=s=>seo[e]:n=e,Object.entries(t).reduce((o,[i,s])=>(o[i]=n(s,i),o),{})}function kc(t,e){return t.reduce((n,o,i)=>(n[o]=e[i],n),{})}var nn="\0";function ir(t,e,n){var o,i,s,r,l,a;if(!(t&&e&&n&&e.dummy==="edge"&&n.dummy==="edge"&&e.edgeObj&&n.edgeObj&&t[e.edgeObj.v]&&t[n.edgeObj.v]&&t[e.edgeObj.w]&&t[n.edgeObj.w]))return 0;let d=!0;e.edgeObj.w===n.edgeObj.w&&(d=!1);let c=d?(i=(o=t[e.edgeObj.v])==null?void 0:o.rank)!=null?i:NaN+1:(r=(s=t[e.edgeObj.w])==null?void 0:s.rank)!=null?r:NaN-1,u=Object.entries(t).find(v=>{var y,S;return((y=v[1].edgeObj)==null?void 0:y.v)===e.edgeObj.v&&((S=v[1].edgeObj)==null?void 0:S.w)===e.edgeObj.w&&v[1].rank===c}),h=Object.entries(t).find(v=>{var y,S;return((y=v[1].edgeObj)==null?void 0:y.v)===n.edgeObj.v&&((S=v[1].edgeObj)==null?void 0:S.w)===n.edgeObj.w&&v[1].rank===c});if(!u||!h)return 0;let p=(l=u[1].order)!=null?l:NaN,m=(a=h[1].order)!=null?a:NaN;return isNaN(p-m)?0:p-m}var Lc="3.1.1",Ac=class{constructor(){Xd(this,"_sentinel");let t={};t._next=t._prev=t,this._sentinel=t}dequeue(){let t=this._sentinel,e=t._prev;if(e!==t)return Cs(e),e}enqueue(t){let e=this._sentinel;t._prev&&t._next&&Cs(t),t._next=e._next,e._next._prev=t,e._next=t,t._prev=e}toString(){let t=[],e=this._sentinel,n=e._prev;for(;n!==e;)t.push(JSON.stringify(n,Ic)),n=n._prev;return"["+t.join(", ")+"]"}};function Cs(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function Ic(t,e){if(t!=="_next"&&t!=="_prev")return e}var Cc=Ac,Tc=()=>1;function Rc(t,e){if(t.nodeCount()<=1)return[];let n=Hc(t,e||Tc);return Oc(n.graph,n.buckets,n.zeroIdx).flatMap(o=>t.outEdges(o.v,o.w)||[])}function Oc(t,e,n){var o;let i=[],s=e[e.length-1],r=e[0],l;for(;t.nodeCount();){for(;l=r.dequeue();)wo(t,e,n,l);for(;l=s.dequeue();)wo(t,e,n,l);if(t.nodeCount()){for(let a=e.length-2;a>0;--a)if(l=(o=e[a])==null?void 0:o.dequeue(),l){i=i.concat(wo(t,e,n,l,!0)||[]);break}}}return i}function wo(t,e,n,o,i){let s=[],r=i?s:void 0;return(t.inEdges(o.v)||[]).forEach(l=>{let a=t.edge(l),d=t.node(l.v);i&&s.push({v:l.v,w:l.w}),d.out-=a,ko(e,n,d)}),(t.outEdges(o.v)||[]).forEach(l=>{let a=t.edge(l),d=l.w,c=t.node(d);c.in-=a,ko(e,n,c)}),t.removeNode(o.v),r}function Hc(t,e){let n=new ae,o=0,i=0;t.nodes().forEach(l=>{n.setNode(l,{v:l,in:0,out:0})}),t.edges().forEach(l=>{let a=n.edge(l.v,l.w)||0,d=e(l),c=a+d;n.setEdge(l.v,l.w,c);let u=n.node(l.v),h=n.node(l.w);i=Math.max(i,u.out+=d),o=Math.max(o,h.in+=d)});let s=Nc(i+o+3).map(()=>new Cc),r=o+1;return n.nodes().forEach(l=>{ko(s,r,n.node(l))}),{graph:n,buckets:s,zeroIdx:r}}function ko(t,e,n){var o,i,s;n.out?n.in?(s=t[n.out-n.in+e])==null||s.enqueue(n):(i=t[t.length-1])==null||i.enqueue(n):(o=t[0])==null||o.enqueue(n)}function Nc(t){let e=[];for(let n=0;n{let i=t.edge(o);t.removeEdge(o),i.forwardName=o.name,i.reversed=!0,t.setEdge(o.w,o.v,i,Io("rev"))});function n(o){return i=>o.edge(i).weight}}function Fc(t,e){let n=[],o={},i={};function s(a){Object.hasOwn(i,a)||(i[a]=!0,o[a]=!0,t.outEdges(a).forEach(d=>{Object.hasOwn(o,d.w)?n.push(d):s(d.w)}),delete o[a])}function r(a){var d;Object.hasOwn(i,a)||(i[a]=!0,o[a]=!0,(d=t.outEdges(a))==null||d.forEach(c=>{var u,h;Object.hasOwn(o,c.w)||((u=e.node(a))==null?void 0:u.rank)>((h=e.node(c.w))==null?void 0:h.rank)&&Dc(t,c.w,c)?n.push(c):r(c.w)}),delete o[a])}let l=s;return e&&typeof e.node=="function"&&(l=r),t.sources().forEach(l),t.nodes().forEach(l),n}function Bc(t){t.edges().forEach(e=>{let n=t.edge(e);if(n.reversed){t.removeEdge(e);let o=n.forwardName;delete n.reversed,delete n.forwardName,t.setEdge(e.w,e.v,n,o)}})}function Dc(t,e,n){let o=new Set;function i(s){var r;if(t.sources().includes(s))return!0;o.add(s);for(let l of(r=t.inEdges(s))!=null?r:[])if(!(l.v===n.v&&l.w===n.w)&&!o.has(l.v)&&i(l.v))return!0;return!1}return i(e)}function jc(t){t.graph().dummyChains=[],t.edges().forEach(e=>Gc(t,e))}function Gc(t,e){let n=e.v,o=t.node(n).rank,i=e.w,s=t.node(i).rank,r=e.name,l=t.edge(e),a=l.labelRank;if(s===o+1)return;t.removeEdge(e);let d,c,u;for(u=0,++o;o{let n=t.node(e),o=n.edgeLabel,i;for(t.setEdge(n.edgeObj,o);n.dummy;)i=t.successors(e)[0],t.removeNode(e),o.points.push({x:n.x,y:n.y}),n.dummy==="edge-label"&&(o.x=n.x,o.y=n.y,o.width=n.width,o.height=n.height),e=i,n=t.node(e)})}function Co(t){let e={};function n(o){let i=t.node(o);if(Object.hasOwn(e,o))return i.rank;e[o]=!0;let s=t.outEdges(o),r=s?s.map(a=>a==null?Number.POSITIVE_INFINITY:n(a.w)-t.edge(a).minlen):[],l=ce(Math.min,r);return l===Number.POSITIVE_INFINITY&&(l=0),i.rank=l}t.sources().forEach(n)}function Xe(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}var sr=Vc;function Vc(t){let e=new ae({directed:!1}),n=t.nodes();if(n.length===0)throw new Error("Graph must have at least one node");let o=n[0],i=t.nodeCount();e.setNode(o,{});let s,r;for(;Wc(e,t){let r=s.v,l=o===r?s.w:r;!t.hasNode(l)&&!Xe(e,s)&&(t.setNode(l,{}),t.setEdge(o,l,{}),n(l))})}return t.nodes().forEach(n),t.nodeCount()}function Kc(t,e){return e.edges().reduce((n,o)=>{let i=Number.POSITIVE_INFINITY;return t.hasNode(o.v)!==t.hasNode(o.w)&&(i=Xe(e,o)),ie.node(o).rank+=n)}var{preorder:Uc,postorder:Xc}=Ao,Yc=Ne;Ne.initLowLimValues=Ro;Ne.initCutValues=To;Ne.calcCutValue=rr;Ne.leaveEdge=ar;Ne.enterEdge=dr;Ne.exchangeEdges=cr;function Ne(t){t=yc(t),Co(t);let e=sr(t);Ro(e),To(e,t);let n,o;for(;n=ar(e);)o=dr(e,t,n),cr(e,t,n,o)}function To(t,e){let n=Xc(t,t.nodes());n=n.slice(0,n.length-1),n.forEach(o=>Zc(t,e,o))}function Zc(t,e,n){let o=t.node(n).parent,i=t.edge(n,o);i.cutvalue=rr(t,e,n)}function rr(t,e,n){let o=t.node(n).parent,i=!0,s=e.edge(n,o),r=0;s||(i=!1,s=e.edge(o,n)),r=s.weight;let l=e.nodeEdges(n);return l&&l.forEach(a=>{let d=a.v===n,c=d?a.w:a.v;if(c!==o){let u=d===i,h=e.edge(a).weight;if(r+=u?h:-h,$c(t,n,c)){let p=t.edge(n,c).cutvalue;r+=u?-p:p}}}),r}function Ro(t,e){arguments.length<2&&(e=t.nodes()[0]),lr(t,{},1,e)}function lr(t,e,n,o,i){let s=n,r=t.node(o);e[o]=!0;let l=t.neighbors(o);return l&&l.forEach(a=>{Object.hasOwn(e,a)||(n=lr(t,e,n,a,o))}),r.low=s,r.lim=n++,i?r.parent=i:delete r.parent,n}function ar(t){return t.edges().find(e=>t.edge(e).cutvalue<0)}function dr(t,e,n){let o=n.v,i=n.w;e.hasEdge(o,i)||(o=n.w,i=n.v);let s=t.node(o),r=t.node(i),l=s,a=!1;return s.lim>r.lim&&(l=r,a=!0),e.edges().filter(d=>a===Ts(t,t.node(d.v),l)&&a!==Ts(t,t.node(d.w),l)).reduce((d,c)=>Xe(e,c)!t.node(i).parent);if(!n)return;let o=Uc(t,[n]);o=o.slice(1),o.forEach(i=>{let s=t.node(i).parent,r=e.edge(i,s),l=!1;r||(r=e.edge(s,i),l=!0),e.node(i).rank=e.node(s).rank+(l?r.minlen:-r.minlen)})}function $c(t,e,n){return t.hasEdge(e,n)}function Ts(t,e,n){return n.low<=e.lim&&e.lim<=n.lim}var Jc=eu;function eu(t){let e=t.graph().ranker;if(typeof e=="function")return e(t);switch(e){case"network-simplex":Rs(t);break;case"tight-tree":nu(t);break;case"longest-path":tu(t);break;case"none":break;default:Rs(t)}}var tu=Co;function nu(t){Co(t),sr(t)}function Rs(t){Yc(t)}var ou=iu;function iu(t){let e=ru(t),n=t.graph();Array.isArray(n.dummyChains)&&n.dummyChains.forEach(o=>{let i=t.node(o),s=i.edgeObj,r=su(t,e,s.v,s.w),l=r.path,a=r.lca,d=0,c=l[d],u=!0;for(;o!==s.w;){if(i=t.node(o),u){for(;(c=l[d])!==a&&t.node(c).maxRankr||l>e[a].lim));let d=a,c=o;for(;(c=t.parent(c))!==d;)s.push(c);return{path:i.concat(s.reverse()),lca:d}}function ru(t){let e={},n=0;function o(i){let s=n;t.children(i).forEach(o),e[i]={low:s,lim:n++}}return t.children(nn).forEach(o),e}function lu(t){let e=Ye(t,"root",{},"_root"),n=au(t),o=Object.values(n),i=ce(Math.max,o)-1,s=2*i+1;t.graph().nestingRoot=e,t.edges().forEach(l=>t.edge(l).minlen*=s);let r=du(t)+1;t.children(nn).forEach(l=>{ur(t,e,s,r,i,n,l)}),t.graph().nodeRankFactor=s}function ur(t,e,n,o,i,s,r){var l;let a=t.children(r);if(!a.length){r!==e&&t.setEdge(e,r,{weight:0,minlen:n});return}let d=Is(t,"_bt"),c=Is(t,"_bb"),u=t.node(r);t.setParent(d,r),u.borderTop=d,t.setParent(c,r),u.borderBottom=c,a.forEach(h=>{var p;ur(t,e,n,o,i,s,h);let m=t.node(h),v=m.borderTop?m.borderTop:h,y=m.borderBottom?m.borderBottom:h,S=m.borderTop?o:2*o,x=v!==y?1:i-((p=s[r])!=null?p:0)+1;t.setEdge(d,v,{weight:S,minlen:x,nestingEdge:!0}),t.setEdge(y,c,{weight:S,minlen:x,nestingEdge:!0})}),t.parent(r)||t.setEdge(e,d,{weight:0,minlen:i+((l=s[r])!=null?l:0)})}function au(t){let e={};function n(o,i){let s=t.children(o);s&&s.length&&s.forEach(r=>n(r,i+1)),e[o]=i}return t.children(nn).forEach(o=>n(o,1)),e}function du(t){return t.edges().reduce((e,n)=>e+t.edge(n).weight,0)}function cu(t){let e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,t.edges().forEach(n=>{t.edge(n).nestingEdge&&t.removeEdge(n)})}var uu=hu;function hu(t){function e(n){let o=t.children(n),i=t.node(n);if(o.length&&o.forEach(e),i&&Object.hasOwn(i,"minRank")){i.borderLeft=[],i.borderRight=[];for(let s=i.minRank,r=i.maxRank+1;sHs(t.node(e))),t.edges().forEach(e=>Hs(t.edge(e)))}function Hs(t){let e=t.width;t.width=t.height,t.height=e}function mu(t){t.nodes().forEach(e=>Eo(t.node(e))),t.edges().forEach(e=>{var n;let o=t.edge(e);(n=o.points)==null||n.forEach(Eo),Object.hasOwn(o,"y")&&Eo(o)})}function Eo(t){t.y=-t.y}function gu(t){t.nodes().forEach(e=>_o(t.node(e))),t.edges().forEach(e=>{var n;let o=t.edge(e);(n=o.points)==null||n.forEach(_o),Object.hasOwn(o,"x")&&_o(o)})}function _o(t){let e=t.x;t.x=t.y,t.y=e}function vu(t,e=null){let n={},o=t.nodes().filter(d=>!t.children(d).length),i=o.map(d=>t.node(d).rank),s=ce(Math.max,i),r=gt(s+1).map(()=>[]);function l(d){if(n[d])return;n[d]=!0;let c=t.node(d);r[c.rank].push(d);let u=t.successors(d);u&&[...u].sort((h,p)=>a(h,p)).forEach(l)}o.sort((d,c)=>t.node(d).rank-t.node(c).rank).forEach(l);function a(d,c){let u=t.node(d),h=t.node(c);return ir(e,u,h)}return r}function bu(t,e){let n=0;for(let o=1;oc)),i=e.flatMap(d=>{let c=t.outEdges(d);return c?c.map(u=>({pos:o[u.w],weight:t.edge(u).weight})).sort((u,h)=>u.pos-h.pos):[]}),s=1;for(;s{let c=d.pos+s;l[c]+=d.weight;let u=0;for(;c>0;)c%2&&(u+=l[c+1]),c=c-1>>1,l[c]+=d.weight;a+=d.weight*u}),a}function xu(t,e=[]){return e.map(n=>{let o=t.inEdges(n);if(!o||!o.length)return{v:n};{let i=o.reduce((s,r)=>{let l=t.edge(r),a=t.node(r.v);return{sum:s.sum+l.weight*a.order,weight:s.weight+l.weight}},{sum:0,weight:0});return{v:n,barycenter:i.sum/i.weight,weight:i.weight}}})}function wu(t,e){let n={};t.forEach((i,s)=>{let r={indegree:0,in:[],out:[],vs:[i.v],i:s};i.barycenter!==void 0&&(r.barycenter=i.barycenter,r.weight=i.weight),n[i.v]=r}),e.edges().forEach(i=>{let s=n[i.v],r=n[i.w];s!==void 0&&r!==void 0&&(r.indegree++,s.out.push(r))});let o=Object.values(n).filter(i=>!i.indegree);return Eu(o)}function Eu(t){let e=[];function n(i){return s=>{s.merged||(s.barycenter===void 0||i.barycenter===void 0||s.barycenter>=i.barycenter)&&_u(i,s)}}function o(i){return s=>{s.in.push(i),--s.indegree===0&&t.push(s)}}for(;t.length;){let i=t.pop();e.push(i),i.in.reverse().forEach(n(i)),i.out.forEach(o(i))}return e.filter(i=>!i.merged).map(i=>en(i,["vs","i","barycenter","weight"]))}function _u(t,e){let n=0,o=0;t.weight&&(n+=t.barycenter*t.weight,o+=t.weight),e.weight&&(n+=e.barycenter*e.weight,o+=e.weight),t.vs=e.vs.concat(t.vs),t.barycenter=n/o,t.weight=o,t.i=Math.min(e.i,t.i),e.merged=!0}function Su(t,e,n,o,i){let s={},r=null,l=null,a=i;typeof e=="boolean"?(a=e,s={}):e&&(s=e,r=n??null,l=o??null);let d=_c(t,S=>Object.hasOwn(S,"barycenter")),c=d.lhs,u=d.rhs.sort((S,x)=>x.i-S.i),h=[],p=0,m=0,v=0;c.sort(Mu(l,r,!!a));for(let[S,x]of Object.entries(s)){let w=c.findIndex(E=>E.vs[0]===S);c.splice(w+1,0,x)}v=Ns(h,u,v),c.forEach(S=>{v+=S.vs.length,h.push(S.vs),p+=S.barycenter*S.weight,m+=S.weight,v=Ns(h,u,v)});let y={vs:h.flat(1)};return m&&(y.barycenter=p/m,y.weight=m),y}function Ns(t,e,n){let o;for(;e.length&&(o=e[e.length-1]).i<=n;)e.pop(),t.push(o.vs),n++;return n}function Mu(t,e,n){return(o,i)=>{if(o.barycenteri.barycenter)return 1;if(t&&(typeof o.vs[0]=="string"||typeof i.vs[0]=="string")){let s=t.node(o.vs[0]),r=t.node(i.vs[0]),l=ir(e,s,r);if(l!==0)return l}return n?i.i-o.i:o.i-i.i}}function fr(t,e,n,o,i){var s,r,l,a,d,c,u,h,p,m,v;let y=null,S=i;typeof o=="boolean"?(S=o,y=null):o!==void 0&&(y=o);let x=t.children(e),w=t.node(e),E=w?w.borderLeft:void 0,b=w?w.borderRight:void 0,M={};E&&(x=x.filter(B=>B!==E&&B!==b));let T=xu(t,x);T.forEach(B=>{if(t.children(B.v).length){let{result:V}=fr(t,B.v,n,y,S);M[B.v]=V,Object.hasOwn(V,"barycenter")&&Lu(B,V)}});let L=wu(T,n);ku(L,M);let C={},N=!1;for(let B=0;B{n.vs=n.vs.flatMap(o=>e[o]?e[o].vs:o)})}function Lu(t,e){t.barycenter!==void 0?(t.barycenter=(t.barycenter*t.weight+e.barycenter*e.weight)/(t.weight+e.weight),t.weight+=e.weight):(t.barycenter=e.barycenter,t.weight=e.weight)}function Au(t,e,n,o){o||(o=t.nodes());let i=Iu(t),s=new ae({compound:!0}).setGraph({root:i}).setDefaultNodeLabel(r=>t.node(r));return o.forEach(r=>{let l=t.node(r),a=t.parent(r);if(l.rank===e||l.minRank<=e&&e<=l.maxRank){s.setNode(r),s.setParent(r,a||i);let d=t[n](r);d&&d.forEach(c=>{let u=c.v===r?c.w:c.v,h=s.edge(u,r),p=h!==void 0?h.weight:0;s.setEdge(u,r,{weight:t.edge(c).weight+p})}),Object.hasOwn(l,"minRank")&&s.setNode(r,{borderLeft:l.borderLeft[e],borderRight:l.borderRight[e]})}}),s}function Iu(t){let e;for(;t.hasNode(e=Io("_root")););return e}function Cu(t,e,n){let o={},i;n.forEach(s=>{let r=t.parent(s),l,a;for(;r;){if(l=t.parent(r),l?(a=o[l],o[l]=r):(a=i,i=r),a&&a!==r){e.setEdge(a,r);return}r=l}})}function pr(t,e={},n=null){if(typeof e.customOrder=="function"){e.customOrder(t,pr);return}let o=nr(t),i=Ps(t,gt(1,o+1),"inEdges"),s=Ps(t,gt(o-1,-1,-1),"outEdges"),r=vu(t,n);if(Fs(t,r),e.disableOptimalOrderHeuristic)return;let l=Number.POSITIVE_INFINITY,a,d=e.constraints||[];for(let c=0,u=0;u<4;++c,++u){Tu(c%2?i:s,c%4>=2,d,n),r=vt(t);let h=bu(t,r);h{o.has(s)||o.set(s,[]),o.get(s).push(r)};for(let s of t.nodes()){let r=t.node(s);if(typeof r.rank=="number"&&i(r.rank,s),typeof r.minRank=="number"&&typeof r.maxRank=="number")for(let l=r.minRank;l<=r.maxRank;l++)l!==r.rank&&i(l,s)}return e.map(function(s){return Au(t,s,n,o.get(s)||[])})}function Tu(t,e,n,o){let i=!0,s=new ae;t.forEach(function(r){n.forEach(c=>s.setEdge(c.left,c.right));let l=r.graph().root,{result:a,usedBias:d}=fr(r,l,s,o,i);e&&d&&(i=!i),a.vs.forEach((c,u)=>r.node(c).order=u),Cu(r,s,a.vs)})}function Fs(t,e){Object.values(e).forEach(n=>n.forEach((o,i)=>t.node(o).order=i))}function Ru(t,e){let n={};function o(i,s){let r=0,l=0,a=i.length,d=s[s.length-1];return s.forEach((c,u)=>{let h=Hu(t,c),p=h?t.node(h).order:a;(h||c===d)&&(s.slice(l,u+1).forEach(m=>{let v=t.predecessors(m);v&&v.forEach(y=>{let S=t.node(y),x=S.order;(x{let u=s[c];if(u!==void 0&&t.node(u).dummy){let h=t.predecessors(u);h&&h.forEach(p=>{if(p===void 0)return;let m=t.node(p);m.dummy&&(m.orderd)&&mr(n,p,u)})}})}function i(s,r){let l=-1,a=-1,d=0;return r.forEach((c,u)=>{if(t.node(c).dummy==="border"){let h=t.predecessors(c);if(h&&h.length){let p=h[0];if(p===void 0)return;a=t.node(p).order,o(r,d,u,l,a),d=u,l=a}}o(r,d,r.length,a,s.length)}),r}return e.length&&e.reduce(i),n}function Hu(t,e){if(t.node(e).dummy){let n=t.predecessors(e);if(n)return n.find(o=>t.node(o).dummy)}}function mr(t,e,n){if(e>n){let i=e;e=n,n=i}let o=t[e];o||(t[e]=o={}),o[n]=!0}function Nu(t,e,n){if(e>n){let i=e;e=n,n=i}let o=t[e];return o!==void 0&&Object.hasOwn(o,n)}function Pu(t,e,n,o,i){let s={},r={},l={};return e.forEach(a=>{a.forEach((d,c)=>{s[d]=d,r[d]=d,l[d]=c})}),e.forEach(a=>{let d=-1,c=-1,u=!1,h=a,p=a.findIndex(m=>i?.includes(m)||Bs(m,t,i));p>0&&(h=[a[p],...a.slice(0,p),...a.slice(p+1)],u=!0),h.forEach(m=>{var v;let y=o(m);if(y&&y.length){i!=null&&i.includes(m)&&(y=y.filter(w=>Bs(w,t,i)));let S=y.sort((w,E)=>{let b=l[w],M=l[E];return(b!==void 0?b:0)-(M!==void 0?M:0)}),x=(S.length-1)/2;for(let w=Math.floor(x),E=Math.ceil(x);w<=E;++w){let b=S[w];if(b===void 0)continue;let M=l[b];if(M!==void 0&&r[m]===m&&d{var S;let x=(S=s[y.v])!=null?S:0,w=r.edge(y);return Math.max(v,x+(w!==void 0?w:0))},0):s[p]=0}function c(p){let m=r.outEdges(p),v=Number.POSITIVE_INFINITY;m&&(v=m.reduce((S,x)=>{let w=s[x.w],E=r.edge(x);return Math.min(S,(w!==void 0?w:0)-(E!==void 0?E:0))},Number.POSITIVE_INFINITY));let y=t.node(p);v!==Number.POSITIVE_INFINITY&&y.borderType!==l&&(s[p]=Math.max(s[p]!==void 0?s[p]:0,v))}function u(p){return r.predecessors(p)||[]}function h(p){return r.successors(p)||[]}return a(d,u),a(c,h),Object.keys(o).forEach(p=>{var m;let v=n[p];v!==void 0&&(s[p]=(m=s[v])!=null?m:0)}),s}function Bu(t,e,n,o){let i=new ae,s=t.graph(),r=Vu(s.nodesep,s.edgesep,o);return e.forEach(l=>{let a;l.forEach(d=>{let c=n[d];if(c!==void 0){if(i.setNode(c),a!==void 0){let u=n[a];if(u!==void 0){let h=i.edge(u,c);i.setEdge(u,c,Math.max(r(t,d,a),h||0))}}a=d}})}),i}function Du(t,e){return Object.values(e).reduce((n,o)=>{let i=Number.NEGATIVE_INFINITY,s=Number.POSITIVE_INFINITY;Object.entries(o).forEach(([l,a])=>{let d=Wu(t,l)/2;i=Math.max(a+d,i),s=Math.min(a-d,s)});let r=i-s;return r{["l","r"].forEach(r=>{let l=s+r,a=t[l];if(!a||a===e)return;let d=Object.values(a),c=o-ce(Math.min,d);r!=="l"&&(c=i-ce(Math.max,d)),c&&(t[l]=tn(a,u=>u+c))})})}function Gu(t,e=void 0){let n=t.ul;return n?tn(n,(o,i)=>{var s,r;if(e){let a=e.toLowerCase(),d=t[a];if(d&&d[i]!==void 0)return d[i]}let l=Object.values(t).map(a=>{let d=a[i];return d!==void 0?d:0}).sort((a,d)=>a-d);return(((s=l[1])!=null?s:0)+((r=l[2])!=null?r:0))/2}):{}}function zu(t,e){let n=vt(t),o=Object.assign(Ru(t,n),Ou(t,n)),i={},s;["u","d"].forEach(l=>{s=l==="u"?n:Object.values(n).reverse(),["l","r"].forEach(a=>{a==="r"&&(s=s.map(u=>Object.values(u).reverse()));let d=Pu(t,s,o,u=>(l==="u"?t.predecessors(u):t.successors(u))||[],e),c=Fu(t,s,d.root,d.align,a==="r");a==="r"&&(c=tn(c,u=>-u)),i[l+a]=c})});let r=Du(t,i);return ju(i,r),Gu(i,t.graph().align)}function Vu(t,e,n){return(o,i,s)=>{let r=o.node(i),l=o.node(s),a=0,d;if(a+=r.width/2,Object.hasOwn(r,"labelpos"))switch(r.labelpos.toLowerCase()){case"l":d=-r.width/2;break;case"r":d=r.width/2;break}if(d&&(a+=n?d:-d),d=void 0,a+=(r.dummy?e:t)/2,a+=(l.dummy?e:t)/2,a+=l.width/2,Object.hasOwn(l,"labelpos"))switch(l.labelpos.toLowerCase()){case"l":d=l.width/2;break;case"r":d=-l.width/2;break}return d&&(a+=n?d:-d),a}}function Wu(t,e){return t.node(e).width}function Bs(t,e,n){var o;if(!n)return!1;let i=(o=e.node(t))==null?void 0:o.edgeObj;if(!i||e.node(t).edgeLabel.reversed)return!1;let s=n.indexOf(i?.v),r=n.indexOf(i?.w);return s!==-1&&r!==-1&&s===(r+1)%n.length||s===(r-1)%n.length}function Ku(t,e){t=er(t),qu(t),Object.entries(zu(t,e)).forEach(([n,o])=>t.node(n).x=o)}function qu(t){let e=vt(t),n=t.graph(),o=n.ranksep,i=n.rankalign,s=0;e.forEach(r=>{let l=r.reduce((a,d)=>{var c;let u=(c=t.node(d).height)!=null?c:0;return a>u?a:u},0);r.forEach(a=>{let d=t.node(a);i==="top"?d.y=s+d.height/2:i==="bottom"?d.y=s+l-d.height/2:d.y=s+l/2}),s+=l+o})}var Ds=new WeakMap;function Uu(t,e={}){return gr(t,or,e),t}function js(t,e,n){let o=e;for(;o!==void 0;){let i=t.parent(o);if(i===n)return o;o=i}}function gr(t,e,n){var o;let i=t.nodes().filter(x=>t.children(x).length),s={};i.forEach(x=>{let w=t.node(x);if(w&&w.rankdir){let E=new ae({multigraph:!0,compound:!0});E.setGraph({rankdir:w.rankdir});let b=t.children(x);b.forEach(z=>{let P={...t.node(z)};E.setNode(z,P);let K=t.parent(z);K&&K!==x&&b.includes(K)&&E.setParent(z,K)});let M=new Set;t.edges().forEach(z=>{let P=js(t,z.v,x),K=js(t,z.w,x);if(P&&K&&P!==K){let We=`${P}\0${K}`;M.has(We)||(M.add(We),E.setEdge(P,K,{...t.edge(z)}))}}),gr(E,e,n);let T=Ws(E);Gs(T,e,n,null),zs(E,T);let L=1/0,C=1/0,N=-1/0,O=-1/0;E.nodes().forEach(z=>{if(z===x)return;let P=E.node(z);P&&typeof P.x=="number"&&typeof P.y=="number"&&typeof P.width=="number"&&typeof P.height=="number"&&(L=Math.min(L,P.x-P.width/2),N=Math.max(N,P.x+P.width/2),C=Math.min(C,P.y-P.height/2),O=Math.max(O,P.y+P.height/2))}),(!isFinite(L)||!isFinite(C)||!isFinite(N)||!isFinite(O))&&(L=C=0,N=O=0);let B=N-L,V=O-C;s[x]={minX:L,minY:C,maxX:N,maxY:O,width:B,height:V,offsetX:L,offsetY:C},w._dagreClusterSubgraph=E}});let r=[],l=x=>{let w=[],E=(t.children(x)||[]).filter(b=>b!==x);for(;E.length>0;){let b=E.shift();w.push(b),(t.children(b)||[]).filter(M=>M!==b).forEach(M=>E.push(M))}return w},a=new Map;i.forEach(x=>{let w=t.node(x);w&&w.rankdir&&s[x]&&a.set(x,(t.children(x)||[]).filter(E=>E!==x))});let d=new Set([...a.values()].flat()),c=new Map;a.forEach((x,w)=>{d.has(w)||c.set(w,l(w))});let u=new Set([...c.values()].flat()),h=x=>{for(let[w,E]of c)if(E.includes(x))return w;return x},p=[];t.edges().forEach(x=>{(u.has(x.v)||u.has(x.w))&&p.push({edge:x,label:t.edge(x)})});let m=new Map;u.forEach(x=>{let w=t.parent(x);m.set(x,typeof w=="string"?w:void 0)}),c.forEach((x,w)=>{let E=t.node(w),b=[];x.forEach(L=>{let C=t.node(L);C&&(b.push({id:L,node:C,parent:m.get(L)}),t.removeNode(L))});let M=p.filter(({edge:L})=>x.includes(L.v)||x.includes(L.w)),T=s[w];E&&(r.push({clusterId:w,subgraph:E._dagreClusterSubgraph,bounds:T,children:x,removedNodes:b,removedEdges:M}),E.width=T.width,E.height=T.height)});let v=new Set;p.forEach(({edge:x,label:w})=>{let E=h(x.v),b=h(x.w);if(E!==b&&t.hasNode(E)&&t.hasNode(b)){let M=`${E}\0${b}`;v.has(M)||(v.add(M),t.setEdge(E,b,{...w,width:0,height:0}))}});let y=Ws(t),S=Gs(y,e,n,(o=Ds.get(t))!=null?o:null);Ds.set(t,S),zs(t,y),v.forEach(x=>{let w=x.indexOf("\0"),E=x.slice(0,w),b=x.slice(w+1);t.hasEdge(E,b)&&t.removeEdge(E,b)}),r.forEach(({clusterId:x,subgraph:w,bounds:E,removedNodes:b,removedEdges:M})=>{var T,L;let C=t.node(x),N=(T=C?.x)!=null?T:0,O=(L=C?.y)!=null?L:0,B=(E.minX+E.maxX)/2,V=(E.minY+E.maxY)/2;b.forEach(({id:z,node:P,parent:K})=>{t.setNode(z,P),K!==void 0&&t.setParent(z,K)}),M.forEach(({edge:z,label:P})=>{t.setEdge(z,P)}),w.nodes().forEach(z=>{if(z===x)return;let P=w.node(z),K=t.node(z);K&&P&&typeof P.x=="number"&&typeof P.y=="number"&&(K.x=N+(P.x-B),K.y=O+(P.y-V))}),delete C._dagreClusterSubgraph}),i.forEach(x=>{var w,E;let b=t.node(x),M=s[x];if(b&&b.rankdir&&b._dagreClusterSubgraph&&M){let T=b._dagreClusterSubgraph,L=(w=b.x)!=null?w:0,C=(E=b.y)!=null?E:0,N=(M.minX+M.maxX)/2,O=(M.minY+M.maxY)/2;T.nodes().forEach(B=>{if(B===x)return;let V=T.node(B),z=t.node(B);if(z&&V&&typeof V.x=="number"&&typeof V.y=="number"){let P=V.x-N,K=V.y-O;z.x=L+P,z.y=C+K}}),delete b._dagreClusterSubgraph}})}function Gs(t,e,n,o=null){var i,s;let r=n?.useDynamic!==!1,l=r&&(i=o?.graph)!=null?i:null,a=r&&(s=o?.rawNodes)!=null?s:null;e(" makeSpaceForEdgeLabels",()=>th(t)),e(" removeSelfEdges",()=>ch(t)),e(" acyclic",()=>Pc(t,l)),e(" nestingGraph.run",()=>lu(t)),e(" rank",()=>Jc(er(t))),e(" injectEdgeLabelProxies",()=>nh(t)),e(" removeEmptyRanks",()=>wc(t)),e(" nestingGraph.cleanup",()=>cu(t)),e(" normalizeRanks",()=>xc(t)),e(" assignRankMinMax",()=>oh(t)),e(" removeEdgeLabelProxies",()=>ih(t)),e(" normalize.run",()=>jc(t)),e(" parentDummyChains",()=>ou(t)),e(" addBorderSegments",()=>uu(t)),e(" order",()=>pr(t,n,a)),e(" insertSelfEdges",()=>uh(t)),e(" adjustCoordinateSystem",()=>fu(t)),e(" position",()=>Ku(t,n.corePath)),e(" positionSelfEdges",()=>hh(t));let d=JSON.parse(JSON.stringify(t._nodes));return e(" removeBorderNodes",()=>dh(t)),e(" normalize.undo",()=>zc(t)),e(" fixupEdgeLabelCoords",()=>lh(t)),e(" undoCoordinateSystem",()=>pu(t)),e(" translateGraph",()=>sh(t)),e(" assignNodeIntersects",()=>rh(t)),e(" reversePoints",()=>ah(t)),e(" acyclic.undo",()=>Bc(t)),{graph:t,rawNodes:d}}function zs(t,e){t.nodes().forEach(n=>{let o=t.node(n),i=e.node(n);o&&(o.x=i.x,o.y=i.y,o.order=i.order,o.rank=i.rank,e.children(n).length&&(o.width=i.width,o.height=i.height))}),t.edges().forEach(n=>{let o=t.edge(n),i=e.edge(n);o.points=i.points,Object.hasOwn(i,"x")&&(o.x=i.x,o.y=i.y)}),t.graph().width=e.graph().width,t.graph().height=e.graph().height}var Xu=["nodesep","edgesep","ranksep","marginx","marginy"],Yu={ranksep:50,edgesep:20,nodesep:50,rankdir:"TB",rankalign:"center"},Zu=["acyclicer","ranker","rankdir","align","rankalign"],Qu=["width","height","rank"],Vs={width:0,height:0},$u=["minlen","weight","width","height","labeloffset"],Ju={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},eh=["labelpos"];function Ws(t){let e=new ae({multigraph:!0,compound:!0}),n=Mo(t.graph());return e.setGraph(Object.assign({},Yu,So(n,Xu),en(n,Zu))),t.nodes().forEach(o=>{let i=Mo(t.node(o)),s=So(i,Qu);Object.keys(Vs).forEach(l=>{s[l]===void 0&&(s[l]=Vs[l])}),e.setNode(o,s);let r=t.parent(o);r!==void 0&&e.setParent(o,r)}),t.edges().forEach(o=>{let i=Mo(t.edge(o));e.setEdge(o,Object.assign({},Ju,So(i,$u),en(i,eh)))}),e}function th(t){let e=t.graph();e.ranksep/=2,t.edges().forEach(n=>{var o;let i=t.edge(n);i.minlen*=2,((o=i.labelpos)!=null?o:"r").toLowerCase()!=="c"&&(e.rankdir==="TB"||e.rankdir==="BT"?i.width+=i.labeloffset:i.height+=i.labeloffset)})}function nh(t){t.edges().forEach(e=>{let n=t.edge(e);if(n.width&&n.height){let o=t.node(e.v),i={rank:(t.node(e.w).rank-o.rank)/2+o.rank,e};Ye(t,"edge-proxy",i,"_ep")}})}function oh(t){let e=0;t.nodes().forEach(n=>{let o=t.node(n);o.borderTop&&(o.minRank=t.node(o.borderTop).rank,o.maxRank=t.node(o.borderBottom).rank,e=Math.max(e,o.maxRank))}),t.graph().maxRank=e}function ih(t){t.nodes().forEach(e=>{let n=t.node(e);if(n.dummy==="edge-proxy"){let o=n;t.edge(o.e).labelRank=n.rank,t.removeNode(e)}})}function sh(t){let e=Number.POSITIVE_INFINITY,n=0,o=Number.POSITIVE_INFINITY,i=0,s=t.graph(),r=s.marginx||0,l=s.marginy||0;function a(d){let c=d.x,u=d.y,h=d.width,p=d.height;e=Math.min(e,c-h/2),n=Math.max(n,c+h/2),o=Math.min(o,u-p/2),i=Math.max(i,u+p/2)}t.nodes().forEach(d=>a(t.node(d))),t.edges().forEach(d=>{let c=t.edge(d);Object.hasOwn(c,"x")&&a(c)}),e-=r,o-=l,t.nodes().forEach(d=>{let c=t.node(d);c.x-=e,c.y-=o}),t.edges().forEach(d=>{let c=t.edge(d);c.points.forEach(u=>{u.x-=e,u.y-=o}),Object.hasOwn(c,"x")&&(c.x-=e),Object.hasOwn(c,"y")&&(c.y-=o)}),s.width=n-e+r,s.height=i-o+l}function rh(t){t.edges().forEach(e=>{if(e.v===e.w)return;let n=t.edge(e),o=t.node(e.v),i=t.node(e.w),s,r;n.points?(s=n.points[0],r=n.points[n.points.length-1]):(n.points=[],s=i,r=o),n.points.unshift(As(o,s)),n.points.push(As(i,r))})}function lh(t){t.edges().forEach(e=>{let n=t.edge(e);if(Object.hasOwn(n,"x"))switch((n.labelpos==="l"||n.labelpos==="r")&&(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset;break}})}function ah(t){t.edges().forEach(e=>{let n=t.edge(e);n.reversed&&n.points.reverse()})}function dh(t){t.nodes().forEach(e=>{if(t.children(e).length){let n=t.node(e),o=t.node(n.borderTop),i=t.node(n.borderBottom),s=t.node(n.borderLeft[n.borderLeft.length-1]),r=t.node(n.borderRight[n.borderRight.length-1]);n.width=Math.abs(r.x-s.x),n.height=Math.abs(i.y-o.y),n.x=s.x+n.width/2,n.y=o.y+n.height/2}}),t.nodes().forEach(e=>{t.node(e).dummy==="border"&&t.removeNode(e)})}function ch(t){t.edges().forEach(e=>{if(e.v===e.w){let n=t.node(e.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e,label:t.edge(e)}),t.removeEdge(e)}})}function uh(t){vt(t).forEach(e=>{let n=0;e.forEach((o,i)=>{let s=t.node(o);typeof s.rank!="number"&&(s.rank=0),s.order=i+n,(s.selfEdges||[]).forEach(r=>{Ye(t,"selfedge",{width:r.label.width,height:r.label.height,rank:s.rank,order:i+ ++n,e:r.e,edgeLabel:r.label},"_se"),(!Array.isArray(r.label.points)||r.label.points.length!==7)&&(r.label.points=[{x:0,y:-10},{x:0,y:-10},{x:0,y:0},{x:0,y:10},{x:0,y:10},{x:0,y:0},{x:0,y:0}])}),delete s.selfEdges})})}function hh(t){t.nodes().forEach(e=>{let n=t.node(e),o=i=>typeof i=="number"&&isFinite(i);if(n.dummy==="selfedge"){let i=n,s=t.node(i.e.v),r=o(s?.x)?s.x:0,l=o(s?.y)?s.y:0,a=o(s?.width)?s.width:0,d=o(s?.height)?s.height:0,c=o(n.x)?n.x:r,u=o(n.y)?n.y:l,h=a/2,p=d/2;i.edgeLabel.points=[{x:c+h,y:u-p},{x:c+h,y:u-p},{x:c,y:u},{x:c-h,y:u+p},{x:c-h,y:u+p},{x:c,y:u},{x:c,y:u}],i.edgeLabel.x=c,i.edgeLabel.y=u,t.setEdge(i.e,i.edgeLabel),t.removeNode(e)}else n&&Array.isArray(n.selfEdges)&&n.selfEdges.forEach(i=>{if(!Array.isArray(i.label.points)||i.label.points.length!==7){let s=o(n.x)?n.x:0,r=o(n.y)?n.y:0,l=o(n.width)?n.width:0,a=o(n.height)?n.height:0,d=l/2,c=a/2;i.label.points=[{x:s+d,y:r-c},{x:s+d,y:r-c},{x:s,y:r},{x:s-d,y:r+c},{x:s-d,y:r+c},{x:s,y:r},{x:s,y:r}]}})})}function So(t,e){return tn(en(t,e),Number)}function Mo(t){let e={};return t&&Object.entries(t).forEach(([n,o])=>{typeof n=="string"&&(n=n.toLowerCase()),e[n]=o}),e}function fh(t){let e=vt(t),n=new ae({compound:!0,multigraph:!0}).setGraph({});return t.nodes().forEach(o=>{n.setNode(o,{label:o}),n.setParent(o,"layer"+t.node(o).rank)}),t.edges().forEach(o=>n.setEdge(o.v,o.w,{},o.name)),e.forEach((o,i)=>{let s="layer"+i;n.setNode(s,{rank:"same"}),o.reduce((r,l)=>(n.setEdge(r,l,{style:"invis"}),l))}),n}var ph={graphlib:qs,version:Lc,layout:Uu,debug:fh,util:{time:Sc,notime:or}},Oo=ph;var yh=58,xh=13*.55;function Po(t,e,n=3){let o=e+"|"+n+"|"+t,i=on.get(o);if(i)return i;let s=Eh(t,e,n);return on.size>=wh&&on.clear(),on.set(o,s),s}var wh=8192,on=new Map;function Eh(t,e,n){let o=Math.max(4,Math.floor((e-yh)/xh)),i=String(t||"").trim().split(/\s+/).filter(Boolean),s=[],r="",l=!1;for(let a=0;ao;){let u=r?o-r.length-1:o;if(u>=4&&(r=r?r+" "+d.slice(0,u):d.slice(0,u),d=d.slice(u)),s.push(r),r="",s.length===n)break}if(s.length===n){l=!0;break}let c=r?r+" "+d:d;if(c.length<=o){r=c;continue}if(s.push(r),r=d,s.length===n){l=!0;break}}if(!l&&r)s.push(r);else if(l&&s.length){let a=s[s.length-1];s[s.length-1]=(a.length>=o?a.slice(0,o-1):a)+"…"}return s.length>n&&(s.length=n),s.length?s:[""]}function Fo(t,e){return Po(t.label||t.qualname||t.id,e).length}function _h(t=!1){return t}function Bo(t){let e=t.loc;return!!(e&&(e.file||e.line))}function Sh(t,e=!1){let n=Bo(t)||e?72:60,o=(Fo(t,br(t,e))-1)*18;return n+o+(_h(e)?26:0)}function br(t,e=!1){return e?248:t.kind==="model"||t.kind==="entrypoint"||t.level==="stage"?248:t.kind==="layer"?176:216}function Do(t,e=!1){return{w:br(t,e),h:Sh(t,e)}}function yr(t,e){let n=[];for(let o of e){let i=t.node(o);i&&isFinite(i.x)&&isFinite(i.y)&&n.push({id:o,n:i})}return n}function xr(t){let e=new Map;for(let n of t){let o=Math.round(n.n.x*100)/100,i=e.get(o);i?i.push(n):e.set(o,[n])}return{keys:Array.from(e.keys()).sort((n,o)=>n-o),columns:e}}function wr(t,e){let n=yr(t,e);if(n.length<2)return;let{keys:o,columns:i}=xr(n),s=0;for(let r of o){let l=i.get(r);for(let v of l)v.n.x+=s;if(l.length<2)continue;l.sort((v,y)=>v.n.y-y.n.y||(v.idy.id?1:0));let a=0,d=0;for(let v of l)a+=v.n.height,d=Math.max(d,v.n.width);if(a+=(l.length-1)*Vt,a<=no)continue;let c=Math.ceil(a/no),u=Math.max(1,Math.ceil(l.length/c)),h=Math.min.apply(null,l.map(v=>v.n.y-v.n.height/2)),p=0,m=h;for(let v=0;v0&&v%u===0&&(p++,m=h);let y=l[v];y.n.x=r+s+p*(d+oo),y.n.y=m+y.n.height/2,m+=y.n.height+Vt}s+=p*(d+oo)}}function Er(t,e){let n=yr(t,e);if(n.length<2)return!1;let{keys:o,columns:i}=xr(n);if(o.length<2)return!1;let s=[];for(let h of o){let p=i.get(h),m=1/0,v=-1/0;for(let y of p)m=Math.min(m,y.n.x-y.n.width/2),v=Math.max(v,y.n.x+y.n.width/2);s.push({members:p,left:m,right:v})}let r=s[0].left;if(s[s.length-1].right-r<=io)return!1;let l=[],a=[],d=r;for(let h of s)a.length>0&&h.right-d>io&&(l.push(a),a=[],d=h.left),a.push(h);if(a.length>0&&l.push(a),l.length<2)return!1;let c=1/0;for(let h of n)c=Math.min(c,h.n.y-h.n.height/2);let u=c;for(let h of l){let p=1/0;for(let S of h)for(let x of S.members)p=Math.min(p,x.n.y-x.n.height/2);let m=r-h[0].left,v=u-p,y=u;for(let S of h)for(let x of S.members)x.n.x+=m,x.n.y+=v,y=Math.max(y,x.n.y+x.n.height/2);u=y+Vt}return!0}function Qe(t){return t.kind==="control"&&t.subkind==="back"}function kr(t,e){let n=new Map,o=[],i=new Map,s=new Map;for(let b of t.graph.nodes||[])s.set(b.id,b.stage);let r=(t.graph.edges||[]).filter(b=>!Qe(b)),l=new Set;for(let b of t.graph.edges||[]){if(!Qe(b))continue;let M=t.nodeById.get(b.source);M&&l.add(M.stage);let T=t.nodeById.get(b.target);T&&l.add(T.stage)}let a=new Set;{let b=new Map;t.lanes.forEach((M,T)=>b.set(M.id,T));for(let M of t.graph.edges||[]){if(Qe(M))continue;let T=t.nodeById.get(M.source),L=t.nodeById.get(M.target);if(!T||!L)continue;let C=b.get(T.stage),N=b.get(L.stage);C===void 0||N===void 0||Math.abs(C-N)>1&&a.add(T.stage+">"+L.stage)}}let d=a.size,c=d>0,u=Wt+d*lo+ro,h=c?Math.min(u,is):0,p=Math.max(0,h-Wt-ro),m=c?Math.max(ss,Math.min(lo,Ih(p/Math.max(1,d)))):0,v=c?Math.max(1,Math.min(d,Math.floor(p/m+1e-6))):0,y=32+h,S=32,x=320;t.lanes.forEach((b,M)=>{i.set(b.id,M);let T=t.roots(b.id).filter(V=>!t.isHidden(V,e)),L=Lr(t,e,T,0,b.id,r,n),C=Math.max(L.w,264),N=(l.has(b.id)?ns:0)+os,O=Math.max(L.h,40)+N,B=28+O+28;L.place(y+28,S+28),o.push({id:b.id,label:b.label||b.id,order:b.order,index:M,x:y,y:S,w:C+56,h:B,headerH:28,nodeCount:Ah(t,b.id)}),x=Math.max(x,C+56),S+=B+40});let w=y+x+32,E=Math.max(S-40+32,64);return{lanes:o,laneIndex:i,boxes:n,width:w,height:E,channelX:32+Wt,hasChannel:c,channelPairs:d,channelSlots:v,channelStep:m,channelW:h}}function Ah(t,e){let n=0;for(let o of t.graph.nodes||[])o.stage===e&&n++;return n}function Lr(t,e,n,o,i,s,r){if(n.length===0)return{w:0,h:0,place:()=>{}};let l=new Set(n),a=new Map,d=new Map,c=new Oo.graphlib.Graph;c.setGraph({...es}),c.setDefaultEdgeLabel(()=>({}));for(let w of n){let E=t.nodeById.get(w);if(!E)continue;let b=t.isGroup(w),M=e.has(w);if(b&&!M){let L=t.laneChildren(w),C=Lr(t,e,L,o+1,i,s,r);a.set(w,C);let N=Math.max(200,C.w+32),O=Math.max(56,34+C.h+16);d.set(w,{w:N,h:O})}else b&&M?d.set(w,Do(E,!0)):d.set(w,Do(E,!1));let T=d.get(w);c.setNode(w,{width:T.w,height:T.h})}let u=new Map;for(let w of s){let E=Mr(t,w.source,l),b=Mr(t,w.target,l);if(!E||!b||E===b)continue;let M=E+"\0"+b,L=(u.get(M)||0)+ts(w.kind);u.set(M,L),c.setEdge(E,b,{weight:L,minlen:1})}Oo.layout(c),wr(c,n),Er(c,n);let h=1/0,p=1/0,m=-1/0,v=-1/0,y=new Map;for(let w of n){let E=c.node(w);if(!E)continue;let b=E.width,M=E.height,T=E.x-b/2,L=E.y-M/2;y.set(w,{x:T,y:L,w:b,h:M}),h=Math.min(h,T),p=Math.min(p,L),m=Math.max(m,T+b),v=Math.max(v,L+M)}isFinite(h)||(h=0,p=0,m=0,v=0);let S=Math.max(0,m-h),x=Math.max(0,v-p);return{w:S,h:x,place(w,E){for(let b of n){let M=y.get(b);if(!M)continue;let T=ln(w+(M.x-h)),L=ln(E+(M.y-p)),C=t.isGroup(b),N=e.has(b);r.set(b,{id:b,x:T,y:L,w:M.w,h:M.h,laneId:i,depth:o,isGroup:C,collapsed:C&&N,headerH:C&&!N?34:0});let O=a.get(b);if(O){let B=T+Math.max(16,(M.w-O.w)/2);O.place(ln(B),ln(L+34))}}}}}function Mr(t,e,n){if(n.has(e))return e;let o=t.parentOf.get(e)||null,i=0;for(;o;){if(n.has(o))return o;if(++i>4096)return null;o=t.parentOf.get(o)||null}return null}function ln(t){return Math.round(t*100)/100}function Ih(t){return Math.floor(t*100)/100}function bt(t,e){let n=t.lanes[e],o=t.lanes[e+1];return n&&o?(n.y+n.h+o.y)/2:n?n.y+n.h+12:o?o.y-12:0}function Ir(t,e,n=rs,o=ls){if(e<=1)return 0;let i=Math.min(o,2*n/(e-1));return an((t-(e-1)/2)*i)}function Ch(t,e,n){let o=Math.abs(t.exitY-t.entryY),i=Math.abs(e.exitY-e.entryY);if(Math.abs(o-i)>.5)return i-o;let s=n.get(t.key)??0,r=n.get(e.key)??0;return Math.abs(s-r)>.5?s-r:t.keye.key?1:0}function Cr(t,e){let n=new Map,o=new Map,i=new Map,s=new Map,r=[];for(let c of e){let u=c.sBox.laneId+">"+c.tBox.laneId,h=s.get(u);h?h.push(c):(s.set(u,[c]),r.push(u))}let l=new Map,a=[];for(let c of r){let u=s.get(c);u.sort((b,M)=>zo(b.tBox)-zo(M.tBox)||Ar(b.tBox)-Ar(M.tBox)||Th(b.key,M.key));let h=u[0],p=t.laneIndex.get(h.sBox.laneId)??0,m=t.laneIndex.get(h.tBox.laneId)??0,v=m>p,y=Math.abs(m-p)===1,S=an(y?bt(t,Math.min(p,m)):v?bt(t,p):bt(t,p-1)),x=an(y?S:v?bt(t,m-1):bt(t,m)),w={key:c,sourceLane:h.sBox.laneId,targetLane:h.tBox.laneId,adjacent:y,down:v,members:u.map(b=>b.key),trunkX:t.channelX,entryY:S,exitY:x,slot:0};n.set(c,w);let E=0;for(let b of u)E+=zo(b.tBox);l.set(c,u.length?E/u.length:0),u.forEach((b,M)=>{o.set(b.key,w),i.set(b.key,M)}),y||a.push(w)}let d=Math.max(1,t.channelSlots);return a.sort((c,u)=>Ch(c,u,l)),a.forEach((c,u)=>{c.slot=u,c.trunkX=an(t.channelX+Math.min(u,d-1)*t.channelStep)}),{pairs:n,pairOf:o,indexOf:i}}function Ar(t){return t.x+t.w/2}function zo(t){return t.y+t.h/2}function Th(t,e){return te?1:0}function an(t){return Math.round(t*100)/100}function ve(t,e){if(t.length<2)return"";let n=["M "+t[0].x+" "+t[0].y];for(let i=1;i=o||i===t.length-1){let r=s===0?0:(o-n)/s,l=Vo(t[i-1],t[i],Math.max(0,Math.min(1,r))),a=Math.atan2(t[i].y-t[i-1].y,t[i].x-t[i-1].x)*180/Math.PI;return{point:{x:$e(l.x),y:$e(l.y)},angle:a}}n+=s}return{point:t[t.length-1],angle:0}}function $e(t){return Math.round(t*100)/100}function dn(t,e){return Math.hypot(e.x-t.x,e.y-t.y)}function Vo(t,e,n){return{x:t.x+(e.x-t.x)*n,y:t.y+(e.y-t.y)*n}}function Rh(t,e){let n=new Map,o=[],i=new Map;for(let r of e.boxes.values()){o.push(r);let l=n.get(r.laneId);l?l.push(r):n.set(r.laneId,[r]);let a=new Set([r.id]);for(let d of t.ancestors(r.id))a.add(d);i.set(r.id,a)}for(let r of n.values())r.sort((l,a)=>l.x-a.x||zh(l.id,a.id));let s=new Map;for(let r of e.lanes){let l=r.y+r.headerH;for(let a of n.get(r.id)||[])l=Math.max(l,a.y+a.h);s.set(r.id,ee(Math.min(l+9,r.y+r.h-5)))}return{index:t,frame:e,laneBoxes:n,ancestry:i,band:s,all:o}}function Rr(t,e,n,o){let i=t.ancestry.get(e.id);if(!i||i.has(n)||i.has(o))return!1;let s=t.ancestry.get(n);if(s&&s.has(e.id))return!1;let r=t.ancestry.get(o);return!(r&&r.has(e.id))}function Oh(t,e){return{x0:Math.min(t.x,e.x),x1:Math.max(t.x,e.x),y0:Math.min(t.y,e.y),y1:Math.max(t.y,e.y)}}function Hh(t,e){return t.x1>e.x+1&&t.x0e.y+1&&t.y0=d||Rr(t,v,i,s)&&c.push({a:v.x-so,b:v.x+v.w+so});c.sort((v,y)=>v.a-y.a);let u=l?l.x+4:0,h=l?l.x+l.w-4:u,p=[],m=u;for(let v of c)if(!(v.b<=m)&&(v.a>m&&p.push({a:m,b:Math.min(v.a,h)}),m=Math.max(m,v.b),m>=h))break;return mv.b-v.a>=10&&v.b>u&&v.a=10&&a.push(ee((h+Math.min(u.b,h+40))/2))}else{let h=Math.min(u.b,e.x-4);h-u.a>=10&&a.push(ee((Math.max(u.a,h-40)+h)/2))}a.sort((u,h)=>n?u-h:h-u);let d=F(e),c=n?e.x+e.w:e.x;for(let u of a)if(!be(t,[A(c,d),A(u,d)],s,r))return u;return null}function Je(t,e,n,o,i,s,r){let l=n>=j(e),a=qo(t,e,l,o,i,s,r);return a!==null?a:qo(t,e,!l,o,i,s,r)}function Or(t,e,n){let o=Rh(t,e),i=new Map,s=[];for(let h of t.graph.edges||[]){let p=t.visibleRepresentative(h.source,n),m=t.visibleRepresentative(h.target,n);if(p===m&&!(h.source===h.target&&p===h.source)||!e.boxes.has(p)||!e.boxes.has(m))continue;let v=p+"\0"+m+"\0"+h.kind+"\0"+(h.subkind||""),y=i.get(v);y||(y={edges:[],s:p,t:m},i.set(v,y),s.push(v)),y.edges.push(h)}let r=[];for(let h of s){let p=i.get(h);if(Qe(p.edges[0]))continue;let m=e.boxes.get(p.s),v=e.boxes.get(p.t);m.laneId!==v.laneId&&(t.ancestors(p.s).indexOf(p.t)>=0||t.ancestors(p.t).indexOf(p.s)>=0||r.push({key:h,sBox:m,tBox:v}))}let l=Cr(e,r),a=new Map,d=new Map,c=new Map,u=[];for(let h of s){let p=i.get(h),m=p.edges[0],v=e.boxes.get(p.s),y=e.boxes.get(p.t),S=Qe(m),x=v.laneId!==y.laneId,w=t.ancestors(p.s).indexOf(p.t)>=0,E=t.ancestors(p.t).indexOf(p.s)>=0,b,M;if(p.s===p.t)b=Gh(v,Ko(c,p.s));else if(S){let C=Ko(a,v.laneId);b=jh(o,t,e,v,y,C)}else if(E||w)b=Ph(o,v,y,E);else if(x){let C=l.pairOf.get(h),N=l.indexOf.get(h)||0,O=Dh(o,v,y,C,N);b=O.points,O.joinFrom>=0&&O.joinTo>O.joinFrom&&(M={key:C.key,sourceLane:C.sourceLane,targetLane:C.targetLane,index:N,size:C.members.length,axis:C.adjacent?"x":"y",channelX:C.trunkX,entryY:C.entryY,exitY:C.adjacent?C.entryY:C.exitY,joinFrom:O.joinFrom,joinTo:O.joinTo})}else{let C=Ko(d,"in:"+v.laneId);b=Fh(o,e,v,y,C)}let T=p.edges.length>1?"×"+p.edges.length:m.label||"",L=Wo(b);u.push({id:m.id,ids:p.edges.map(C=>C.id),kind:m.kind,subkind:m.subkind,source:p.s,target:p.t,label:T,points:b,d:ve(b,le),mid:L.point,midAngle:L.angle,crossLane:x,back:S,count:p.edges.length,trunk:M})}return u}function Ko(t,e){let n=(t.get(e)||0)+1;return t.set(e,n),n-1}function j(t){return t.x+t.w/2}function F(t){return t.y+t.h/2}function Ph(t,e,n,o){let i=o?e:n,s=o?n:e,r=ee(i.x+12),l=i.y+Math.min(28,i.h/2),a=o?[A(r,l),A(r,F(s)),A(s.x,F(s))]:[A(s.x,F(s)),A(r,F(s)),A(r,l)];if(!be(t,a,e.id,n.id))return a;let d=qo(t,s,!1,F(s),F(s),e.id,n.id),c=d===null?r:d,u=d===null||d<=s.x?s.x:s.x+s.w,h=o?[A(r,l),A(r,F(s)),A(c,F(s)),A(u,F(s))]:[A(u,F(s)),A(c,F(s)),A(r,F(s)),A(r,l)];return be(t,h,e.id,n.id)?a:un(h)}function Fh(t,e,n,o,i){let s=n.x+n.w,r=Tr(n,i),l=o.x,a=Tr(o,i),d;if(l>=s+16){let u=ee((s+l)/2);d=Math.abs(r-a)<.5?[A(s,r),A(l,a)]:[A(s,r),A(u,r),A(u,a),A(l,a)]}else{let u=e.lanes[e.laneIndex.get(n.laneId)??0],h=Math.max(n.y+n.h,o.y+o.h)+14+i*6,p=u?u.y+u.h-6:h,m=ee(Math.min(h,p));d=[A(j(n),n.y+n.h),A(j(n),m),A(j(o),m),A(j(o),o.y+o.h)]}if(!be(t,d,n.id,o.id))return d;let c=Bh(t,n,o,i);return c&&!be(t,c,n.id,o.id)?c:d}function Bh(t,e,n,o){let i=ee((t.band.get(e.laneId)??Math.max(e.y+e.h,n.y+n.h)+12)+o*5),s=Je(t,e,j(n),F(e),i,e.id,n.id),r=Je(t,n,j(e),i,F(n),e.id,n.id);if(s===null||r===null)return null;let l=s>=j(e)?e.x+e.w:e.x,a=r>=j(n)?n.x+n.w:n.x;return un([A(l,F(e)),A(s,F(e)),A(s,i),A(r,i),A(r,F(n)),A(a,F(n))])}function Dh(t,e,n,o,i){let s=o.down,r=Ir(i,o.members.length),l=s?e.y+e.h:e.y,a=s?n.y:n.y+n.h,d=ee(o.entryY+r),c=d,u=ee(o.exitY+r),h=o.trunkX,p=o.adjacent?[A(j(e),l),A(j(e),d),A(j(n),d),A(j(n),a)]:[A(j(e),l),A(j(e),c),A(h,c),A(h,u),A(j(n),u),A(j(n),a)],m=o.adjacent?[p[1],p[2]]:[p[2],p[3]];if(!be(t,p,e.id,n.id))return cn(p,m);let v=o.adjacent?d:c,y=o.adjacent?d:u,S=Je(t,e,j(n),F(e),v,e.id,n.id),x=Je(t,n,j(e),y,F(n),e.id,n.id);if(S===null||x===null)return cn(p,m);let w=S>=j(e)?e.x+e.w:e.x,E=x>=j(n)?n.x+n.w:n.x,b=o.adjacent?[A(w,F(e)),A(S,F(e)),A(S,d),A(x,d),A(x,F(n)),A(E,F(n))]:[A(w,F(e)),A(S,F(e)),A(S,c),A(h,c),A(h,u),A(x,u),A(x,F(n)),A(E,F(n))],M=o.adjacent?[b[2],b[3]]:[b[3],b[4]];return be(t,b,e.id,n.id)?cn(p,m):cn(un(b),M)}function cn(t,e){return{points:t,joinFrom:t.indexOf(e[0]),joinTo:t.indexOf(e[1])}}function jh(t,e,n,o,i,s){let r=n.lanes[n.laneIndex.get(o.laneId)??0],l=Math.max(o.y+o.h,i.y+i.h),a=new Set(e.ancestors(o.id)),d=new Set(e.ancestors(i.id));for(let x of n.boxes.values()){if(x.laneId!==o.laneId||x.id!==o.id&&x.id!==i.id&&(a.has(x.id)||d.has(x.id)))continue;let w=Math.min(j(o),j(i)),E=Math.max(j(o),j(i));x.x+x.wE||(l=Math.max(l,x.y+x.h))}let c=ee(Math.min(l+12+s*7,r?r.y+r.h-6:l+12)),u=d.has(o.id)||a.has(i.id)?ee(i.x+Math.min(28,i.w/3)):j(i),h=[A(j(o),o.y+o.h),A(j(o),c),A(u,c),A(u,i.y+i.h)];if(!be(t,h,o.id,i.id))return h;let p=Je(t,o,j(i),F(o),c,o.id,i.id),m=Je(t,i,j(o),c,F(i),o.id,i.id);if(p===null||m===null)return h;let v=p>=j(o)?o.x+o.w:o.x,y=m>=j(i)?i.x+i.w:i.x,S=un([A(v,F(o)),A(p,F(o)),A(p,c),A(m,c),A(m,F(i)),A(y,F(i))]);return be(t,S,o.id,i.id)?h:S}function Gh(t,e){let n=14+e*6,o=t.x+t.w,i=t.y+t.h,s=i-Math.min(14+e*6,t.h/2),r=o-Math.min(28+e*6,t.w/2);return[A(o,s),A(o+n,s),A(o+n,i+n),A(r,i+n),A(r,i)]}function Tr(t,e){if(e===0)return ee(F(t));let n=Math.min(t.h-16,40),o=n/4,i=(e+1>>1)*(e%2===1?1:-1);return ee(F(t)+Math.max(-n/2,Math.min(n/2,i*o)))}function un(t){let e=[];for(let n of t){let o=e[e.length-1];o&&Math.abs(o.x-n.x)<.01&&Math.abs(o.y-n.y)<.01||e.push(n)}return e.length>=2?e:t}function zh(t,e){return te?1:0}function A(t,e){return{x:ee(t),y:ee(e)}}function ee(t){return Math.round(t*100)/100}function Uo(t){let e=Array.from(t.boxes.values());return e.length?e.slice().sort((n,o)=>n.y-o.y||n.x-o.x)[0]:null}function Hr(t,e,n,o){let i=Array.from(e.boxes.values()),s=e.boxes.get(n);if(!s||!i.length)return Uo(e);let r=t.parentOf.get(n)||null,l=i.filter(u=>(t.parentOf.get(u.id)||null)===r&&u.laneId===s.laneId);l.length<2&&(l=i.filter(u=>u.laneId===s.laneId));let a=o==="ArrowRight"||o==="ArrowLeft",d=o==="ArrowRight"||o==="ArrowDown",c=u=>{let h=u.slice().sort((m,v)=>a?m.x-v.x||m.y-v.y:m.y-v.y||m.x-v.x),p=h.findIndex(m=>m.id===n);return p<0?null:h[p+(d?1:-1)]||null};return c(l)||c(i)}var Vh=["is-hover","is-selected","is-lit","is-flowing","is-traced"];function Wh(t){let e=t.bundle,n=t.sourceLabel||e.sourceLane,o=t.targetLabel||e.targetLane;return(e.edgeCount===1?"1 connection":e.edgeCount+" connections")+" from "+n+" to "+o+". Hover to separate them."}function Nr(t){let e=t.bundle,n=e.kinds.length===1?e.kinds[0]:"mixed",o=R("g",{class:"mlv-bundle mlv-bundle--"+n,"data-bundle-id":e.id,"data-count":String(e.edgeCount),"data-members":String(e.count),"data-axis":e.axis});o.setAttribute("data-lane-pair",e.sourceLane+">"+e.targetLane),t.stage&&o.setAttribute("data-stage",t.stage),t.phase!==void 0&&se(o,t.phase),t.severity&&(o.setAttribute("data-sev",t.severity),o.classList.add("has-issue"));let i=R("title");i.textContent=Wh(t),o.appendChild(i);for(let d of e.spurs)o.appendChild(R("path",{class:"mlv-bundle__spur",d:d.d}));o.appendChild(R("path",{class:"mlv-bundle__trunk",d:e.d}));let s=String(e.edgeCount),r=15+6*s.length,l=R("g",{class:"mlv-bundle__badge",transform:"translate("+e.badge.x+","+e.badge.y+")"});l.appendChild(R("rect",{class:"mlv-bundle__badge-box",x:-r/2,y:-8,width:r,height:16,rx:8}));let a=R("text",{class:"mlv-bundle__badge-text",x:0,y:0});return a.textContent=s,l.appendChild(a),o.appendChild(l),o}var hn=class{constructor(){this.els=new Map;this.memberOf=new Map}adopt(e,n){this.els=e,this.memberOf=new Map;for(let o of n)for(let i of o.memberIds)this.memberOf.set(i,o.id)}sync(e){if(this.els.size===0)return;let n=new Set;for(let[o,i]of this.memberOf){let s=e.get(o);s&&Kh(s)&&n.add(i)}for(let[o,i]of this.els)i.classList.toggle("is-expanded",n.has(o));for(let[o,i]of this.memberOf){let s=e.get(o);s&&s.classList.toggle("is-bundled",!n.has(i))}}};function Kh(t){let e=t.classList;for(let n of Vh)if(e.contains(n))return!0;return!1}var Xo={entrypoint:"M5 3.2 12.4 8 5 12.8Z",config:"M2.2 4.5h11.6M2.2 8h11.6M2.2 11.5h11.6M5.6 3.1v2.8M10.4 6.6v2.8M7 10.1v2.8",dataset:"M3 4.3c0-1 2.24-1.8 5-1.8s5 .8 5 1.8-2.24 1.8-5 1.8-5-.8-5-1.8ZM3 4.3v3.4c0 1 2.24 1.8 5 1.8s5-.8 5-1.8V4.3M3 7.7v3.4c0 1 2.24 1.8 5 1.8s5-.8 5-1.8V7.7",dataloader:"M2 4.2h2.6l6.8 7.6H14M2 11.8h2.6l6.8-7.6H14M11.6 2.4 14 4.2l-2.4 1.8M11.6 10 14 11.8l-2.4 1.8",split:"M2 8h3.6l3.4-4.2H14M9 12.2 5.6 8H2M11.8 2 14 3.8l-2.2 1.8M11.8 10.4 14 12.2l-2.2 1.8",transform:"M2.4 13.6 10 6M8.4 4.4l3.2 3.2M12 1.8v2.8M10.6 3.2h2.8M4 2v1.8M3.1 2.9h1.8",augment:"M5.2 2.2 6.1 5 8.9 5.9 6.1 6.8 5.2 9.6 4.3 6.8 1.5 5.9 4.3 5ZM11.4 8.2l.7 1.9 1.9.7-1.9.7-.7 1.9-.7-1.9-1.9-.7 1.9-.7Z",model:"M8 2 14 5 8 8 2 5ZM2 8.2 8 11.2 14 8.2M2 11.2 8 14.2 14 11.2",layer:"M2.4 4.6h11.2v2.4H2.4ZM2.4 9h11.2v2.4H2.4Z",loss:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM8 5.4A2.6 2.6 0 1 0 8 10.6 2.6 2.6 0 0 0 8 5.4ZM8 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2Z",optimizer:"M2.6 12.2a6.4 6.4 0 1 1 10.8 0M8 8.4 11.2 5.2M7.2 12.2h1.6",scheduler:"M2 12.4 5.6 8.6l2.4 2.2L13.4 4.6M9.6 4.6h3.8v3.8",scaler:"M2.4 4h11.2v8H2.4ZM5.4 4v2.4M8 4v3.6M10.6 4v2.4",train_loop:"M13.2 8A5.2 5.2 0 1 1 11.5 4.1M13.6 2.6v3.2h-3.2",eval_loop:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM5.2 8.1 7.2 10.2 11 6",metric:"M3 13V8.4M6.4 13V4.4M9.8 13v-3.4M13.2 13V6.4M2 13.8h12",checkpoint:"M2.6 3h7.6l3.2 3.2v7.4H2.6ZM5.6 3v3.4h4.6V3M5.6 13.6v-3.6h4.6v3.6",tracker:"M8 6.8A1.2 1.2 0 1 0 8 9.2 1.2 1.2 0 0 0 8 6.8ZM5.2 5.2a4 4 0 0 0 0 5.6M10.8 5.2a4 4 0 0 1 0 5.6M3.1 3.1a7 7 0 0 0 0 9.8M12.9 3.1a7 7 0 0 1 0 9.8",predict:"M9.2 1.6 4 8.6h3.3l-.7 5.8L12 7.2H8.5Z",function:"M6.6 2.8c-1.7 0-2.1.9-2.1 2.3v1.1c0 1-.5 1.6-1.5 1.6 1 0 1.5.6 1.5 1.6v1.1c0 1.4.4 2.3 2.1 2.3M9.4 2.8c1.7 0 2.1.9 2.1 2.3v1.1c0 1 .5 1.6 1.5 1.6-1 0-1.5.6-1.5 1.6v1.1c0 1.4-.4 2.3-2.1 2.3",class:"M3 2.6h10v10.8H3ZM3 6.2h10M5.4 8.8h5.2M5.4 11h3.2",artifact:"M8 2 14 5v6l-6 3-6-3V5ZM2 5l6 3 6-3M8 8v6",external:"M9 2.8h4.2V7M13.2 2.8 7.4 8.6M11.6 9.4v3.8H2.8V4.4h3.8",unknown:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM6.2 6.3a1.85 1.85 0 1 1 2.7 1.7c-.6.3-.9.8-.9 1.5M8 11.2v.9"},qh={step:"M8 5.6A2.4 2.4 0 1 0 8 10.4 2.4 2.4 0 0 0 8 5.6Z",group:"M2.6 2.6h4.4V7H2.6ZM9 2.6h4.4V7H9ZM2.6 9h4.4v4.4H2.6ZM9 9h4.4v4.4H9Z"},et={operation:"function",data:"dataset",model:"model",state:"layer",objective:"loss",optimizer:"optimizer",evaluation:"eval_loop",metric:"metric",output:"external",config:"config",loop:"train_loop",branch:"split",group:"group",entrypoint:"entrypoint",artifact:"artifact",op:"function",step:"function",function:"function",method:"function",compute:"function",computation:"function",process:"function",construction:"function",initialization:"function",hook:"function",callback:"function",forward:"function",class:"class",component:"class",dataset:"dataset","data source":"dataset",datasource:"dataset",input:"dataset",inputs:"dataset",batch:"dataset",dataloader:"dataloader","data loader":"dataloader",loader:"dataloader",batching:"dataloader",sampler:"dataloader","data pipeline":"dataloader",split:"split","data split":"split",decision:"split",conditional:"split",condition:"split",selection:"split",control:"split","control flow":"split",preprocessing:"transform",preprocess:"transform",postprocessing:"transform",transform:"transform",transformation:"transform",augmentation:"augment",augment:"augment",network:"model",module:"model",architecture:"model",layer:"layer","state update":"layer",update:"layer","state init":"layer",variable:"layer",buffer:"layer",parameters:"layer",weights:"layer",loss:"loss",criterion:"loss",optimiser:"optimizer",optimization:"optimizer",optimisation:"optimizer","optimizer step":"optimizer","optimization step":"optimizer",scheduler:"scheduler",schedule:"scheduler","learning rate":"scheduler",scaler:"scaler","training loop":"train_loop","train loop":"train_loop","optimization loop":"train_loop",epoch:"train_loop","training run":"train_loop",eval:"eval_loop",validation:"eval_loop",test:"eval_loop","evaluation loop":"eval_loop",metrics:"metric",score:"metric",scoring:"metric",log:"tracker",logging:"tracker",logger:"tracker",tracker:"tracker",history:"tracker",checkpoint:"checkpoint",checkpointing:"checkpoint",persistence:"checkpoint",save:"checkpoint",outputs:"external",result:"external",return:"external",display:"external",external:"external",configuration:"config",settings:"config",hyperparameters:"config",arguments:"config",args:"config",environment:"config","entry point":"entrypoint",entry:"entrypoint",launcher:"entrypoint","api entry":"entrypoint",main:"entrypoint",script:"entrypoint",file:"artifact",inference:"predict",predict:"predict",prediction:"predict",sampling:"predict",generation:"predict",phase:"group",stage:"group"};function Uh(t){return String(t||"").trim().toLowerCase().replace(/[\s_-]+/g," ")}function Xh(t){let e=Uh(t||"");if(!e||e==="unknown")return"step";if(et[e])return et[e];let n=e.split(/[^a-z0-9]+/).filter(Boolean),o=n[n.length-1];if(o&&et[o])return et[o];let i=n[0];return i&&et[i]?et[i]:"step"}function tt(t,e){return e?"group":Xh(t.kind)}function fn(t){return Xo[t]||qh[t]||Xo.unknown}function Yo(t){return Object.prototype.hasOwnProperty.call(Xo,t)}function Zo(t,e=16){let n=R("svg",{class:"mlv-icon",viewBox:"0 0 16 16",width:e,height:e,"aria-hidden":"true",focusable:"false"}),o=document.createElementNS(he,"path");return fe(o,{d:fn(t),fill:"none",stroke:"currentColor","stroke-width":"1.3","stroke-linecap":"round","stroke-linejoin":"round","vector-effect":"non-scaling-stroke"}),n.appendChild(o),n}var Pr={search:"M7 2.6a4.4 4.4 0 1 0 0 8.8 4.4 4.4 0 0 0 0-8.8ZM10.2 10.2 13.6 13.6",more:"M3.4 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2ZM8 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2ZM12.6 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2Z",close:"M3.6 3.6 12.4 12.4M12.4 3.6 3.6 12.4",chevron:"M6 3.6 10.4 8 6 12.4",plus:"M8 3.2v9.6M3.2 8h9.6",minus:"M3.2 8h9.6",fit:"M2.6 6V2.6H6M10 2.6h3.4V6M13.4 10v3.4H10M6 13.4H2.6V10",rail:"M2.4 3h11.2v10H2.4ZM9.6 3v10",open:"M9 2.8h4.2V7M13.2 2.8 7.4 8.6M11.6 9.4v3.8H2.8V4.4h3.8",target:"M8 2.6v2.2M8 11.2v2.2M2.6 8h2.2M11.2 8h2.2M8 5.2A2.8 2.8 0 1 0 8 10.8 2.8 2.8 0 0 0 8 5.2Z",warning:"M8 2.2 14.4 13.4H1.6ZM8 6.4v3.4M8 11.6v.1",info:"M8 2.2a5.8 5.8 0 1 0 0 11.6A5.8 5.8 0 0 0 8 2.2ZM8 7.2v4M8 5v.1",notobserved:"M2.6 5V2.6H5M7 2.6h2M11 2.6h2.4V5M13.4 7v2M13.4 11v2.4H11M9 13.4H7M5 13.4H2.6V11M2.6 9V7",flow:"M2.4 8h8.4M8.4 5.2 11.6 8l-3.2 2.8M13.2 6.4v3.2",legend:"M2.4 3.6h2.4v2.4H2.4ZM2.4 10h2.4v2.4H2.4ZM6.8 4.8h6.8M6.8 11.2h6.8",copy:"M5.8 5.8h7.6v7.6H5.8ZM2.6 10.2V2.6h7.6v3.2",minimap:"M2.4 3.6h11.2v8.8H2.4ZM8.4 7.4h4.4v4.4H8.4Z",image:"M2.4 3.4h11.2v9.2H2.4ZM2.4 10.6 5.8 7.4l2.4 2.2 2.2-2 3.2 3M10.3 5.3a1.05 1.05 0 1 0 0 2.1 1.05 1.05 0 0 0 0-2.1Z",keyboard:"M1.8 4.2h12.4v7.6H1.8ZM4.2 6.6h.1M6.7 6.6h.1M9.2 6.6h.1M11.7 6.6h.1M4.8 9.4h6.4"};function D(t,e=14){let n=R("svg",{class:"mlv-uicon",viewBox:"0 0 16 16",width:e,height:e,"aria-hidden":"true",focusable:"false"}),o=document.createElementNS(he,"path");return fe(o,{d:Pr[t]||Pr.close,fill:"none",stroke:"currentColor","stroke-width":"1.4","stroke-linecap":"round","stroke-linejoin":"round"}),n.appendChild(o),n}var Yh=240;function re(t){return t==="inferred"?"inferred":t==="unresolved"?"? unresolved":""}function Zh(t,e){return t==="inferred"?"Inferred, not observed: reasoned from the cited code and stated assumptions; the quotes do not show all of it.":"Unresolved: the evidence does not settle this claim. It does not mean the "+e+" is missing."}function nt(t,e="step"){let n=re(t);if(!n||!t)return null;let o=f("span","mlv-basis-tag",n);return o.setAttribute("data-basis",t),o.setAttribute("aria-hidden","true"),o.title=Zh(t,e),o}function Fr(t){if(!t.stale)return"";let e=t.staleQuotes,n=t.staleElsewhere?"a file in another folder":"a changed or missing file";return e?e.stale+" of "+e.total+(e.total===1?" quote cites ":" quotes cite ")+n:"cites "+n}function Br(t){return"mlv-n-"+t.replace(/[^A-Za-z0-9-]/g,e=>"_"+e.charCodeAt(0).toString(16)+"_")}function Qo(t){return t.stage||"unknown"}function mn(t){return t.sublabel?t.sublabel:t.kind&&t.kind!=="unknown"&&Yo(t.kind)?t.kind.replace(/_/g," "):""}function Qh(t){return t.kind==="unknown"||!Yo(t.kind)?"step":t.kind.replace(/_/g," ")}function yt(t){let e=t.node,n=[];n.push(Qh(e)+" "+e.label),n.push(e.phaseLabel?e.phaseLabel+" phase":Qo(e)+" stage"),e.loc.file&&n.push(Rt(e.loc));let o=He(e.basis);o&&n.push(o.slice(2));let i=J(t.counts),s=Y(t.counts);i>0&&n.push(i+(i===1?" finding":" findings")+", highest severity "+s),t.descendants>0&&n.push(t.descendants+(t.descendants===1?" nested step":" nested steps")),t.stale&&n.push(Fr(t));let r=e.detail?gn(e.detail):"";return n.join(", ")+"."+(r?" "+r:"")}var pn=160,$h=new Set(["etc","vs","cf","approx","incl","esp","resp","fig","figs","eq","eqs","al"]);function Jh(t,e){if(t[e]!==".")return!1;let n=/[A-Za-z.]*$/.exec(t.slice(0,e)),o=(n?n[0]:"").toLowerCase();return o?/^(?:[a-z]\.)+[a-z]$/.test(o)?!0:$h.has(o):!1}function gn(t){let e=t.replace(/\s+/g," ").trim();if(!e)return"";let n=/[.!?](?=\s|$)/g;for(let s=n.exec(e);s&&s.indexpn/2?o.slice(0,i):o).replace(/[\s,;:]+$/,"")+"…"}function $o(t){return t.detail&&t.detail.trim()&&Bo(t)?2:1}function Dr(t,e){let n=t.node,o=e,i=f("div","mlv-node");i.id=Br(n.id),i.setAttribute("role","button"),i.setAttribute("tabindex","-1"),i.setAttribute("data-node-id",n.id),i.setAttribute("data-stage",Qo(n)),i.setAttribute("data-kind",n.kind),i.setAttribute("data-level",n.level),n.basis&&i.setAttribute("data-basis",n.basis),t.phase!==void 0&&se(i,t.phase);let s=Y(t.counts);s&&i.setAttribute("data-sev",s),i.setAttribute("aria-label",yt(t)),i.style.left=t.box.x+"px",i.style.top=t.box.y+"px",i.style.width=t.box.w+"px",i.style.height=t.box.h+"px",s&&i.classList.add("has-issues"),t.stale&&i.classList.add("is-stale"),o&&i.classList.add("is-collapsed-group"),g(i,f("div","mlv-node__rail"));let r=g(i,f("div","mlv-node__main"));g(r,f("div","mlv-node__iconbox")).appendChild(Zo(tt(n,o)));let a=g(r,f("div","mlv-node__text")),d=n.label||n.qualname||n.id;g(a,f("div","mlv-node__title mlv-node__title--wrap",d)).setAttribute("data-lines",String(Fo(n,t.box.w)));let u=o?1:$o(n),h=g(a,f("div","mlv-node__sub",mn(n).slice(0,Yh)));if(u===2?h.setAttribute("data-lines","2"):n.loc.file&&g(a,Oe("mlv-node__loc",n.loc,"div")),o){let m=g(a,f("div","mlv-node__chips"));g(m,f("span","mlv-chip",Pe(t.descendants)))}t.stale&&i.appendChild(Gr(t));let p=nt(n.basis,o?"group":"step");if(p&&i.appendChild(p),o){let m=Ht(t.counts,14);m&&(m.classList.add("mlv-node__cluster"),m.style.position="absolute",m.style.top="-8px",m.style.right="-6px",m.style.padding="1px 6px",m.style.borderRadius="var(--mlv-r-pill)",m.style.background="var(--mlv-surface)",m.style.boxShadow="var(--mlv-sh-1)",i.appendChild(m))}else{let m=Vi(t.counts,18,t.findings||[]);m&&i.appendChild(m)}return i}function Pe(t){return t+(t===1?" step":" steps")}function xt(t){return t+(t===1?" finding touches this phase":" findings touch this phase")}function Jo(t){let e=J(t),n=Y(t);return e+(e===1?" finding touches":" findings touch")+" this phase, highest severity "+(n||"none")+". A finding that cites steps or connections in several phases counts in each of them."}function jr(t){let e=t.node,n=f("div","mlv-group");n.id=Br(e.id),n.setAttribute("data-node-id",e.id),n.setAttribute("data-group","1"),n.setAttribute("data-stage",Qo(e)),t.phase!==void 0&&se(n,t.phase),e.basis&&n.setAttribute("data-basis",e.basis),n.setAttribute("data-depth",String(Math.min(2,t.box.depth)));let o=Y(t.counts);o&&n.setAttribute("data-sev",o),n.style.left=t.box.x+"px",n.style.top=t.box.y+"px",n.style.width=t.box.w+"px",n.style.height=t.box.h+"px";let i=f("div","mlv-group__header");i.setAttribute("role","button"),i.setAttribute("tabindex","-1"),i.setAttribute("aria-expanded","true"),i.setAttribute("aria-label",yt(t)+" Group, expanded. Double-click to collapse.");let s=f("button","mlv-group__chevron-btn");s.type="button",s.tabIndex=-1,s.setAttribute("aria-label","Collapse "+(e.label||e.qualname));let r=D("chevron",12);r.setAttribute("class","mlv-uicon mlv-group__chevron"),s.appendChild(r),i.appendChild(s),i.appendChild(Zo(tt(e,!1),14)),g(i,f("span","mlv-group__name",e.label||e.qualname));let l=nt(e.basis,"group");l&&i.appendChild(l),g(i,f("span","mlv-group__count",Pe(t.descendants)));let a=Ht(t.counts,13);return a&&i.appendChild(a),t.stale&&(n.classList.add("is-stale"),i.appendChild(Gr(t))),n.appendChild(i),n}function Gr(t){let e=f("span","mlv-node__stale");return e.setAttribute("data-stale","1"),e.setAttribute("aria-hidden","true"),e.title=Fr(t)+(t.staleElsewhere?". Its jumps are blocked; the notice above says which folder to add.":" since publishing. Its jumps are blocked."),e.appendChild(D("warning",12)),e}function zr(t,e,n,o){let i=f("div","mlv-lane");i.setAttribute("data-lane-id",t.id),i.setAttribute("data-stage",t.id),o!==void 0&&se(i,o),i.style.left=t.x+"px",i.style.top=t.y+"px",i.style.width=t.w+"px",i.style.height=t.h+"px",i.style.setProperty("--mlv-lane-header-h",t.headerH+"px"),n&&i.classList.add("is-absent");let s=g(i,f("div","mlv-lane__header"));if(o!==void 0){let d=g(s,f("span","mlv-lane__num",String(o+1)));d.title="Phase "+(o+1)}let r=g(s,f("span","mlv-lane__label",t.label));r.title=o!==void 0?"Phase "+(o+1)+": "+t.label:t.label,g(s,f("span","mlv-lane__count",Pe(t.nodeCount))),g(s,f("span","mlv-lane__spacer"));let l=Jo(e),a=Ht(e,13,l);if(a){s.appendChild(a);let d=g(s,f("span","mlv-lane__unit",xt(J(e))));d.setAttribute("aria-hidden","true"),d.title=l}return i}function Wr(t){let e=new Map,n=[];for(let i of t){if(!ef(i))continue;let s=i.trunk.key,r=e.get(s);r?r.push(i):(e.set(s,[i]),n.push(s))}let o=[];for(let i of n){let s=e.get(i);if(s.length<2)continue;s.sort((l,a)=>l.trunk.index-a.trunk.index||sf(l.id,a.id));let r=s[0].trunk.axis==="y"?tf(i,s):nf(i,s);for(let l of r)o.push(l)}return o}function ef(t){let e=t.trunk;return!e||e.joinFrom<1||e.joinTo<=e.joinFrom?!1:e.joinTo+1<=t.points.length-1}function tf(t,e){let n=e[0].trunk,o=n.channelX,i=1/0,s=1/0;for(let d of e)i=Math.min(i,d.points[d.trunk.joinFrom-1].x),s=Math.min(s,d.points[d.trunk.joinTo+1].x);if(Math.abs(n.exitY-n.entryY){let a=r.points[r.trunk.joinFrom].x,d=r.points[r.trunk.joinTo].x;return{i:l,lo:Math.min(a,d),hi:Math.max(a,d)}}),i=[],s=0;for(let r of of(o)){if(r.length<2)continue;let l=1/0,a=-1/0;for(let h of r)l=Math.min(l,h.lo),a=Math.max(a,h.hi);if(!(a-l>=ao))continue;let d=r.slice().sort((h,p)=>h.i-p.i).map(h=>e[h.i]),c=[oe(l,n.entryY),oe(a,n.entryY)],u=[];for(let h of d){let p=h.trunk.joinFrom,m=wt(h.points.slice(0,p+1).concat([oe(h.points[p].x,n.entryY)]));u.push({id:h.id,side:"in",points:m,d:ve(m,le)})}for(let h of d){let p=h.trunk.joinTo,m=wt([oe(h.points[p].x,n.entryY)].concat(h.points.slice(p)));u.push({id:h.id,side:"out",points:m,d:ve(m,le)})}i.push(Kr(t,s++,d,n,c,u,oe((l+a)/2,n.entryY)))}return i}function of(t){let e=t.slice().sort((s,r)=>s.lo-r.lo||s.hi-r.hi||s.i-r.i),n=[],o=[],i=-1/0;for(let s of e)o.length&&s.lo>i&&(n.push(o),o=[]),o.push(s),i=o.length===1?s.hi:Math.max(i,s.hi);return o.length&&n.push(o),n}function Kr(t,e,n,o,i,s,r){let l=[],a=0;for(let d of n)a+=d.ids.length,l.indexOf(d.kind)<0&&l.push(d.kind);return{id:"bundle:"+t+(e?"#"+e:""),sourceLane:o.sourceLane,targetLane:o.targetLane,part:e,memberIds:n.map(d=>d.id),count:n.length,edgeCount:a,axis:o.axis,trunk:i,d:ve(i,le),spurs:s.filter(d=>d.points.length>1),badge:r,kinds:l}}function wt(t){let e=[];for(let n of t){let o=e[e.length-1];o&&Math.abs(o.x-n.x)<.01&&Math.abs(o.y-n.y)<.01||e.push(n)}return e}function sf(t,e){return te?1:0}function oe(t,e){return{x:Vr(t),y:Vr(e)}}function Vr(t){return Math.round(t*100)/100}function vn(t){let{index:e,frame:n,routes:o,keep:i}=t,s=[];for(let h of n.lanes)s.push({lane:h,counts:e.laneCounts(h.id,i),phase:e.phaseIndexOf(h.id)});let r=[],l=Array.from(n.boxes.values()).sort((h,p)=>h.depth-p.depth||lf(h.id,p.id));for(let h of l){let p=e.nodeById.get(h.id);if(!p)continue;let m=h.isGroup&&!h.collapsed,y=m||h.collapsed?e.subtreeIssues(h.id,i):e.issuesOf(h.id,i);r.push({expandedGroup:m,visual:{node:p,box:h,counts:e.countsFor(y),findings:y.map(S=>S.short).filter(Boolean),phase:e.phaseIndexOf(p.stage),descendants:e.descendantCount(h.id),...Ur(p.evidenceLocs,p.loc,t.staleFiles)}})}let a=[];for(let h of o){let p=e.issuesOfEdges(h.ids,i),m=e.nodeById.get(h.source),v=e.nodeById.get(h.target);a.push({route:h,severity:Y(e.countsFor(p)),findings:p.map(y=>y.short).filter(Boolean),phase:m?e.phaseIndexOf(m.stage):void 0,labelVisible:h.back,stage:m?m.stage:void 0,sourceLabel:m?m.label||m.qualname:void 0,targetLabel:v?v.label||v.qualname:void 0,mountSerial:t.mountSerial,placement:t.labels?t.labels.get(h.id):void 0,weight:Ji(h.ids),...af(h.ids,e,t.staleFiles),basis:rf(h.ids,e)})}let d=new Map;for(let h of a)d.set(h.route.id,h.severity);let c=new Map;for(let h of n.lanes)c.set(h.id,h.label);let u=[];for(let h of Wr(o)){let p=null;for(let m of h.memberIds){let v=d.get(m)||null;v&&(!p||Z.indexOf(v)e?1:0}function Ur(t,e,n){if(!n.size)return{stale:!1};let o=t||(e.file?[e]:[]),i=[];for(let r of o){let l=r.file?n.get(r.file):void 0;l&&i.push(l)}if(!i.length)return{stale:!1};let s={stale:!0,staleQuotes:{stale:i.length,total:o.length}};return ie(i)&&(s.staleElsewhere=!0),s}function af(t,e,n){if(!n.size)return{};let o=!1,i=!0;for(let s of t){let r=e.edgeById.get(s),l=r?Ur(r.evidenceLocs,r.loc,n):null;!l||!l.stale||(o=!0,l.staleElsewhere||(i=!1))}return o?i?{stale:!0,staleElsewhere:!0}:{stale:!0}:{}}function Xr(t,e){let n=vn(e),o=n.frame;H(t.lanes),H(t.nodes),H(t.bundleGroup),H(t.edgeGroup),H(t.connectors),t.world.style.width=o.width+"px",t.world.style.height=o.height+"px",t.edgesSvg.setAttribute("width",String(o.width)),t.edgesSvg.setAttribute("height",String(o.height)),t.edgesSvg.setAttribute("viewBox","0 0 "+o.width+" "+o.height);for(let l of n.lanes)t.lanes.appendChild(zr(l.lane,l.counts,!1,l.phase));let i=new Map;for(let l of n.nodes){let a=l.visual,d=l.expandedGroup?jr(a):Dr(a,a.box.collapsed);e.wireNode(d,a.node.id,l.expandedGroup),i.set(a.node.id,d),l.expandedGroup?t.lanes.appendChild(d):t.nodes.appendChild(d)}let s=new Map;for(let l of n.bundles){let a=Nr(l);t.bundleGroup.appendChild(a),s.set(l.bundle.id,a)}let r=new Map;for(let l of n.edges){let a=bs(l);e.wireEdge(a,l.route),t.edgeGroup.appendChild(a),r.set(l.route.id,a)}return{nodeEls:i,edgeEls:r,bundleEls:s,plan:n}}function Yr(t,e,n){let o=[];for(let i of e.boxes.values()){let s=t.nodeById.get(i.id);s&&o.push({x:i.x,y:i.y,w:i.w,h:i.h,stage:s.stage,phase:t.phaseIndexOf(s.stage),severity:Y(t.subtreeCounts(i.id,n))})}return o}var ot=.15,Et=2.5,df=1.2;function el(t,e,n,o,i=24){let s=(n-i*2)/Math.max(1,t),r=(o-i*2)/Math.max(1,e);return{zoom:ue(Math.min(s,r),ot,df)}}var Fe=.62,Zr=[.15,.2,.25,.32,.4,.5,.62,.8,1,1.25,1.6,2,2.5];function cf(t){if(!(t>0))return 1;let e=Zr[0],n=1/0;for(let o of Zr){let i=Math.abs(Math.log(t/o));i=Fe||!l)return{zoom:r,x:(e-i*r)/2,y:Math.max(o,(n-s*r)/2),mode:"whole"};let a=l.x-Math.max(0,t.channelW||0),d=Math.max(1,l.x+l.w-a),c=Math.max(1,l.h),u=Math.min((e-o*2)/d,(n-o*2)/c),h=u>=uf,p=ue(h?Math.min(u,_t):_t,ot,Et),m=i*p<=e?(e-i*p)/2:o-a*p,v=s*p<=n?(n-s*p)/2:o-l.y*p;return{zoom:p,x:m,y:v,mode:h?"phase-fit":"phase-anchor"}}var Qr=80,$r=.1,bn=.45,ff=1,yn=class{constructor(e,n,o){this.vp={x:0,y:0,zoom:1};this.contentW=1;this.contentH=1;this.lod="full";this.zBucket=0;this.fitted=!1;this.fitSize=null;this.frame=null;this.canvas=e,this.world=n,this.onChange=o}setContent(e,n,o=null){this.contentW=Math.max(1,e),this.contentH=Math.max(1,n),this.frame=o}apply(){let{x:e,y:n,zoom:o}=this.vp;this.world.style.transform="translate("+e+"px,"+n+"px) scale("+o+")";let i=o=Qr&&o>=this.fitSize.w*$r||i>=Qr&&i>=this.fitSize.h*$r)return this.applyFit(this.fitSize.padding,this.fitSize.whole),!0}return this.apply(),!1}visibleArea(){return this.size()}acceptResize(){if(!this.fitted||!this.fitSize)return;let{w:e,h:n}=this.size();this.fitSize={...this.fitSize,w:e,h:n}}fitsAt(e,n,o=48){let{w:i,h:s}=this.visibleArea();return Math.min((i-o)/Math.max(1,e.w),(s-o)/Math.max(1,e.h))>=n}frameRect(e,n,o,i=48){let{w:s,h:r}=this.visibleArea(),l=Math.min((s-i)/Math.max(1,e.w),(r-i)/Math.max(1,e.h)),a=this.vp.zoom;if(a>=Fe&&l>=a){this.centerOn(e);return}let d=a>=Fe?Math.max(ff,a):o,c=Math.max(bn,Math.min(d,l));this.centerOn(l>=bn?e:n,c)}centerOn(e,n){this.fitted=!1;let{w:o,h:i}=this.visibleArea();typeof n=="number"&&(this.vp.zoom=ue(n,ot,Et));let s=this.vp.zoom;this.vp.x=o/2-(e.x+e.w/2)*s,this.vp.y=i/2-(e.y+e.h/2)*s,this.apply()}revealRect(e,n=16){let{w:o,h:i}=this.visibleArea(),s=this.vp.zoom,r=(d,c,u)=>{let h=Math.max(0,Math.min(n,(u-c)/2));return c>u?-d:d<0?h-d:d+c>u?u-h-(d+c):0},l=r(e.x*s+this.vp.x,e.w*s,o),a=r(e.y*s+this.vp.y,e.h*s,i);(l||a)&&this.panBy(l,a)}zoomToBox(e,n=80){let{w:o,h:i}=this.visibleArea(),s=ue(Math.min((o-n)/Math.max(e.w,1),(i-n)/Math.max(e.h,1)),ot,Et);this.centerOn(e,s)}isVisible(e,n=this.visibleArea()){let{w:o,h:i}=n,s=e.x*this.vp.zoom+this.vp.x,r=e.y*this.vp.zoom+this.vp.y;return s>=0&&r>=0&&s+e.w*this.vp.zoom<=o&&r+e.h*this.vp.zoom<=i}};function ue(t,e,n){return tn?n:t}function Jr(t,e,n,o){let i=Math.max(1,n),s=Math.max(1,o),r=Math.min(t/i,e/s);return{scale:r,ox:(t-i*r)/2,oy:(e-s*r)/2,w:t,h:e,contentW:i,contentH:s}}function pf(t,e,n){return{x:ue((e-t.ox)/t.scale,0,t.contentW),y:ue((n-t.oy)/t.scale,0,t.contentH)}}function mf(t,e,n){return{x:t.ox+e*t.scale,y:t.oy+n*t.scale}}var xn=class{constructor(e,n){this.collapsedState=!1;this.w=200;this.h=130;this.fit=Jr(200,130,1,1);this.root=f("div","mlv-minimap"),this.root.setAttribute("aria-hidden","true"),this.toggleBtn=ne("mlv-btn mlv-btn--icon mlv-minimap__toggle","Collapse minimap"),this.toggleBtn.appendChild(D("chevron",12)),this.toggleBtn.setAttribute("aria-expanded","true"),this.toggleBtn.tabIndex=-1,_(this.toggleBtn,"click",o=>{o.preventDefault(),o.stopPropagation(),this.setCollapsed(!this.collapsedState),n&&n(this.collapsedState)}),this.root.appendChild(this.toggleBtn),this.svgEl=R("svg",{class:"mlv-minimap__svg",viewBox:"0 0 200 130","aria-hidden":"true"}),this.nodesG=R("g"),this.viewRect=R("rect",{class:"mlv-minimap__view",x:0,y:0,width:0,height:0,rx:2}),this.svgEl.appendChild(this.nodesG),this.svgEl.appendChild(this.viewRect),this.root.appendChild(this.svgEl),this.svgEl.addEventListener("pointerdown",o=>{let i=o,s=this.svgEl.getBoundingClientRect(),r=s.width?(i.clientX-s.left)/s.width*this.w:0,l=s.height?(i.clientY-s.top)/s.height*this.h:0,a=pf(this.fit,r,l);e(a.x,a.y)})}get collapsed(){return this.collapsedState}setCollapsed(e){this.collapsedState=e,e?this.root.classList.add("is-collapsed"):this.root.classList.remove("is-collapsed"),this.toggleBtn.setAttribute("aria-expanded",e?"false":"true");let n=e?"Expand minimap":"Collapse minimap";this.toggleBtn.title=n,this.toggleBtn.setAttribute("aria-label",n)}render(e,n,o){this.fit=Jr(this.w,this.h,n,o);let{scale:i,ox:s,oy:r}=this.fit;for(;this.nodesG.firstChild;)this.nodesG.removeChild(this.nodesG.firstChild);for(let l of e){let a=R("rect",{class:"mlv-minimap__node"+(l.severity?" mlv-minimap__node--issue":""),x:Se(s+l.x*i),y:Se(r+l.y*i),width:Se(Math.max(2,l.w*i)),height:Se(Math.max(2,l.h*i)),rx:1});a.setAttribute("data-stage",l.stage),l.phase!==void 0&&se(a,l.phase),l.severity&&a.setAttribute("data-sev",l.severity),this.nodesG.appendChild(a)}}setViewport(e,n,o){let{scale:i,ox:s,oy:r}=this.fit,l=mf(this.fit,-e.x/e.zoom,-e.y/e.zoom),a=n/e.zoom*i,d=o/e.zoom*i,c=ue(l.x,s,s+this.fit.contentW*i),u=ue(l.y,r,r+this.fit.contentH*i);this.viewRect.setAttribute("x",String(Se(c))),this.viewRect.setAttribute("y",String(Se(u))),this.viewRect.setAttribute("width",String(Se(Math.max(0,Math.min(a,s+this.fit.contentW*i-c))))),this.viewRect.setAttribute("height",String(Se(Math.max(0,Math.min(d,r+this.fit.contentH*i-u)))))}};function Se(t){return Math.round(t*100)/100}function gf(t,e){let n=new Set([e]),o=new Set,i=s=>{let r=[e],l=new Set([e]);for(;r.length;){let a=r.pop();for(let d of t){let c=s?d.source:d.target,u=s?d.target:d.source;c===a&&(o.add(d.id),!l.has(u)&&(l.add(u),n.add(u),r.push(u)))}}};return i(!0),i(!1),{nodes:n,edges:o}}function ei(t,e){let n=new Set([e]),o=new Set;for(let i of t)i.source!==e&&i.target!==e||(o.add(i.id),n.add(i.source),n.add(i.target));return{nodes:n,edges:o}}function vf(t,e,n){return n==="direct"?ei(t,e):gf(t,e)}function tl(t,e,n,o,i){for(let r of t.nodes.values())r.classList.remove("is-lit");for(let r of t.edges.values())r.classList.remove("is-lit");if(!n){t.canvas.classList.remove(o);return}let s=vf(e,n,i);for(let r of s.nodes){let l=t.nodes.get(r);l&&l.classList.add("is-lit")}for(let r of s.edges){let l=t.edges.get(r);l&&l.classList.add("is-lit")}t.canvas.classList.add(o)}var G={MAX_EDGES:120,PULSE_SPEED:320,PULSE_MIN_MS:380,PULSE_MAX_MS:2200,STREAM_SPEED:220,HEAD_PX:12,GAP_DATA:34,GAP_CALL:96,GAP_BACK:24,PERIOD_DATA:210,PERIOD_CALL:490,PERIOD_BACK:165,HOP_MS:90,HOP_MAX:6,PORT_R:3.5,CHARGE_HALO_R:9,CHARGE_GLOW_R:5.5,CHARGE_CORE_R:2.6,CHARGE_TWIN_PX:360,SETTLE_PASSES:2,SETTLE_MIN_MS:1600,SETTLE_MAX_MS:6e3},bf=["--mlv-flow-dur","--mlv-flow-len","--mlv-flow-head","--mlv-flow-gap","--mlv-flow-end","--mlv-flow-delay","--mlv-flow-iter"],yf=["is-flowing","is-flowing--pulse","is-flow-settled"];function nl(t){let e=0;for(let n=1;n0?Math.max(1,Math.round((t-e)/n)):1}function ol(t){return Math.min(t,G.HOP_MAX)*G.HOP_MS}function Mf(t){return t.kind!=="config"}function kf(t,e){let n=new Map([[e,0]]),o=new Map,i=s=>{let r=[e],l=new Set([e]),a=0;for(;r.length;){a++;let d=[];for(let c of r)for(let u of t){let h=s?u.source:u.target,p=s?u.target:u.source;if(h!==c)continue;let m=o.get(u.id);if((m===void 0||athis.pulse(e);let n=this.modeFor(1);if(this.host.canvas.setAttribute("data-flow",n),n==="off")return;let o=this.host.edges().get(e.id);if(!o)return;this.decorate(o,e,n,null,0),o.classList.add("is-flowing--pulse"),this.decorated.push(e.id),this.decoratedRoutes.set(e.id,e);let i=this.host.nodes(),s=i.get(e.source),r=i.get(e.target);s&&(s.classList.add("is-flow-source"),this.sourceId=e.source),r&&(r.classList.add("is-flow-target"),this.targetId=e.target)}stream(e,n){this.clear(),this.lastRun=()=>{this.stream(e,n)};let o=this.host.routes(),i=Lf(o,e,n),s=i.edges.size,r=this.modeFor(s);this.host.canvas.setAttribute("data-flow",r);let l=s>G.MAX_EDGES,a=l&&r!=="off";if(r==="off"||l)return{mode:r,capped:a,edges:s};let d=this.host.edges(),c=[],u=0,h=0;for(let m of o){let v=i.edges.get(m.id);if(v===void 0||!Mf(m))continue;let y=d.get(m.id);y&&(c.push({route:m,g:y,hop:v}),u=Math.max(u,nl(m.points||[])),h=Math.max(h,ol(v)))}let p=_f(u,h);for(let{route:m,g:v,hop:y}of c)this.decorate(v,m,r,y,p),v.classList.add("is-flowing"),this.decorated.push(m.id),this.decoratedRoutes.set(m.id,m);return{mode:r,capped:a,edges:s}}settle(e){let n=this.decoratedRoutes.get(e),o=this.host.edges().get(e);if(!n||!o||o.classList.contains("is-flow-settled"))return!1;let i=o.querySelectorAll(".mlv-edge__flow, .mlv-edge__charge");for(let s=0;s=2&&!o.querySelector(".mlv-edge__dir")&&o.appendChild(ll(n)),o.classList.add("is-flow-settled"),this.settled&&this.host.canvas.setAttribute("data-flow-settled","true"),!0}get settled(){if(!this.decorated.length)return!1;let e=this.host.edges();return this.decorated.every(n=>{let o=e.get(n);return!o||o.classList.contains("is-flow-settled")})}get flowing(){return this.decorated.length>0}replay(){return!this.lastRun||!this.decorated.length||this.modeFor(1)!=="motion"?!1:(this.lastRun(),this.decorated.length>0)}modeFor(e){return this.enabledFlag?this.motionFlag==="reduced"||e>G.MAX_EDGES?"static":"motion":"off"}decorate(e,n,o,i,s){let r=n.points||[];if(r.length<2)return;let l=nl(r),a=e.style;if(e.appendChild(rl("out",r[0])),e.appendChild(rl("in",r[r.length-1])),o==="static"){e.appendChild(ll(n));return}let d=R("path",{class:"mlv-edge__flow",d:n.d,pathLength:100});e.appendChild(d);let c=sl(G.HEAD_PX,l);if(a.setProperty("--mlv-flow-len",Me(l)+"px"),a.setProperty("--mlv-flow-head",String(c)),i===null){let u=xf(l);a.setProperty("--mlv-flow-dur",u+"ms"),a.setProperty("--mlv-flow-iter",String(G.SETTLE_PASSES));let h=l>0?Af(e):"";if(h){let p=If(e),m=il(h,u,0,p);e.appendChild(m),this.settleOnEnd(m,n.id),l>G.CHARGE_TWIN_PX&&e.appendChild(il(h,u,-Math.round(u/2),p))}}else{let u=wf(n.kind,n.subkind,n.back),h=sl(u,l);a.setProperty("--mlv-flow-gap",String(h)),a.setProperty("--mlv-flow-end",String(Me(-(c+h))));let p=ol(i);a.setProperty("--mlv-flow-delay",p+"ms"),a.setProperty("--mlv-flow-iter",String(Sf(s,p,Ef(n.kind,n.back)))),this.settleOnEnd(d,n.id)}}settleOnEnd(e,n){e.addEventListener("animationend",o=>{o.target===e&&this.settle(n)})}};function Af(t){let e=t.querySelector(".mlv-edge__path");return e&&e.getAttribute("id")||""}function If(t){try{let e=t,n=e.ownerSVGElement||(typeof e.closest=="function"?e.closest("svg"):null);if(!n||typeof n.getCurrentTime!="function")return null;let o=n.getCurrentTime();return typeof o=="number"&&isFinite(o)?o:null}catch{return null}}function il(t,e,n,o){let i=R("g",{class:n===0?"mlv-edge__charge":"mlv-edge__charge mlv-edge__charge--twin"});i.appendChild(R("circle",{class:"mlv-edge__charge-halo",cx:0,cy:0,r:G.CHARGE_HALO_R})),i.appendChild(R("circle",{class:"mlv-edge__charge-glow",cx:0,cy:0,r:G.CHARGE_GLOW_R})),i.appendChild(R("circle",{class:"mlv-edge__charge-core",cx:0,cy:0,r:G.CHARGE_CORE_R}));let s=R("animateMotion",{dur:e+"ms",repeatCount:String(G.SETTLE_PASSES),calcMode:"linear",rotate:"auto",fill:"freeze"});o!==null?s.setAttribute("begin",Cf(o+n/1e3)+"s"):n!==0&&s.setAttribute("begin",n+"ms");let r=R("mpath");return r.setAttribute("href","#"+t),r.setAttributeNS(Bi,"xlink:href","#"+t),s.appendChild(r),i.appendChild(s),i}function sl(t,e){return e>0?Me(Math.min(100,t/e*100)):100}function Me(t){return Math.round(t*100)/100}function Cf(t){return Math.round(t*1e3)/1e3}function rl(t,e){return R("circle",{class:"mlv-edge__port mlv-edge__port--"+t,cx:Me(e.x),cy:Me(e.y),r:G.PORT_R})}function ll(t){return R("path",{class:"mlv-edge__dir",d:"M -3.4 -3.6 L 0 0 L -3.4 3.6",fill:"none",transform:"translate("+Me(t.mid.x)+","+Me(t.mid.y)+") rotate("+Me(t.midAngle)+")"})}function al(t){for(let o of yf)t.classList.remove(o);let e=t.style;if(e&&typeof e.removeProperty=="function")for(let o of bf)e.removeProperty(o);let n=t.querySelectorAll(".mlv-edge__flow, .mlv-edge__charge, .mlv-edge__port, .mlv-edge__dir");for(let o=0;o"u"||!window.matchMedia)return!1;let e=window.matchMedia(t);return!!(e&&e.matches)}catch{return!1}}var En=class{constructor(e){this.stop=null;this.current=dl();let n=()=>{let i=dl();i!==this.current&&(this.current=i,e(i))},o=[];try{let i=typeof window<"u"&&window.matchMedia?window.matchMedia(cl):null;i&&typeof i.addEventListener=="function"&&(i.addEventListener("change",n),o.push(()=>i.removeEventListener("change",n)))}catch{}try{let i=typeof document<"u"?document.body:null;if(i&&typeof MutationObserver=="function"){let s=new MutationObserver(n);s.observe(i,{attributes:!0,attributeFilter:["class"]}),o.push(()=>s.disconnect())}}catch{}o.length&&(this.stop=()=>{for(let i of o)try{i()}catch{}})}get mode(){return this.current}destroy(){this.stop&&this.stop(),this.stop=null}};function Hf(t,e="full",n="lineage"){if(n==="direct"){let o="This card has "+t+" connections, ";return e==="reduced"?o+"more than the "+G.MAX_EDGES+" that can be marked at once. Hover a single connection to see its direction.":o+"past the "+G.MAX_EDGES+" the animation can carry. Hover a single connection to see its flow."}return e==="reduced"?"This lineage has "+t+" connections, more than the "+G.MAX_EDGES+" that can be marked at once. Hover a single connection, or walk them with e, to see each direction.":"This lineage has "+t+" connections, past the "+G.MAX_EDGES+" the animation can carry. Hover a single connection, or walk them with e, to see each flow."}var _n=class{constructor(e){this.latchedEdgeId=null;this.cappedKey=null;this.host=e,this.controller=new wn({canvas:e.canvas,edges:()=>e.edges(),nodes:()=>e.nodes(),routes:()=>e.routes()}),this.motionWatch=new En(n=>{this.controller.setMotion(n),this.controller.syncCanvas(),this.cappedKey=null,this.stop()}),this.controller.setMotion(this.motionWatch.mode),this.controller.syncCanvas()}get enabled(){return this.controller.enabled}get motion(){return this.controller.motion}setEnabled(e){this.controller.setEnabled(e),e&&this.stop()}syncCanvas(){this.controller.syncCanvas()}clear(){this.cappedKey=null,this.controller.clear()}pulse(e){this.controller.pulse(e)}replay(){return this.controller.replay()}stream(e,n){let o=this.controller.stream(e,n);if(!o.capped)return;let i=n+":"+e;this.cappedKey!==i&&(this.cappedKey=i,this.host.toast(Hf(o.edges,this.controller.motion,n)))}stop(){if(this.latchedEdgeId){let i=this.routeById(this.latchedEdgeId);if(i){this.controller.pulse(i);return}}let e=this.host.lockedNodeId();if(e){this.stream(e,"lineage");return}let n=this.host.hoverNodeId();if(n){this.stream(n,"direct");return}let o=this.host.hoverEdgeId();if(o){let i=this.routeById(o);if(i){this.controller.pulse(i);return}}this.controller.clear()}setLatchedEdge(e){this.latchedEdgeId=e}routeById(e){for(let n of this.host.routes())if(n.id===e)return n;return null}incidentRoutes(e){return this.host.routes().filter(n=>n.source===e||n.target===e)}focusEdge(e){let n=this.host.edges().get(e);if(!n)return!1;let o=n.querySelector(".mlv-edge__hit");if(!o||typeof o.focus!="function")return!1;try{o.focus()}catch{return!1}return!0}blurFocusedEdge(){let e=this.host.canvas.ownerDocument,n=e?e.activeElement:null;return!n||!n.classList||!n.classList.contains("mlv-edge__hit")||typeof n.blur!="function"?!1:(n.blur(),!0)}edgeAnnouncement(e){let n=this.host.index(),o=n?n.nodeById.get(e.source):null,i=n?n.nodeById.get(e.target):null,s=o?o.label||o.qualname:e.source,r=i?i.label||i.qualname:e.target;return"Connection: "+s+" to "+r+(e.label?", "+e.label:"")+"."}destroy(){this.motionWatch.destroy()}};function Nf(t,e,n){let o=n.x-e.x,i=n.y-e.y,s=t.x-e.x,r=t.y-e.y,l=o*o+i*i,a=l>0?(s*o+r*i)/l:0;a<0?a=0:a>1&&(a=1);let d=s-a*o,c=r-a*i;return d*d+c*c}function Pf(t,e){let n=t.points||[];if(n.length===0)return 1/0;if(n.length===1){let i=e.x-n[0].x,s=e.y-n[0].y;return Math.sqrt(i*i+s*s)}let o=1/0;for(let i=1;ithis.resolve(e))}enter(e){this.set(e)}leave(e){this.currentId===e.id&&this.set(null)}reset(){this.currentId=null,this.timer!==null&&(clearTimeout(this.timer),this.timer=null),this.host.hide()}destroy(){this.reset()}resolve(e){let n=this.host.routes();if(!n.length||this.host.canvas.classList.contains("is-panning"))return;let o=e.target;if(o&&typeof o.closest=="function"&&o.closest(Bf)){this.currentId&&this.set(null);return}let i=this.host.canvas.getBoundingClientRect(),s=this.host.viewport.vp,r=s.zoom>0?s.zoom:1,l={x:(e.clientX-i.left-s.x)/r,y:(e.clientY-i.top-s.y)/r},a=this.host.markers?this.host.markers():null,d=a&&a.size?pl(n,a,l,qe):null;this.set(d||fl(n,l,Ff/r))}set(e){let n=e?e.id:null;if(this.currentId===n)return;let o=this.host.edges();if(this.currentId){let i=o.get(this.currentId);i&&i.classList.remove("is-hover")}if(this.currentId=n,e){let i=o.get(e.id);if(i){i.classList.add("is-hover");let s=i.parentNode;s&&s.lastChild!==i&&s.appendChild(i)}}this.host.changed&&this.host.changed(e),this.intent(e)}intent(e){this.timer!==null&&(clearTimeout(this.timer),this.timer=null);let n=()=>{e?this.host.open(e):this.host.close()},o=e?this.host.openDelayMs:this.host.closeDelayMs;if(o<=0){n();return}this.timer=setTimeout(()=>{this.timer=null,n()},o)}};var Mn=12,Be=8;function Df(t,e,n,o,i){let s=o.w>0&&o.h>0,r=!1;if(s){let d=e-Mn-o.h>=Be,c=i.h<=0||n+Mn+o.h<=i.h-Be;r=!d&&c}let l=r?n+Mn:e-Mn;s&&(r?l=Math.min(l,Math.max(Be,i.h-Be-o.h)):l=Math.max(l,Be+o.h));let a=t;if(s&&i.w>0){let d=o.w/2,c=d+Be,u=i.w-d-Be;a=u>=c?Math.min(Math.max(t,c),u):i.w/2}return{left:Math.round(a),top:Math.round(l),below:r}}var kn=class{constructor(e,n){this.root=f("div","mlv-tooltip"),this.root.setAttribute("role","tooltip"),this.root.hidden=!0,this.viewport=e,this.bounds=n||(()=>({w:0,h:0}))}showNode(e,n,o,i){let s=e.nodeById.get(n);if(!s)return;H(this.root),g(this.root,f("div","mlv-tooltip__title",s.label||s.qualname));let r=re(s.basis);r&&g(this.root,f("div","mlv-tooltip__row mlv-tooltip__basis",r.replace("? ","")+", not observed")),s.sublabel&&g(this.root,f("div","mlv-tooltip__row",s.sublabel)),s.loc.file&&g(this.root,Oe("mlv-tooltip__loc",s.loc,"div"));let l=o.isGroup||o.collapsed;this.issueRows(l?e.subtreeIssues(n,i):e.issuesOf(n,i)),this.placeAt(o.x+o.w/2,o.y,o.y+o.h)}showEdge(e,n,o,i){let s=e.edgeById.get(n.id);H(this.root);let r=n.count<=1&&s&&s.label?s.label:"";g(this.root,f("div","mlv-tooltip__title",r||n.label||ft(n.kind)));let l=s&&s.authoredKind?" · authored as "+s.authoredKind:"";g(this.root,f("div","mlv-tooltip__row",ft(n.kind)+(n.subkind?" · "+n.subkind:"")+l));let a=s?re(s.basis):"";a&&g(this.root,f("div","mlv-tooltip__row mlv-tooltip__basis",a.replace("? ","")+", not observed")),s&&s.loc.file&&g(this.root,Oe("mlv-tooltip__loc",s.loc,"div")),n.count>1&&g(this.root,f("div","mlv-tooltip__row",n.count+" merged connections")),this.issueRows(e.issuesOfEdges(n.ids,o)),i?this.placeAt(i.x,i.y-qe,i.y+qe):this.placeAt(n.mid.x,n.mid.y,n.mid.y)}issueRows(e){for(let n of e){let o=g(this.root,f("div","mlv-tooltip__row"));o.appendChild(U(n.severity,12,"")),g(o,f("span",""," "+(n.short?n.short+" · ":"")+n.code+" "+n.title))}}placeAt(e,n,o){let i=this.viewport();this.root.hidden=!1,this.root.style.transform="translate(-50%, -100%)";let s=this.root.getBoundingClientRect(),r=Df(e*i.zoom+i.x,n*i.zoom+i.y,o*i.zoom+i.y,{w:s.width,h:s.height},this.bounds());this.root.style.left=r.left+"px",this.root.style.top=r.top+"px",this.root.style.transform=r.below?"translate(-50%, 0)":"translate(-50%, -100%)"}hide(){this.root.hidden=!0}};function ml(t){let e=f("div","mlv-state mlv-state--empty");e.setAttribute("role","status");let n=g(e,f("div","mlv-state__inner"));g(n,f("h2","mlv-state__title","No workflow steps")),g(n,f("p","mlv-state__body","This authored workflow does not contain any steps."));let o=t?t.diagnostics||[]:[];if(o.length){let i=g(n,f("ul","mlv-state__list"));for(let s of o.slice(0,8))g(i,f("li","",s.kind+" — "+s.message))}else t&&g(n,f("p","mlv-state__body","The author inspected "+t.workspace.filesAnalyzed+" files."));return e}function gl(t){let e=f("div","mlv-state mlv-state--empty");e.setAttribute("role","status");let n=g(e,f("div","mlv-state__inner"));g(n,f("h2","mlv-state__title","No steps match your filters"));let o=g(n,f("div","mlv-state__actions")),i=W("mlv-btn","Clear all filters");return _(i,"click",t),o.appendChild(i),e}var Ln=class{constructor(){this.timers=[];this.root=f("div","mlv-toasts"),this.root.setAttribute("role","status")}show(e){let n=f("div","mlv-toast",e);for(this.root.appendChild(n);this.root.childElementCount>3&&this.root.firstElementChild;)this.root.removeChild(this.root.firstElementChild);let o=setTimeout(()=>{n.parentNode===this.root&&this.root.removeChild(n)},4e3);this.timers.push(o)}destroy(){for(let e of this.timers)clearTimeout(e);this.timers=[],H(this.root)}};var jf=0,Gf=1,zf=2,Vf=100/3,ni=800,Wf=.999,xl=40,Kf=6;function qf(t,e=ni){let n=typeof t.deltaMode=="number"?t.deltaMode:jf,o=1;return n===Gf?o=Vf:n===zf&&(o=e>0?e:ni),{dx:vl(t.deltaX)*o,dy:vl(t.deltaY)*o,mode:n}}function vl(t){return typeof t=="number"&&isFinite(t)?t:0}function Uf(t,e){return t.ctrlKey||t.metaKey?"zoom":t.shiftKey?"pan":e.dx===0&&Math.abs(e.dy)>=xl?"zoom":"pan"}function Xf(t,e){let n=(t.ctrlKey||t.metaKey)&&Math.abs(e.dy){n.preventDefault();let o=t.getBoundingClientRect(),i=qf(n,o.height||t.clientHeight||ni);if(Uf(n,i)==="zoom"){e.zoomAt(Xf(n,i),n.clientX-o.left,n.clientY-o.top);return}let s=Yf(n,i);e.panBy(s.dx,s.dy)})}function bl(t,e){let n=t.x-e.x,o=t.y-e.y;return Math.sqrt(n*n+o*o)}function yl(t,e){return{x:(t.x+e.x)/2,y:(t.y+e.y)/2}}var An=class{constructor(e,n){this.points=new Map;this.lastDistance=0;this.lastMid=null;this.canvas=e,this.viewport=n}active(){return this.points.size>=2}down(e){this.points.set(e.pointerId,{x:e.clientX,y:e.clientY}),this.reseed()}up(e){this.points.delete(e.pointerId),this.reseed()}clear(){this.points.clear(),this.lastDistance=0,this.lastMid=null}move(e){if(!this.points.has(e.pointerId)||(this.points.set(e.pointerId,{x:e.clientX,y:e.clientY}),this.points.size<2))return!1;let n=this.pair();if(!n)return!1;let o=bl(n[0],n[1]),i=yl(n[0],n[1]),s=this.canvas.getBoundingClientRect();return this.lastDistance>0&&o>0&&(this.lastMid&&this.viewport.panBy(i.x-this.lastMid.x,i.y-this.lastMid.y),this.viewport.zoomAt(o/this.lastDistance,i.x-s.left,i.y-s.top)),this.lastDistance=o,this.lastMid=i,!0}pair(){let e=Array.from(this.points.keys()).sort((n,o)=>n-o);return e.length<2?null:[this.points.get(e[0]),this.points.get(e[1])]}reseed(){let e=this.pair();if(!e){this.lastDistance=0,this.lastMid=null;return}this.lastDistance=bl(e[0],e[1]),this.lastMid=yl(e[0],e[1])}};function El(t){let e=t.ownerDocument;return!e||!e.body||t.parentElement!==e.body?()=>{}:(e.documentElement.classList.add("mlv-fills-page"),e.body.classList.add("mlv-fills-page"),()=>{e.documentElement.classList.remove("mlv-fills-page"),e.body.classList.remove("mlv-fills-page")})}var Zf=0;function _l(t,e){t.classList.add("mlv-root"),t.setAttribute("data-theme",e),H(t);let n="mlv-shell"+ ++Zf,o=f("div","mlv-body"),i=f("main","mlv-main");i.setAttribute("aria-labelledby",n+"-main-heading");let s=g(i,f("h2","mlv-sr","Pipeline diagram"));s.id=n+"-main-heading";let r=f("div","mlv-canvas");r.id=n+"-canvas",r.setAttribute("role","application"),r.setAttribute("aria-label","ML pipeline diagram"),r.setAttribute("data-lod","full"),r.tabIndex=0,i.appendChild(r);let l=f("a","mlv-skiplink","Skip to diagram");l.href="#"+r.id,_(l,"click",w=>{w.preventDefault();try{r.focus()}catch{}}),t.appendChild(l);let a=g(r,f("div","mlv-world")),d=g(a,f("div","mlv-layer mlv-layer--lanes")),c=R("svg",{class:"mlv-layer mlv-layer--edges",width:1,height:1});c.appendChild(gs());let u=R("g",{class:"mlv-bundles"}),h=R("g",{class:"mlv-edges"}),p=R("g",{class:"mlv-connectors"});c.appendChild(u),c.appendChild(h),c.appendChild(p),a.appendChild(c);let m=g(a,f("div","mlv-layer mlv-layer--nodes")),v=f("div","mlv-zoom"),y=g(v,f("span","mlv-zoom__level","100%"));y.setAttribute("aria-label","Zoom level");let S=g(r,f("div","mlv-statehost")),x=f("div","mlv-sr");return x.setAttribute("aria-live","polite"),x.setAttribute("role","status"),{body:o,main:i,canvas:r,world:a,lanesLayer:d,edgesSvg:c,bundleGroup:u,edgeGroup:h,connectorLayer:p,nodesLayer:m,zoomBar:v,zoomLevel:y,stateHost:S,live:x}}function Qf(t){let e=t;return!!e.classList&&e.classList.contains("mlv-root")}function Sl(t,e,n){let o=[],i=new An(t,e),s=!1,r=0,l=0;o.push(_(t,"pointerdown",c=>{if(i.down(c),i.active()){s=!1,t.classList.remove("is-panning");return}let u=c.target;if(!(u.closest&&u.closest(".mlv-node, .mlv-group__header, .mlv-minimap, .mlv-zoom, .mlv-edge__hit, .mlv-legend, .mlv-statehost, .mlv-tooltip, .mlv-toasts"))){s=!0,r=c.clientX,l=c.clientY,t.classList.add("is-panning");try{t.setPointerCapture(c.pointerId)}catch{}}})),o.push(_(t,"pointermove",c=>{i.move(c)||s&&(e.panBy(c.clientX-r,c.clientY-l),r=c.clientX,l=c.clientY)}));let a=c=>{c&&i.up(c),s=!1,t.classList.remove("is-panning")};o.push(_(t,"pointerup",a)),o.push(_(t,"pointercancel",a)),o.push(_(t,"pointerleave",a)),o.push(wl(t,e)),o.push(_(t,"keydown",c=>n.onKeyDown(c)));let d=t.ownerDocument;return d&&o.push(_(d,"keydown",c=>{let u=c.target;u&&(u!==d.body&&u!==d.documentElement&&!Qf(u)||n.onKeyDown(c))})),o.push(_(t,"click",c=>{let u=c.target;(!u.closest||!u.closest(".mlv-node, .mlv-edge__hit, .mlv-group"))&&n.onBackgroundClick()})),typeof window<"u"&&o.push(_(window,"resize",()=>n.onResize?n.onResize():e.apply())),o.push(()=>i.clear()),o}function Ml(t,e,n,o,i){H(t);let s=i.nodeIds[0];if(!s)return 0;let r=n.boxes.get(e.visibleRepresentative(s,o));if(!r)return 0;let l=$(i.severity),a=0,d=new Set([r.id]);for(let c of i.nodeIds.slice(1)){let u=e.nodeById.get(c);if(!u)continue;let h=n.boxes.get(e.visibleRepresentative(c,o));!h||d.has(h.id)||(d.add(h.id),a++,t.appendChild(ys({x:r.x+r.w/2,y:r.y},{x:h.x+h.w/2,y:h.y},a,l,"Also affects — "+(u.label||u.id))))}return a}var In=class{constructor(e){this.hoverId=null;this.hoverTimer=null;this.pendingHover=null;this.focusLocked=!1;this.focusNodeId=null;this.focusShown=null;this.ctx=e}get hoverNodeId(){return this.hoverId}get lockedNodeId(){return this.focusLocked?this.focusShown??this.focusNodeId:null}get isFocusLocked(){return this.focusLocked}resetHover(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null),this.pendingHover=null,this.hoverId=null,this.trace(null,"is-tracing","direct"),this.ctx.tooltip.hide(),this.retraceFocus()}retraceFocus(){if(!this.focusLocked||!this.focusNodeId)return;let e=this.ctx,n=e.index(),o=n&&n.nodeById.has(this.focusNodeId)?n.visibleRepresentative(this.focusNodeId,e.collapsed()):null;if(o&&e.nodeEls().has(o)){this.focusShown=o,e.canvas.classList.remove("is-tracing"),this.trace(o,"is-focusing","lineage");return}this.focusLocked=!1,this.focusNodeId=null,this.focusShown=null,e.canvas.classList.remove("is-focusing"),this.trace(null,"is-focusing","lineage"),e.announce("Focus mode off.")}applySelection(e){let n=this.ctx;for(let o of n.nodeEls().values())o.classList.remove("is-selected");for(let o of n.edgeEls().values())o.classList.remove("is-selected");if(H(n.connectors),n.flow.setLatchedEdge(e&&e.kind==="edge"?e.id:null),!e){n.canvas.removeAttribute("aria-activedescendant"),this.retraceFocus(),n.flow.stop(),n.syncBundles();return}if(e.kind==="node"){let o=n.nodeEls().get(e.id);o&&(o.classList.add("is-selected"),n.canvas.setAttribute("aria-activedescendant",o.id))}else if(e.kind==="edge"){let o=n.edgeEls().get(e.id);o&&o.classList.add("is-selected")}else e.kind==="issue"&&this.highlightIssue(e.id);this.focusLocked&&e.kind==="node"?(this.focusNodeId=e.id,this.retraceFocus()):this.focusLocked&&this.trace(null,"is-focusing","lineage"),n.flow.stop(),n.syncBundles()}highlightIssue(e){let n=this.ctx,o=n.index(),i=n.frame();if(!o||!i)return;let s=o.issueById.get(e);if(!s)return;let r=s.nodeIds[0];if(r){let l=n.nodeEls().get(o.visibleRepresentative(r,n.collapsed()));l&&(l.classList.add("is-selected"),n.canvas.setAttribute("aria-activedescendant",l.id))}for(let l of s.edgeIds)for(let[a,d]of n.edgeEls())(d.getAttribute("data-edge-ids")||a).split(" ").indexOf(l)>=0&&d.classList.add("is-selected");Ml(n.connectors,o,i,n.collapsed(),s)}hoverIntent(e){if(this.hoverTimer!==null){if(this.pendingHover===e)return;clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null}if(this.hoverId===e)return;let n=e?400:120;if(n<=0){this.setHover(e);return}this.pendingHover=e,this.hoverTimer=setTimeout(()=>{this.hoverTimer=null,this.pendingHover=null,this.setHover(e)},n)}setHover(e){if(this.hoverId===e||(this.hoverId=e,this.focusLocked))return;let n=this.ctx;if(!e){this.trace(null,"is-tracing","direct"),n.tooltip.hide(),n.flow.stop(),n.syncBundles();return}this.trace(e,"is-tracing","direct"),n.flow.stream(e,"direct"),n.syncBundles();let o=n.index(),i=n.frame();if(!o||!i)return;let s=i.boxes.get(e);s&&n.tooltip.showNode(o,e,s,r=>n.keep(r))}trace(e,n,o){let i=this.ctx;tl({nodes:i.nodeEls(),edges:i.edgeEls(),canvas:i.canvas},i.routes(),e,n,o),i.syncBundles()}toggleFocusMode(e){let n=this.ctx;if(this.focusLocked){this.focusLocked=!1,this.focusNodeId=null,this.focusShown=null,n.canvas.classList.remove("is-focusing"),this.trace(null,"is-focusing","lineage"),n.flow.clear(),n.syncBundles(),n.announce("Focus mode off.");let o=this.hoverId;o&&(this.hoverId=null,this.setHover(o));return}if(!e||e.kind!=="node"){n.toast("Select a node first, then press F to focus.");return}this.focusLocked=!0,this.focusNodeId=e.id,this.retraceFocus(),this.focusLocked&&(n.flow.stream(this.focusShown??e.id,"lineage"),n.announce("Focus mode on."))}destroy(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null)}};function kl(t,e,n,o){let i=n?t.querySelector(".mlv-group__header"):t;if(!i)return;let s=t.querySelector(".mlv-group__chevron-btn");s&&(_(s,"click",d=>{d.preventDefault(),d.stopPropagation(),o.isGroup(e)&&o.toggleCollapse(e)}),_(s,"dblclick",d=>{d.preventDefault(),d.stopPropagation()}));let r=n||o.isGroup(e),l=null,a=()=>{l!==null&&(clearTimeout(l),l=null)};o.addDisposer(a),_(i,"click",d=>{if(d.stopPropagation(),!r){o.activateNode(e,d);return}a(),l=setTimeout(()=>{l=null,o.activateNode(e)},220)}),_(i,"dblclick",d=>{d.preventDefault(),d.stopPropagation(),a(),o.isGroup(e)?o.toggleCollapse(e):r||o.openNode(e,!1)}),_(i,"pointerenter",()=>o.hoverIntent(e)),_(i,"pointerleave",()=>o.hoverIntent(null)),_(i,"keydown",d=>{d.key==="Enter"&&!d.ctrlKey&&!d.metaKey&&!d.shiftKey?(d.preventDefault(),d.stopPropagation(),o.openNode(e,d.altKey)):d.key===" "&&o.isGroup(e)&&(d.preventDefault(),d.stopPropagation(),o.toggleCollapse(e))})}function Ll(t,e,n){let o=t.querySelector(".mlv-edge__hit");if(!o)return;let i=o;_(i,"click",s=>{s.stopPropagation(),n.activateEdge(e.id,s)}),_(i,"dblclick",s=>{s.preventDefault(),s.stopPropagation(),n.openEdge(e.id,!1)}),_(i,"pointerenter",()=>n.enterEdge(e)),_(i,"pointerleave",()=>n.leaveEdge(e)),_(i,"keydown",s=>{s.key!=="Enter"||s.ctrlKey||s.metaKey||s.shiftKey||(s.preventDefault(),s.stopPropagation(),n.openEdge(e.id,s.altKey))}),_(i,"focus",()=>{t.classList.add("is-hover"),n.syncBundles(),n.pulse(e)}),_(i,"blur",()=>{t.classList.remove("is-hover"),n.syncBundles(),n.stopFlow()})}var Cn=class{constructor(e,n){this.index=null;this.frameData=null;this.routes=[];this.labelPlan=null;this.collapsedSet=new Set;this.staleFiles=new Map;this.nodeEls=new Map;this.edgeEls=new Map;this.markerPoints=new Map;this.bundles=new hn;this.mountSerial=ps();this.disposers=[];this.lastArea=null;this.host=n,this.canvasEl=e.canvas,this.worldEl=e.world,this.lanesLayer=e.lanesLayer,this.edgesSvg=e.edgesSvg,this.bundleGroup=e.bundleGroup,this.edgeGroup=e.edgeGroup,this.connectorLayer=e.connectorLayer,this.nodesLayer=e.nodesLayer,this.zoomLevelEl=e.zoomLevel,this.stateHost=e.stateHost,this.tooltip=new kn(()=>this.viewport.vp,()=>this.viewport.size()),this.canvasEl.appendChild(this.tooltip.root),this.minimap=new xn((i,s)=>{let r=this.viewport.size();this.viewport.set({x:r.w/2-i*this.viewport.vp.zoom,y:r.h/2-s*this.viewport.vp.zoom})},i=>this.host.onMinimapCollapsed(i)),e.main.insertBefore(this.minimap.root,this.canvasEl),this.toasts=new Ln,this.canvasEl.appendChild(this.toasts.root),this.viewport=new yn(this.canvasEl,this.worldEl,i=>{this.zoomLevelEl.textContent=Math.round(i.zoom*100)+"%";let s=this.viewport.size();this.minimap.setViewport(i,s.w,s.h),this.lastArea=this.viewport.visibleArea(),this.host.onViewportChange(i)});for(let i of Sl(this.canvasEl,this.viewport,{onKeyDown:s=>this.host.onKeyDown(s),onBackgroundClick:()=>this.host.onBackgroundClick(),onResize:()=>this.handleResize()}))this.disposers.push(i);let o=typeof window<"u"?window.ResizeObserver:void 0;if(o){let i=new o(()=>this.handleResize());i.observe(this.canvasEl),this.disposers.push(()=>i.disconnect())}this.flow=new _n({canvas:this.canvasEl,edges:()=>this.edgeEls,nodes:()=>this.nodeEls,routes:()=>this.routes,index:()=>this.index,hoverNodeId:()=>this.emphasis.hoverNodeId,lockedNodeId:()=>this.emphasis.lockedNodeId,hoverEdgeId:()=>this.edgeHover.hoveredId,toast:i=>this.toasts.show(i)}),this.emphasis=new In({canvas:this.canvasEl,connectors:this.connectorLayer,flow:this.flow,tooltip:this.tooltip,nodeEls:()=>this.nodeEls,edgeEls:()=>this.edgeEls,routes:()=>this.routes,index:()=>this.index,frame:()=>this.frameData,collapsed:()=>this.collapsedSet,syncBundles:()=>this.syncBundles(),keep:i=>this.host.keep(i),announce:i=>this.host.announce(i),toast:i=>this.toasts.show(i)}),this.edgeHover=new Sn({canvas:this.canvasEl,viewport:this.viewport,routes:()=>this.routes,edges:()=>this.edgeEls,markers:()=>this.markerPoints,open:i=>{this.index&&(this.tooltip.showEdge(this.index,i,s=>this.host.keep(s),this.markerPoints.get(i.id)),this.flow.pulse(i))},close:()=>{this.emphasis.hoverNodeId||this.tooltip.hide(),this.flow.stop()},hide:()=>this.tooltip.hide(),changed:()=>this.syncBundles(),openDelayMs:400,closeDelayMs:120}),this.disposers.push(this.edgeHover.wire())}get frame(){return this.frameData}get collapsed(){return this.collapsedSet}setIndex(e){this.index=e}setCollapsed(e){this.collapsedSet=new Set(e)}get minimapCollapsed(){return this.minimap.collapsed}setMinimapCollapsed(e){this.minimap.setCollapsed(e)}setPanelWidth(e){e>0&&this.minimap.root.classList.toggle("is-narrow",e<=900)}minimapUnavailable(){let e=this.minimap.root;return e.hidden?"Shown when "+30+" or more cards are drawn":e.classList.contains("is-narrow")?"No room in a panel "+900+" px wide or narrower":e.classList.contains("is-short")?"No room in a canvas under "+350+" px tall":null}setStale(e){this.staleFiles=new Map(e.map(n=>[n.path,n.reason]))}nodeElement(e){return this.nodeEls.get(e)}relayout(){this.index&&(this.frameData=kr(this.index,this.collapsedSet),this.routes=Or(this.index,this.frameData,this.collapsedSet),this.labelPlan=hs(this.frameData,this.routes),this.viewport.setContent(this.frameData.width,this.frameData.height,this.frameData),this.render())}planInputs(){return!this.index||!this.frameData?null:{index:this.index,frame:this.frameData,routes:this.routes,labels:this.labelPlan?this.labelPlan.placements:null,mountSerial:this.mountSerial,keep:e=>this.host.keep(e),staleFiles:this.staleFiles}}scenePlan(){let e=this.planInputs();return e?vn(e):null}render(){let e=this.planInputs();if(!e)return;let n=this.nodeWiring(),o=this.edgeWiring(),i=Xr({world:this.worldEl,lanes:this.lanesLayer,edgesSvg:this.edgesSvg,bundleGroup:this.bundleGroup,edgeGroup:this.edgeGroup,connectors:this.connectorLayer,nodes:this.nodesLayer},{...e,wireNode:(s,r,l)=>kl(s,r,l,n),wireEdge:(s,r)=>Ll(s,r,o)});this.nodeEls=i.nodeEls,this.edgeEls=i.edgeEls,this.markerPoints=new Map;for(let s of i.plan.edges)s.severity&&this.markerPoints.set(s.route.id,go(s));this.bundles.adopt(i.bundleEls,i.plan.bundles.map(s=>s.bundle)),this.syncBundles(),this.emphasis.resetHover(),this.edgeHover.reset(),this.flow.clear(),this.renderMinimap(),this.renderEmptyState()}renderMinimap(){if(!this.index||!this.frameData)return;let e=Yr(this.index,this.frameData,n=>this.host.keep(n));this.minimap.render(e,this.frameData.width,this.frameData.height),this.minimap.root.hidden=e.length<30,this.syncShortCanvas()}syncShortCanvas(){let e=this.canvasEl.getBoundingClientRect().height;e>0&&this.minimap.root.classList.toggle("is-short",e<350)}handleResize(){let e=this.host.keptTarget(),n=e?this.targetRect(e):null,o=this.lastArea,i=!!(n&&o&&this.viewport.isVisible(n,o));n&&i?(this.viewport.acceptResize(),this.viewport.apply(),this.viewport.isVisible(n)||this.viewport.revealRect(n)):this.viewport.onResize(),this.syncShortCanvas()}afterSheetToggle(){this.viewport.acceptResize(),this.handleResize(),this.lastArea=this.viewport.visibleArea()}afterChromeChange(){this.viewport.refitIfFitted(),this.syncShortCanvas()}renderEmptyState(){let e=this.stateHost.querySelector(".mlv-state--empty");if(e&&this.stateHost.removeChild(e),!this.index)return;let n=this.index.graph;n.nodes.length===0?this.stateHost.appendChild(ml(n)):this.frameData&&this.frameData.boxes.size===0&&this.stateHost.appendChild(gl(()=>this.host.clearFilters()))}nodeWiring(){return{isGroup:e=>!!this.index&&this.index.isGroup(e),toggleCollapse:e=>this.toggleCollapse(e),hoverIntent:e=>this.emphasis.hoverIntent(e),activateNode:(e,n)=>this.host.activateNode(e,n),openNode:(e,n)=>this.host.openNode(e,n),addDisposer:e=>this.disposers.push(e)}}edgeWiring(){return{activateEdge:(e,n)=>this.host.activateEdge(e,n),openEdge:(e,n)=>this.host.openEdge(e,n),enterEdge:e=>this.edgeHover.enter(e),leaveEdge:e=>this.edgeHover.leave(e),syncBundles:()=>this.syncBundles(),pulse:e=>this.flow.pulse(e),stopFlow:()=>this.flow.stop()}}incidentRoutes(e){return this.flow.incidentRoutes(e)}focusEdge(e){return this.flow.focusEdge(e)}blurFocusedEdge(){return this.flow.blurFocusedEdge()}edgeAnnouncement(e){return this.flow.edgeAnnouncement(e)}applySelection(e){this.emphasis.applySelection(e)}hoverIntent(e){this.emphasis.hoverIntent(e)}setHover(e){this.emphasis.setHover(e)}get isFocusLocked(){return this.emphasis.isFocusLocked}toggleFocusMode(e){this.emphasis.toggleFocusMode(e)}syncBundles(){this.bundles.sync(this.edgeEls)}toggleCollapse(e){if(!this.frameData)return;let n=this.frameData.boxes.get(e),o=this.viewport.vp.zoom,i=n?{x:n.x*o+this.viewport.vp.x,y:n.y*o+this.viewport.vp.y}:null;this.collapsedSet.has(e)?this.collapsedSet.delete(e):this.collapsedSet.add(e),this.relayout();let s=this.frameData.boxes.get(e);s&&i&&this.viewport.set({x:i.x-s.x*this.viewport.vp.zoom,y:i.y-s.y*this.viewport.vp.zoom});let r=this.index?this.index.nodeById.get(e):null,l=this.index?this.index.descendantCount(e):0;this.host.afterCollapse(),this.host.announce("Group "+(r?r.label:e)+(this.collapsedSet.has(e)?" collapsed, "+l+(l===1?" step":" steps")+" hidden.":" expanded."))}overview(){if(!this.index)return 0;let e=[];for(let n of this.index.graph.nodes||[])this.index.isGroup(n.id)&&e.push(n.id);return this.collapsedSet=new Set(e),this.relayout(),this.viewport.fitWhole(),this.host.afterCollapse(),this.host.announce("Overview: "+e.length+(e.length===1?" group":" groups")+" collapsed, whole diagram fitted."),e.length}expandAncestors(e){if(!this.index)return!1;let n=!1;for(let o of this.index.ancestors(e))this.collapsedSet.delete(o)&&(n=!0);return n&&this.relayout(),n}centerOnNode(e,n){if(!this.index||!this.frameData)return;let o=this.index.visibleRepresentative(e,this.collapsedSet),i=this.frameData.boxes.get(o);if(!i||(this.viewport.centerOn(i),!n))return;let s=this.nodeEls.get(o);s&&(s.classList.add("is-pulse"),setTimeout(()=>s.classList.remove("is-pulse"),700))}revealNode(e,n){if(!this.index||!this.frameData)return;let o=this.frameData.boxes.get(this.index.visibleRepresentative(e,this.collapsedSet));if(o){if(this.viewport.vp.zoom>=Fe){this.centerOnNode(e,n);return}this.viewport.centerOn(o,Math.max(this.viewport.vp.zoom,this.readableZoomFor(o))),n&&this.pulseNode(this.index.visibleRepresentative(e,this.collapsedSet))}}keepInView(e){let n=this.targetRect(e);n&&!this.viewport.isVisible(n)&&this.viewport.centerOn(n)}refresh(e){let n=this.canvasEl.ownerDocument,o=n?n.activeElement:null,i=!!o&&o!==this.canvasEl&&this.canvasEl.contains(o)&&typeof o.closest=="function",s=i&&o.closest("[data-node-id]")?.getAttribute("data-node-id")||null,r=i&&!s&&o.classList.contains("mlv-edge__hit")&&o.closest("[data-edge-id]")?.getAttribute("data-edge-id")||null;this.render(),this.applySelection(e);let l=null;if(s){let a=this.nodeEls.get(s);l=a&&a.classList.contains("mlv-group")?a.querySelector(".mlv-group__header"):a}else r&&(l=this.edgeEls.get(r)?.querySelector(".mlv-edge__hit"));if(l)try{l.focus()}catch{}}frameIssue(e){if(!this.index||!this.frameData)return;let n=this.index.issueById.get(e);if(!n)return;let o=[],i=[];for(let d of n.nodeIds){let c=this.targetRect({kind:"node",id:d});c&&o.push(c)}for(let d of n.edgeIds){let c=this.targetRect({kind:"edge",id:d});c&&i.push(c)}let s=o.concat(i);if(!s.length)return;let r=d=>{let c=Math.min(...d.map(h=>h.x)),u=Math.min(...d.map(h=>h.y));return{x:c,y:u,w:Math.max(...d.map(h=>h.x+h.w))-c,h:Math.max(...d.map(h=>h.y+h.h))-u}},l=r(s),a=!o.length||!i.length||this.viewport.fitsAt(l,bn)?l:r(o);this.viewport.frameRect(a,s[0],_t),n.nodeIds[0]&&this.pulseNode(this.index.visibleRepresentative(n.nodeIds[0],this.collapsedSet))}revealEdge(e){let n=this.targetRect({kind:"edge",id:e});n&&(this.viewport.vp.zoomn.classList.remove("is-pulse"),700))}zoomStep(e){let n=this.viewport.size();this.viewport.zoomAt(e>0?1.2:1/1.2,n.w/2,n.h/2)}fit(){this.viewport.fit()}fitWhole(){this.viewport.fitWhole()}zoomToNode(e){if(!this.index||!this.frameData)return;let n=this.frameData.boxes.get(this.index.visibleRepresentative(e,this.collapsedSet));n&&this.viewport.zoomToBox(n)}nextSelection(e,n){if(!this.index||!this.frameData)return null;let o=e&&this.frameData.boxes.has(e)?e:null,i=o?Hr(this.index,this.frameData,o,n):Uo(this.frameData);return i?{id:i.id,visible:this.viewport.isVisible(i)}:null}labelOf(e){let n=this.nodeEls.get(e);return n?n.getAttribute("aria-label"):null}toast(e){this.toasts.show(e)}destroy(){this.emphasis.destroy(),this.edgeHover.destroy();for(let e of this.disposers)try{e()}catch{}this.disposers=[],this.flow.destroy(),this.toasts.destroy()}};var Tl='button, [role="button"], a[href], select';function Rl(t){return typeof t.getClientRects=="function"&&t.getClientRects().length>0}var Tn=class{constructor(e,n="input, textarea"){this.activeIndex=0;this.activeEl=null;this.disposers=[];this.container=e,this.ownStopSelector=n,this.disposers.push(_(e,"keydown",o=>this.onKey(o))),this.disposers.push(_(e,"focusin",o=>{let i=o.target;if(!i||typeof i.closest!="function")return;let r=this.items().indexOf(i);r>=0&&this.setActive(r,!1)})),this.sync()}items(){let e=this.container.querySelectorAll(Tl),n=Rl(this.container),o=[];for(let i=0;i=0?s:r>=0?r:this.activeEl?Math.min(this.activeIndex,e.length-1):0;this.activeIndex=l,(s>=0||this.activeEl)&&(this.activeEl=e[l]);for(let a=0;a"u")return!1;let t=navigator,e=t.userAgentData&&t.userAgentData.platform||t.platform||t.userAgent||"";return/mac/i.test(e)}catch{return!1}}function De(t,e=si()){return e?t.replace(/^Mod\+/,"⌘").replace(/^Ctrl\+/,"⌃").replace(/^Alt\+/,"⌥").replace(/^Shift\+/,"⇧"):t.replace(/^Mod\+/,"Ctrl+")}var Ol=[{id:"search",label:"Search steps and findings",icon:"search",keys:"Mod+F",folded:!0},{id:"about",label:"About this revision",icon:"info",folded:!0},{id:"exceptions",label:"Not observed",icon:"notobserved",check:!0,folded:!0},{id:"legend",label:"Legend",icon:"legend",keys:"L",check:!0,group:!0},{id:"flow",label:"Connection flow animation",icon:"flow",keys:"A",check:!0},{id:"minimap",label:"Overview map",icon:"minimap",check:!0},{id:"rail",label:"Side panel",icon:"rail",keys:"B",check:!0},{id:"fit",label:"Fit the whole diagram",icon:"fit",group:!0},{id:"zoomsel",label:"Zoom to the selection",icon:"target",keys:"Z"},{id:"svg",label:"Export SVG…",icon:"image",group:!0},{id:"png",label:"Export PNG…",icon:"image"},{id:"copy-svg",label:"Copy SVG",icon:"copy"},{id:"shortcuts",label:"Keyboard shortcuts",icon:"keyboard",keys:"?",group:!0}],Hl=["svg","png","copy-svg"],Jf=0,Rn=class{constructor(e,n=()=>{}){this.items=new Map;this.openState=!1;this.disposers=[];this.onPick=e,this.beforeOpen=n;let o="mlv-more"+ ++Jf;this.button=ne("mlv-btn mlv-btn--icon mlv-btn--more","More actions"),this.button.title="More: legend, flow, fit, export, shortcuts",this.button.appendChild(D("more",16)),this.button.setAttribute("aria-haspopup","menu"),this.button.setAttribute("aria-expanded","false"),this.button.setAttribute("aria-controls",o),_(this.button,"click",s=>{s.preventDefault(),s.stopPropagation(),this.setOpen(!this.openState)}),_(this.button,"keydown",s=>{if(s.key==="Escape"){if(!this.openState)return;s.preventDefault(),s.stopPropagation(),this.setOpen(!1);return}s.key!=="ArrowDown"&&s.key!=="ArrowUp"||(s.preventDefault(),s.stopPropagation(),this.setOpen(!0),this.focusItem(s.key==="ArrowDown"?0:this.shown().length-1))}),this.panel=f("div","mlv-moremenu"),this.panel.id=o,this.panel.setAttribute("role","menu"),this.panel.setAttribute("aria-label","More actions"),this.panel.hidden=!0;for(let s of Ol){if(s.group){let l=g(this.panel,f("div","mlv-moremenu__sep"));l.setAttribute("role","separator"),l.setAttribute("data-sep-before",s.id)}let r=f("button","mlv-moremenu__item");r.type="button",r.tabIndex=-1,r.setAttribute("role",s.check?"menuitemcheckbox":"menuitem"),r.setAttribute("data-more-item",s.id),Hl.indexOf(s.id)>=0&&r.setAttribute("data-export-action",s.id),s.folded&&r.setAttribute("data-folded","1"),r.appendChild(D(s.icon,14)),g(r,f("span","mlv-moremenu__label",s.label)),s.id==="minimap"&&(g(r,f("span","mlv-moremenu__note")).hidden=!0),s.keys&&g(r,f("kbd","mlv-moremenu__keys",De(s.keys))).setAttribute("aria-hidden","true"),_(r,"click",l=>{if(l.preventDefault(),r.disabled)return;let a=l.detail===0;this.setOpen(!1),this.onPick(s.id,a)}),this.panel.appendChild(r),this.items.set(s.id,r)}_(this.panel,"keydown",s=>this.onKey(s));let i=typeof document>"u"?null:document;i&&(this.disposers.push(_(i,"pointerdown",s=>{if(!this.openState)return;let r=s.target;r&&(this.panel.contains(r)||this.button.contains(r))||this.setOpen(!1)})),this.disposers.push(_(i,"keydown",s=>{if(!this.openState||s.key!=="Escape")return;s.preventDefault(),s.stopPropagation();let r=i.activeElement,l=!!r&&(this.panel.contains(r)||this.button.contains(r));this.setOpen(!1),l&&this.focusButton()},!0)))}get open(){return this.openState}update(e){for(let[o,i]of this.items){let s=Ol.find(l=>l.id===o),r=!s.folded||(o==="search"?e.folded.search:o==="about"?e.folded.about:e.folded.exceptions);if(i.hidden=!r,o==="about"){let l=i.querySelector(".mlv-moremenu__label");l&&(l.textContent=e.aboutLabel),i.title="Show About: the request, coverage, limitations and provenance"}if(o==="exceptions"){let l=i.querySelector(".mlv-moremenu__label");l&&(l.textContent=e.exceptionsLabel),i.title=e.exceptionsOn?"The observed claims are faded; choose to show them again":"Fade the observed claims so these stand out"}if(o==="rail"){let l=i.querySelector(".mlv-moremenu__label");l&&(l.textContent=e.railMode==="sheet"?"Bottom panel":"Side panel"),i.title=e.railMode==="sheet"?"Open or collapse the bottom panel":"Show or hide the side panel"}if(s.check){let l=o==="legend"?e.legendOpen:o==="flow"?e.flowOn:o==="minimap"?e.minimapShown&&!e.minimapUnavailable:o==="exceptions"?e.exceptionsOn:e.railOpen;i.setAttribute("aria-checked",l?"true":"false")}if(o==="minimap"){let l=i.querySelector(".mlv-moremenu__note");l&&(l.textContent=e.minimapUnavailable||"",l.hidden=!e.minimapUnavailable),i.disabled=!!e.minimapUnavailable,i.title=e.minimapUnavailable?"Overview map: "+e.minimapUnavailable.charAt(0).toLowerCase()+e.minimapUnavailable.slice(1):"Show or hide the overview map"}o==="zoomsel"&&(i.disabled=!e.hasSelection),Hl.indexOf(o)>=0&&(i.disabled=!e.canExport),i.disabled?i.setAttribute("aria-disabled","true"):i.removeAttribute("aria-disabled")}let n=this.shown()[0];for(let o of Array.from(this.panel.querySelectorAll(".mlv-moremenu__sep")))o.hidden=!!n&&o.getAttribute("data-sep-before")===n.getAttribute("data-more-item")}setOpen(e){if(this.openState!==e)if(this.openState=e,this.panel.hidden=!e,this.button.setAttribute("aria-expanded",e?"true":"false"),e){try{this.beforeOpen()}catch{}this.position(),this.focusItem(0)}else{let n=this.panel.ownerDocument?this.panel.ownerDocument.activeElement:null;n&&this.panel.contains(n)&&this.focusButton()}}shown(){return Array.from(this.items.values()).filter(e=>!e.hidden&&!e.disabled)}position(){let e=null;try{e=this.button.getBoundingClientRect()}catch{e=null}e&&(this.panel.style.position="fixed",this.panel.style.top=Math.round(e.bottom+4)+"px",this.panel.style.left="auto",this.panel.style.right=Math.max(8,Math.round(ep()-e.right))+"px")}focusItem(e){let n=this.shown();if(!n.length)return;let o=(e%n.length+n.length)%n.length;try{n[o].focus()}catch{}}focusButton(){try{this.button.focus()}catch{}}onKey(e){if(e.key==="Escape"){e.preventDefault(),e.stopPropagation(),this.setOpen(!1);return}if(e.key==="Tab"){this.setOpen(!1);return}let n=this.shown(),o=n.indexOf(e.target);if(!(o<0)){if(e.key==="ArrowDown")this.focusItem(o+1);else if(e.key==="ArrowUp")this.focusItem(o-1);else if(e.key==="Home")this.focusItem(0);else if(e.key==="End")this.focusItem(n.length-1);else return;e.preventDefault(),e.stopPropagation()}}destroy(){for(let e of this.disposers)try{e()}catch{}this.disposers=[]}};function ep(){try{if(typeof window<"u"&&window.innerWidth)return window.innerWidth}catch{}return 1280}var ri={mid:620,wide:1e3,full:1200};function tp(t){return t>0?tt+" "+(t===1?e:n);function Nl(t){let e=t.steps+t.connections+t.findings,n=[];return t.steps&&n.push(ke(t.steps,"step","steps")),t.connections&&n.push(ke(t.connections,"connection","connections")),t.findings&&n.push(ke(t.findings,"finding","findings")),e+" not observed ("+n.join(", ")+")"}function np(t){let e=t.coverage.limitations.length;return"Coverage: "+t.coverage.status+(e?" · "+ke(e,"limitation","limitations"):"")}var Pl=4;function op(t){let e=t.steps+t.connections+t.findings;return e+(e===1?" claim":" claims")+" not observed"}var ip=0,On=class{constructor(e,n){this.sevButtons=new Map;this.roving=null;this.layout="full";this.width=0;this.fitLevel=0;this.hasDocument=!1;this.hasExceptions=!1;this.sevShown=new Set;this.searchOpen=!1;this.lastAboutLabel="About this revision";this.state=null;this.cb=e;let o="mlv"+ ++ip;this.header=f("header","mlv-header"),this.header.setAttribute("data-layout",this.layout),this.title=g(this.header,f("h1","mlv-header__title","")),this.toolbar=g(this.header,f("div","mlv-header__bar")),this.toolbar.setAttribute("role","toolbar"),this.toolbar.setAttribute("aria-label","Diagram controls"),this.toolbar.setAttribute("aria-orientation","horizontal"),this.provenance=f("button","mlv-chip mlv-header__prov"),this.provenance.type="button",g(this.provenance,f("span","mlv-header__dot")).setAttribute("aria-hidden","true"),this.provHost=g(this.provenance,f("span","mlv-header__host","")),this.provRev=g(this.provenance,f("span","mlv-header__rev","")),_(this.provenance,"click",a=>e.onAbout(a.detail===0)),this.toolbar.appendChild(this.provenance),g(this.toolbar,f("span","mlv-header__spacer")),this.search=g(this.toolbar,f("div","mlv-search")),g(this.search,D("search",14)).setAttribute("class","mlv-uicon mlv-search__glyph");let s=g(this.search,f("label","mlv-sr","Search steps, findings, IDs, or cited text"));s.htmlFor=o+"-search-input",this.searchInput=g(this.search,f("input","mlv-input")),this.searchInput.id=o+"-search-input",this.searchInput.type="search",this.searchInput.placeholder="Search steps and findings",this.searchInput.setAttribute("role","combobox"),this.searchInput.setAttribute("aria-expanded","false"),this.searchInput.setAttribute("aria-controls",o+"-search-results"),this.searchInput.autocomplete="off",this.results=g(this.search,f("ul","mlv-search__results")),this.results.id=o+"-search-results",this.results.setAttribute("role","listbox"),this.results.setAttribute("aria-label","Search results"),this.results.hidden=!0,_(this.searchInput,"input",()=>e.onQuery(this.searchInput.value)),_(this.searchInput,"keydown",a=>e.onSearchKey(a)),_(this.searchInput,"blur",()=>{this.searchInput.value||this.setSearchOpen(!1)}),this.searchBtn=ne("mlv-btn mlv-btn--icon mlv-header__searchbtn","Search ("+De("Mod+F")+")"),this.searchBtn.appendChild(D("search",16)),this.searchBtn.setAttribute("aria-controls",this.searchInput.id),_(this.searchBtn,"click",()=>e.onSearchOpen()),this.toolbar.appendChild(this.searchBtn);for(let a of Z){let d=f("button","mlv-chip mlv-chip--btn mlv-chip--sev");d.type="button",d.setAttribute("aria-pressed","true"),d.setAttribute("data-severity",a),d.appendChild(U(a,14,"")),g(d,f("span","mlv-chip__count","0")),_(d,"click",()=>e.onSeverity(a)),this.sevButtons.set(a,d),this.toolbar.appendChild(d)}this.sevUnit=g(this.toolbar,f("span","mlv-header__unit","findings")),this.sevUnit.setAttribute("aria-hidden","true"),this.sevUnit.hidden=!0,this.exceptionsBtn=f("button","mlv-chip mlv-chip--btn mlv-chip--exceptions"),this.exceptionsBtn.type="button",this.exceptionsBtn.setAttribute("aria-pressed","false"),this.exceptionsBtn.hidden=!0,_(this.exceptionsBtn,"click",()=>e.onToggleExceptions(this.exceptionsBtn.getAttribute("aria-pressed")!=="true")),this.toolbar.appendChild(this.exceptionsBtn),this.more=new Rn((a,d)=>this.pick(a,d),()=>e.onMenuOpen()),this.toolbar.appendChild(this.more.button),this.refineSlot=g(this.toolbar,f("span","mlv-header__refine")),this.status=f("footer","mlv-status"),this.status.setAttribute("data-layout",this.layout),this.counts=g(this.status,f("span","mlv-status__counts","")),this.coverage=f("button","mlv-status__coverage"),this.coverage.type="button",this.coverage.hidden=!0,_(this.coverage,"click",a=>e.onAbout(a.detail===0)),this.status.appendChild(this.coverage),g(this.status,f("span","mlv-status__spacer")),this.fresh=g(this.status,f("span","mlv-status__freshness"));let r=ne("mlv-btn mlv-btn--icon mlv-zoom__btn","Zoom out");r.appendChild(D("minus",12)),_(r,"click",()=>e.onZoom(-1));let l=ne("mlv-btn mlv-btn--icon mlv-zoom__btn","Zoom in");l.appendChild(D("plus",12)),_(l,"click",()=>e.onZoom(1)),n.insertBefore(r,n.firstChild),n.appendChild(l),this.status.appendChild(n),this.roving=new Tn(this.toolbar)}destroy(){this.roving&&this.roving.destroy(),this.roving=null,this.more.destroy()}syncRoving(){this.fitRow()}setWidth(e){let n=tp(e),o=n!==this.layout;return o&&(this.layout=n,this.header.setAttribute("data-layout",n),this.status.setAttribute("data-layout",n),this.syncSearch()),(o||e!==this.width)&&(this.width=e,this.fitRow()),n}get headerFit(){return this.fitLevel}get headerLayout(){return this.layout}setSearchOpen(e){this.searchOpen=e,this.syncSearch(),this.fitRow()}syncSearch(){let n=!(this.layout==="mid"||this.layout==="narrow")||this.searchOpen||!!this.searchInput.value;this.header.setAttribute("data-search",n?"open":"closed"),this.searchBtn.setAttribute("aria-expanded",n?"true":"false"),this.syncControls()}syncControls(){this.provenance.hidden=!this.hasDocument||this.layout==="narrow"||this.fitLevel>=1;let e=(this.layout==="mid"||this.layout==="narrow")&&this.header.getAttribute("data-search")!=="open";this.searchBtn.hidden=!(this.layout==="mid"&&e),this.exceptionsBtn.hidden=!this.hasExceptions||this.fitLevel>=2;for(let[n,o]of this.sevButtons)o.hidden=!this.sevShown.has(n)||this.fitLevel>=4;this.sevUnit.hidden=this.sevShown.size===0||this.fitLevel>=4,this.header.setAttribute("data-fit",String(this.fitLevel))}fitRow(){this.fitLevel=0,this.syncControls();let e=this.header.clientWidth,n=this.header.getAttribute("data-search")==="open"&&(this.layout==="mid"||this.layout==="narrow")?Pl:Pl-1;if(e>0)for(;this.fitLevelthis.header.clientWidth;)this.fitLevel++,this.syncControls();this.updateMenu(),this.roving&&this.roving.sync()}updateMenu(){let e=this.state;if(!e)return;let n=e.notObserved,o=this.hasExceptions&&this.fitLevel>=2;this.more.update({folded:{search:this.layout==="narrow",about:this.layout==="narrow"||this.fitLevel>=1,exceptions:o},aboutLabel:this.lastAboutLabel,exceptionsLabel:this.hasExceptions?Nl(n):"",exceptionsOn:e.exceptionsOn,legendOpen:e.legendOpen,flowOn:e.flowOn,minimapShown:!e.minimapCollapsed,minimapUnavailable:e.minimapUnavailable,railOpen:e.railOpen,railMode:e.railMode,hasSelection:e.hasSelection,canExport:!!e.graph})}pick(e,n){let o=this.cb;e==="search"?o.onSearchOpen():e==="about"?o.onAbout(n):e==="exceptions"?o.onToggleExceptions(!(this.state&&this.state.exceptionsOn)):e==="legend"?o.onToggleLegend(!(this.state&&this.state.legendOpen)):e==="flow"?o.onToggleFlow(!(this.state&&this.state.flowOn)):e==="minimap"?o.onToggleMinimap(!(this.state&&this.state.minimapCollapsed)):e==="rail"?o.onToggleRail():e==="fit"?o.onFitWhole():e==="zoomsel"?o.onZoomToSelection():e==="shortcuts"?o.onShortcuts():o.onExport(e)}update(e){this.state=e;let n=e.document,o=e.graph;this.renderHeader(e,n);let i=e.notObserved,s=i.steps+i.connections+i.findings;if(this.hasExceptions=!!o&&s>0,s>0){let l=Nl(i),a=l.indexOf(" (");H(this.exceptionsBtn),this.exceptionsBtn.appendChild(D("notobserved",14)),this.layout==="full"&&a>0?(g(this.exceptionsBtn,f("span","mlv-chip__lead",l.slice(0,a))),g(this.exceptionsBtn,f("span","mlv-chip__detail",l.slice(a)))):g(this.exceptionsBtn,f("span","mlv-chip__lead",op(i))),this.exceptionsBtn.setAttribute("aria-label",l),this.exceptionsBtn.setAttribute("aria-pressed",e.exceptionsOn?"true":"false"),this.exceptionsBtn.title=l+(e.exceptionsOn?". The observed claims are faded; press to show them again.":". Press to fade the observed claims so these stand out.")}for(let l of Z){let a=this.sevButtons.get(l),d=e.filters.severities.indexOf(l)>=0,c=e.visibleCounts[l];a.setAttribute("aria-pressed",d?"true":"false");let u=a.querySelector(".mlv-chip__count");u&&(u.textContent=String(c)),o&&c>0?this.sevShown.add(l):this.sevShown.delete(l),a.title=ke(c,l+" finding",l+" findings")+(d?". Press to hide them.":", hidden. Press to show them."),a.setAttribute("aria-label",a.title)}let r=Z.filter(l=>this.sevShown.has(l));this.sevUnit.textContent=r.length===1&&e.visibleCounts[r[0]]===1?"finding":"findings",this.syncSearch(),this.renderStatus(e),this.fitRow()}renderHeader(e,n){if(!n){this.title.textContent="",this.title.removeAttribute("title"),this.hasDocument=!1;return}this.title.textContent=n.title,this.title.title=n.title,this.hasDocument=!0;let o=n.producer.host,i=n.producer.model?" ("+n.producer.model+")":"";this.provHost.textContent=o+" · ",this.provRev.textContent=n.revision.id;let s=!!e.freshness&&e.freshness.state==="stale";this.provenance.setAttribute("data-stale",s?"true":"false");let r=n.verification?"Published "+n.verification.publishedAt+" with hashes of "+ke(Object.keys(n.verification.files||{}).length,"file","files")+".":"Published without source hashes.",l=s&&e.freshness?" "+e.freshness.text+".":"";this.provenance.title="Revision "+n.revision.id+" by "+o+i+". "+r+l+" Show About: the request, coverage and provenance.",this.provenance.setAttribute("aria-label","Revision "+n.revision.id+", "+o+"."+l+" Show About this revision"),this.lastAboutLabel="About revision "+n.revision.id+" · "+o}renderStatus(e){let n=e.graph,o=e.document;if(!n){this.counts.textContent="Waiting for a workflow…",this.coverage.hidden=!0,H(this.fresh);return}if(this.counts.textContent=ke(n.nodes.length,"step","steps")+" · "+ke(n.edges.length,"connection","connections"),o){this.coverage.hidden=!1;let s=np(o);this.coverage.textContent=this.layout==="narrow"?s.replace(/^Coverage: /,""):s,this.coverage.setAttribute("aria-label",s);let l=o.coverage.status==="scoped"?'"scoped": the assistant lists no remaining work within the stated scope.':'"partial": the assistant lists work that remains.';this.coverage.title="Coverage as the assistant recorded it. "+l+" Show About, where the limitations are listed."}else this.coverage.hidden=!0;H(this.fresh);let i=e.freshness;if(this.fresh.setAttribute("data-stale",i&&i.state==="stale"?"true":"false"),i&&i.state==="stale"){let s=g(this.fresh,f("span","mlv-status__fresh is-warn"));s.appendChild(D("warning",12)),g(s,f("span","",i.text)),s.title=i.title,s.setAttribute("data-freshness","stale")}if(e.checking)g(this.fresh,f("span","mlv-status__fresh is-checking","Checking source freshness…")).setAttribute("data-freshness","checking");else if(i&&i.state!=="stale"){let s=g(this.fresh,f("span","mlv-status__fresh is-muted",i.text));s.title=i.title,s.setAttribute("data-freshness",i.state)}}};function St(t){let e=nt(t.basis,"finding");return e?(e.removeAttribute("aria-hidden"),e.classList.add("mlv-basis-tag--rail"),e):null}function Hn(t,e){if(!e)return[];let n=[];for(let o of[t.loc].concat(t.relatedLocs||[])){let i=o&&o.file?e(o.file):void 0;i&&n.indexOf(i)<0&&n.push(i)}return n}function it(t){return ie(t)?"cites a file in another folder":t.length===1&&t[0]==="changed"?"cites a changed file":t.indexOf("changed")<0?t.length===1?"cites a missing file":"cites missing files":"cites changed or missing files"}function Nn(t,e="div"){if(!(t.fixHint||"").trim())return null;let o=f("div","mlv-insp__fix");return o.setAttribute("data-suggestion",t.id),g(o,f(e,"mlv-insp__fix-label","What to change")),g(o,f("p","mlv-insp__fix-text",t.fixHint)),o}function Mt(t,e,n,o){if(o){t.disabled=!0,t.classList.add("is-stale"),t.setAttribute("data-stale",o);let i=de(e)+": "+me[o]+". Not opened.";t.title=i,t.setAttribute("aria-label",i);return}_(t,"click",i=>{i.stopPropagation(),n(e,i.altKey)}),_(t,"keydown",i=>{i.key!=="Enter"||!i.altKey||(i.preventDefault(),i.stopPropagation(),n(e,!0))})}function Fl(t,e,n){if(H(t),g(t,f("h3","mlv-sr","Findings")),!e.index){g(t,f("div","mlv-empty-note","No workflow loaded yet."));return}let o=e.issues.filter(e.keep);if(!o.length){e.issues.length?t.appendChild(hp(n)):(e.index.graph.nodes||[]).length===0?t.appendChild(up(e)):t.appendChild(cp(e));return}for(let i of Z){let s=o.filter(a=>$(a.severity)===i);if(!s.length)continue;let r=g(t,f("section","mlv-rail__section"));r.setAttribute("data-severity-section",i);let l=g(r,f("h4","mlv-rail__heading"));l.appendChild(U(i,12,"")),g(l,f("span","",i+" · "+s.length+(s.length===1?" finding":" findings"))),r.appendChild(sp(s,i,e,n))}}function sp(t,e,n,o,i){let s=f("ul","mlv-issues");s.setAttribute("role","listbox"),s.setAttribute("aria-label",i||e+" severity findings");for(let r of t)s.appendChild(rp(r,n,o));return dp(s,o),s}function rp(t,e,n){let o=e.selectedIssueId===t.id,i=f("li","mlv-issues__item");i.setAttribute("role","presentation");let s=f("div","mlv-issue");s.setAttribute("role","option"),s.tabIndex=-1,s.setAttribute("data-issue-id",t.id),s.setAttribute("aria-selected",o?"true":"false"),s.setAttribute("aria-label",(t.short?t.short+", ":"")+t.code+" "+t.severity+" severity, "+t.title+(t.loc.file?", "+Rt(t.loc):"")+He(t.basis)),o&&s.classList.add("is-selected");let r=Hn(t,e.staleReason);r.length&&(s.classList.add("is-stale"),s.setAttribute("data-stale",r.join(" ")),s.setAttribute("aria-label",s.getAttribute("aria-label")+", "+it(r)+(ie(r)?"":" since publishing"))),s.appendChild(U(t.severity,14,"")),t.short&&g(s,f("span","mlv-issue__short",t.short));let l=g(s,f("div","mlv-issue__text"));g(l,f("div","mlv-issue__title",t.title));let a=g(l,f("div","mlv-issue__meta"));g(a,f("span","",t.code)),t.loc.file&&a.appendChild(Oe("",t.loc));let d=St(t);if(d&&a.appendChild(d),r.length&&a.appendChild(lp(r)),_(s,"click",c=>n.onSelectIssue(t.id,c)),i.appendChild(s),t.loc.file){let c=ne("mlv-btn mlv-btn--icon mlv-issue__open","Open "+de(t.loc));c.appendChild(D("open",12)),Mt(c,t.loc,n.onOpen,e.staleReason?e.staleReason(t.loc.file):void 0),i.appendChild(c)}return o&&i.appendChild(ap(t,e,n)),i}function lp(t){let e=f("span","mlv-chip mlv-chip--stale");return e.appendChild(D("warning",11)),g(e,f("span","",it(t))),e.setAttribute("data-stale",t.join(" ")),e.title=ie(t)?"A file this finding cites is not under the workspace root; it is unchanged in another folder, which the notice above names. Its jump is blocked.":"A file this finding cites no longer matches the published revision. Its jump is blocked.",e}function ap(t,e,n){let o=f("div","mlv-issue__detail");o.setAttribute("data-issue-detail",t.id),t.message&&g(o,f("p","mlv-insp__line",t.message));let i=Nn(t);i&&o.appendChild(i);let s=g(o,f("div","mlv-issue__goto"));if(t.loc.file){let r=W("mlv-btn","Go to "+de(t.loc));Mt(r,t.loc,n.onOpen,e.staleReason?e.staleReason(t.loc.file):void 0),s.appendChild(r)}for(let r of t.relatedLocs||[]){let l="Go to "+(r.message||r.role.replace(/_/g," "))+" — "+de(r),a=W("mlv-btn",l);Mt(a,r,n.onOpen,e.staleReason?e.staleReason(r.file):void 0),s.appendChild(a)}return o}function dp(t,e){let n=()=>Array.prototype.slice.call(t.querySelectorAll('[role="option"]')),o=n(),i=o.filter(s=>s.getAttribute("aria-selected")==="true")[0]||o[0]||null;for(let s of o)s.tabIndex=s===i?0:-1;_(t,"keydown",s=>{let r=s.target;if(!r||typeof r.closest!="function")return;let l=r.closest('[role="option"]');if(!l||!t.contains(l))return;let a=n(),d=a.indexOf(l),c=null;if(s.key==="ArrowDown")c=a[Math.min(a.length-1,d+1)];else if(s.key==="ArrowUp")c=a[Math.max(0,d-1)];else if(s.key==="Home")c=a[0];else if(s.key==="End")c=a[a.length-1];else if(s.key==="Enter"){if(s.ctrlKey||s.metaKey||s.shiftKey)return;s.preventDefault();let u=l.getAttribute("data-issue-id");u&&e.onOpenIssue(u,s.altKey);return}else if(s.key===" "){s.preventDefault();let u=l.getAttribute("data-issue-id");u&&e.onSelectIssue(u);return}else return;if(s.preventDefault(),!!c){for(let u of a)u.tabIndex=u===c?0:-1;c.focus()}})}function cp(t){let e=f("div","mlv-empty-note mlv-nofindings");e.setAttribute("role","status"),g(e,f("div","mlv-clean__title","No findings recorded in this revision"));let n=t.index?t.index.graph.authoredCoverage:void 0,o=n?n.status:"unknown",i=n?n.limitations:0;return g(e,f("div","mlv-clean__detail","The assistant recorded no findings. Coverage: "+o+(i?"; "+i+(i===1?" limitation":" limitations")+" listed in About":"")+". This is not a check result.")),e}function up(t){let e=f("div","mlv-empty-note");e.setAttribute("role","status"),g(e,f("div","mlv-clean__title","Nothing analyzed"));let n=t.index?t.index.graph:null,o=n?n.diagnostics||[]:[],i=n?n.workspace.filesAnalyzed:0;if(g(e,f("div","mlv-clean__detail","No ML pipeline was found, so there is nothing to flag. "+i+(i===1?" file":" files")+" analyzed.")),o.length){let s=g(e,f("ul","mlv-state__list"));for(let r of o.slice(0,5))g(s,f("li","",r.kind+" — "+r.message))}return e}function hp(t){let e=f("div","mlv-empty-note");g(e,f("div","","No findings match these filters."));let n=W("mlv-btn","Clear filters");return _(n,"click",()=>t.onClearFilters()),e.appendChild(n),e}var li='[role="treeitem"]';function Dl(t,e,n){let o=g(t,f("ul","mlv-outline"));o.setAttribute("role","tree"),o.setAttribute("aria-label","Pipeline outline");for(let i of e.index.lanes){let s=g(o,f("li","mlv-outline__item"));s.setAttribute("role","treeitem"),s.setAttribute("data-outline-lane",i.id),s.tabIndex=-1;let r=g(s,f("div","mlv-outline__row mlv-outline__row--lane"));g(r,f("span","mlv-outline__label",e.index.phaseIndexOf(i.id)+1+" · "+(i.label||i.id)));let l=e.index.laneCounts(i.id,e.keep);if(J(l)>0){r.appendChild(U(Y(l),12,""));let d=J(l),c=g(r,f("span","mlv-outline__stage mlv-outline__phasecount",xt(d)));c.title=Jo(l)}_(r,"click",()=>n.onSelectLane(i.id));let a=e.index.roots(i.id);s.setAttribute("aria-expanded",a.length?"true":"false"),a.length&&s.appendChild(jl(a,e,n))}return fp(t,e,n),yp(o,n),Gl(o,mp(o,e.selectedNodeId)),o}function fp(t,e,n){let o=g(t,f("section","mlv-relations"));g(o,f("h4","mlv-rail__heading","Text relationships"));let i=e.selectedNodeId&&e.index.nodeById.has(e.selectedNodeId)?e.selectedNodeId:null;i&&g(o,f("p","mlv-relations__context","For "+(e.index.nodeById.get(i)?.label||i)));let s=g(o,f("div","mlv-relations__views"));s.setAttribute("role","group"),s.setAttribute("aria-label","Relationship direction");let r=g(o,f("div")),l=(c,u=!1)=>{for(let m of Array.from(s.querySelectorAll("button")))m.setAttribute("aria-pressed",m.getAttribute("data-relation-view")===c?"true":"false");for(;r.firstChild;)r.removeChild(r.firstChild);let h=c==="unresolved"?e.index.graph.edges.filter(m=>m.basis==="unresolved"||e.index.nodeById.get(m.source)?.basis==="unresolved"||e.index.nodeById.get(m.target)?.basis==="unresolved"):!i||c==="all"?e.index.graph.edges:c==="incoming"?e.index.inEdges.get(i)||[]:e.index.outEdges.get(i)||[],p=g(r,f("ul","mlv-relations__list"));if(p.setAttribute("aria-label",c[0].toUpperCase()+c.slice(1)+" relationships"),!h.length){g(p,f("li","mlv-empty-note","No "+c+" relationships in this view."));return}for(let m of h){let v=e.index.nodeById.get(m.source),y=e.index.nodeById.get(m.target),S=g(p,f("li","mlv-relations__item")),x=g(S,f("button","mlv-relations__row"));x.type="button",x.setAttribute("data-relation-id",m.id);let w=i?m.target===i?"Incoming":m.source===i?"Outgoing":"Related":"Directed",E=He(m.basis);x.setAttribute("aria-label",`${w}: ${v?.label||m.source} to ${y?.label||m.target}; ${m.label||m.kind}${E?";"+E.slice(1):""}`),g(x,f("span","mlv-relations__path",(v?.label||m.source)+" → "+(y?.label||m.target))),g(x,f("span","mlv-relations__detail",(m.label||m.kind||"connection")+(re(m.basis)?" · "+re(m.basis):""))),_(x,"click",()=>n.onSelectEdge(m.id))}u&&p.querySelector("button")?.focus()},a=[{id:"all",label:"All"},{id:"incoming",label:"Incoming"},{id:"outgoing",label:"Outgoing"},{id:"unresolved",label:"Unresolved"}];for(let c of a){let u=g(s,f("button","mlv-btn",c.label));u.type="button",u.setAttribute("data-relation-view",c.id),u.disabled=!i&&c.id!=="all"&&c.id!=="unresolved",_(u,"click",()=>{n.onRelationMode(c.id),l(c.id,!0)})}let d=!i&&(e.relationMode==="incoming"||e.relationMode==="outgoing")?"all":e.relationMode;l(d)}function jl(t,e,n){let o=f("ul");o.setAttribute("role","group");for(let i of t){let s=e.index.nodeById.get(i);if(!s)continue;let r=g(o,f("li","mlv-outline__item"));r.setAttribute("role","treeitem"),r.setAttribute("data-outline-id",i),r.tabIndex=-1;let l=e.index.laneChildren(i),a=l.length>0&&e.collapsed.has(i);l.length&&r.setAttribute("aria-expanded",a?"false":"true");let d=g(r,f("div","mlv-outline__row")),c=e.selectedNodeId===i;if(r.setAttribute("aria-selected",c?"true":"false"),c&&d.classList.add("is-selected"),l.length){let p=D("chevron",12);p.setAttribute("class","mlv-uicon mlv-outline__chevron"),d.appendChild(p)}else g(d,f("span","mlv-outline__chevron mlv-outline__chevron--none"));g(d,f("span","mlv-outline__label",s.label||s.qualname));let u=re(s.basis);u&&g(d,f("span","mlv-outline__stage",u)).setAttribute("data-basis",s.basis||"");let h=pp(e,i);if(h&&d.appendChild(h),_(d,"click",p=>n.onSelectNode(i,p)),l.length){let p=jl(l,e,n);p.hidden=a,r.appendChild(p)}}return o}function pp(t,e){let n=Y(t.index.subtreeCounts(e,t.keep));return n?U(n,12,""):null}function ai(t){return Array.prototype.slice.call(t.querySelectorAll(li)).filter(n=>{let o=n.parentElement;for(;o&&o!==t;){if(o.hidden)return!1;o=o.parentElement}return!0})}function Gl(t,e){let n=ai(t),o=e&&n.indexOf(e)>=0?e:n[0]||null;for(let i of n)i.tabIndex=i===o?0:-1}function mp(t,e){if(!e)return null;let n=ai(t);for(let o of n)if(o.getAttribute("data-outline-id")===e)return o;return null}function je(t,e){e&&(Gl(t,e),e.focus())}function gp(t,e){let n=e.parentElement;for(;n&&n!==t;){if(n.getAttribute("role")==="treeitem")return n;n=n.parentElement}return null}function vp(t){let e=t.querySelector('[role="group"]');return!e||e.hidden?null:e.querySelector(li)}function Bl(t,e){let n=t.getAttribute("data-outline-id");if(n){e.onSelectNode(n);return}let o=t.getAttribute("data-outline-lane");o&&e.onSelectLane(o)}function bp(t){let e=t.querySelector(".mlv-outline__label");return(e&&e.textContent||"").toLowerCase()}function yp(t,e){_(t,"keydown",n=>{let o=n.target;if(!o||typeof o.closest!="function")return;let i=o.closest(li);if(!i||!t.contains(i))return;let s=ai(t),r=s.indexOf(i);if(r<0)return;let l=i.getAttribute("aria-expanded"),a=i.getAttribute("data-outline-id");switch(n.key){case"ArrowDown":n.preventDefault(),je(t,s[Math.min(s.length-1,r+1)]);return;case"ArrowUp":n.preventDefault(),je(t,s[Math.max(0,r-1)]);return;case"Home":n.preventDefault(),je(t,s[0]);return;case"End":n.preventDefault(),je(t,s[s.length-1]);return;case"ArrowRight":n.preventDefault(),l==="false"&&a?e.onToggleCollapse(a):l==="true"&&je(t,vp(i));return;case"ArrowLeft":n.preventDefault(),l==="true"&&a?e.onToggleCollapse(a):je(t,gp(t,i));return;case"Enter":if(n.ctrlKey||n.metaKey||n.shiftKey)return;n.preventDefault(),a?e.onOpenNode(a,n.altKey):Bl(i,e);return;case" ":n.preventDefault(),Bl(i,e);return;default:break}if(n.key.length!==1||n.ctrlKey||n.metaKey||n.altKey)return;let d=n.key.toLowerCase();for(let c=1;c<=s.length;c++){let u=s[(r+c)%s.length];if(bp(u).indexOf(d)===0){n.preventDefault(),je(t,u);return}}})}var xp="A matching quote shows these lines exist unchanged since publishing. Whether they support the claim is for you to judge.",Le=(t,e,n)=>t+" "+(t===1?e:n);function wp(t,e){return t==="inferred"?"Reasoned from the cited code and stated assumptions; the quotes do not show all of it directly.":t==="unresolved"?"The evidence does not settle this claim. It does not mean the "+e+" is missing.":""}function zl(t,e,n){let o=g(t,f("div","mlv-sel"));o.setAttribute("data-columns",String(e.columns)),e.issue?(o.setAttribute("data-kind","finding"),Sp(o,e.issue,e,n)):e.edge?(o.setAttribute("data-kind","connection"),_p(o,e.edge,e,n)):e.node?(o.setAttribute("data-kind",e.index.isGroup(e.node.id)?"group":"step"),Ep(o,e.node,e,n)):g(o,f("p","mlv-empty-note","Select a step, a connection or a finding to read its claim beside its evidence."))}function hi(t,e){if(e.columns===1)return{claim:t,evidence:t};let n=g(t,f("div","mlv-sel__col mlv-sel__col--claim")),o=g(t,f("div","mlv-sel__col mlv-sel__col--evidence"));return{claim:n,evidence:o}}function Ep(t,e,n,o){let i=n.index,s=i.isGroup(e.id),r=s?"group":"step",{claim:l,evidence:a}=hi(t,n),d=g(l,f("p","mlv-insp__eyebrow")),c=i.phaseIndexOf(e.stage),u=e.phaseLabel||Wl(i,e.stage),h=g(d,f("span","mlv-insp__phase"));h.setAttribute("data-stage",e.stage),se(h,c),g(h,f("span","mlv-insp__phasedot")).setAttribute("aria-hidden","true"),g(h,f("span","",c+1+" · "+u)),h.title="Phase "+(c+1)+": "+u,e.kind&&e.kind!=="unknown"&&di(d,"mlv-insp__kind",e.kind);let p=i.parentOf.get(e.id),m=p?i.nodeById.get(p):void 0;m&&di(d,"mlv-insp__parent","in "+(m.label||m.id)),g(l,f("h4","mlv-insp__title",e.label||e.qualname)),fi(l,e.basis,r),e.detail&&g(l,f("p","mlv-insp__detail",e.detail)),s?(ci(l,i.subtreeIssues(e.id,n.keep),"Findings in this group",n,o),Ip(l,e,n,o)):ci(l,i.issuesOf(e.id,n.keep),"Findings on this step",n,o);let v=()=>s?Cp(l,e,n,o):Ap(l,e,n,o);n.columns===1?(ui(a,e.evidenceLocs||[],n,o,r),v()):(v(),ui(a,e.evidenceLocs||[],n,o,r)),pi(a,o),mi(a,n,o)}function _p(t,e,n,o){let i=n.index,{claim:s,evidence:r}=hi(t,n),l=g(s,f("p","mlv-insp__eyebrow"));g(l,f("span","","Connection"));let a=di(l,"mlv-insp__edgekind",ft(e.kind)+(e.authoredKind?" · authored as "+e.authoredKind:""));a.setAttribute("data-edge-kind",e.kind),a.title="Kind of connection, as the author named it",g(s,f("h4","mlv-insp__title",e.label||"Connection"));let d=i.nodeById.get(e.source),c=i.nodeById.get(e.target),u=g(s,f("p","mlv-insp__ends"));u.appendChild(Ge(e.source,d,o)),u.appendChild(document.createTextNode(" → ")),u.appendChild(Ge(e.target,c,o)),fi(s,e.basis,"connection"),ci(s,i.issuesOfEdge(e.id,n.keep),"Findings on this connection",n,o),ui(r,e.evidenceLocs||[],n,o,"connection"),pi(r,o),mi(r,n,o)}function Sp(t,e,n,o){let i=n.index,{claim:s,evidence:r}=hi(t,n),l=g(s,f("div","mlv-insp__eyebrow mlv-insp__issue-head"));if(l.setAttribute("data-issue-id",e.id),l.appendChild(U(e.severity,14,"")),g(l,f("span","mlv-insp__severity",Tp(e.severity)+" finding")),e.short){let m=g(l,f("span","mlv-insp__short",e.short));m.title=e.short+" is this finding's number in this revision; its id is "+e.code+"."}g(l,f("span","mlv-mono",e.code));let a=St(e);a&&l.appendChild(a),g(s,f("h4","mlv-insp__title",e.title)),fi(s,e.basis,"finding"),e.message&&g(s,f("p","mlv-insp__detail mlv-insp__message",e.message));let d=Nn(e,"h5");d&&s.appendChild(d);let c=g(s,f("section","mlv-insp__section mlv-insp__cited"));c.setAttribute("data-section","cited");let u=e.nodeIds.filter(m=>i.nodeById.has(m)),h=e.edgeIds.filter(m=>i.edgeById.has(m));if(!u.length&&!h.length&&g(c,f("p","mlv-insp__note","This finding cites no step or connection: it is about the workflow as a whole.")),u.length){c.appendChild(Ae("Cited steps",Le(u.length,"step","steps")));let m=g(c,f("ul","mlv-insp__links"));for(let v of u){let y=i.nodeById.get(v),S=g(m,f("li"));S.appendChild(Ge(v,y,o));let x=y.phaseLabel||Wl(i,y.stage);g(S,f("span","mlv-insp__aside"," · "+(i.phaseIndexOf(y.stage)+1)+" · "+x))}}if(h.length){c.appendChild(Ae("Cited connections",Le(h.length,"connection","connections")));let m=g(c,f("ul","mlv-insp__links"));for(let v of h){let y=i.edgeById.get(v),S=g(m,f("li")),x=W("mlv-link mlv-insp__flow-edge",y.label||"connection","Select the connection "+vi(i,y));x.setAttribute("data-edge-id",y.id),_(x,"click",()=>o.onShowEdge(y.id)),S.appendChild(x),g(S,f("span","mlv-insp__aside"," · "+gi(i,y)))}}let p=e.relatedLocs||[];Vl(r,p,n,o,{noun:"finding",role:m=>m.role||"Supporting evidence",staleNote:()=>{let m=Hn(e,n.staleReason);return m.length?ie(m)?"This finding "+it(m)+"; those jumps are blocked. The notice above says which folder to add.":"This finding "+it(m)+" since publishing; those jumps are blocked.":""}}),pi(r,o),mi(r,n,o)}function di(t,e,n){return g(t,f("span","mlv-insp__sep"," · ")).setAttribute("aria-hidden","true"),g(t,f("span",e,n))}function fi(t,e,n){let o=wp(e,n);if(!o)return;let i=g(t,f("p","mlv-insp__basis"));i.setAttribute("data-basis",e||"");let s=n==="finding"?null:nt(e,n);s&&(s.removeAttribute("aria-hidden"),s.removeAttribute("title"),i.appendChild(s),i.appendChild(document.createTextNode(" "))),g(i,f("span","",o))}function Ae(t,e){let n=f("h5","mlv-rail__heading");return g(n,f("span","mlv-rail__headtext",t)),e&&g(n,f("span","mlv-rail__count",e)),n}function ci(t,e,n,o,i){if(!e.length)return;let s=g(t,f("section","mlv-insp__section"));s.setAttribute("data-section","findings"),s.appendChild(Ae(n,Le(e.length,"finding","findings")));for(let r of e){let l=g(s,f("div","mlv-insp__issue"));l.setAttribute("data-issue-id",r.id);let a=g(l,f("div","mlv-insp__issue-head"));if(a.appendChild(U(r.severity,14,"")),r.short){let p=g(a,f("span","mlv-insp__short",r.short));p.title=r.short+" is this finding's number in this revision; its id is "+r.code+"."}let d=W("mlv-link mlv-insp__issue-title",r.title,"Select finding "+(r.short?r.short+", ":"")+r.title);_(d,"click",()=>i.onShowIssue(r.id)),a.appendChild(d);let c=St(r);c&&a.appendChild(c),g(l,f("p","mlv-insp__issue-id mlv-mono",r.code)),r.message&&g(l,f("p","mlv-insp__line",r.message));let u=Nn(r,"h6");u&&l.appendChild(u);let h=Hn(r,o.staleReason);if(h.length){l.classList.add("is-stale");let p=g(l,f("p","mlv-insp__stale-note"));p.appendChild(D("warning",12)),g(p,f("span","","This finding "+it(h)+(ie(h)?".":" since publishing.")))}}}function ui(t,e,n,o,i){Vl(t,e,n,o,{noun:i,nav:!0,staleNote:()=>{let s=d=>n.staleReason&&d.file?n.staleReason(d.file):void 0,r=qi(e,d=>!!(n.staleReason&&n.staleReason(d)));if(!r.stale)return"";let l=e.map(s).filter(d=>!!d),a=r.stale+" of "+r.total+(r.total===1?" quote cites ":" quotes cite ");return ie(l)?a+"a file that is not under the workspace root but is unchanged in another folder; those jumps are blocked. The notice above says which folder to add.":a+"a file that changed or went missing since publishing; those jumps are blocked. To compare the claim with the code as it is now, ask the assistant for a fresh revision."}})}function Vl(t,e,n,o,i){let s=g(t,f("section","mlv-insp__section mlv-insp__quotes"));if(s.setAttribute("data-section","quotes"),!e.length){s.appendChild(Ae("Source evidence","no quotes")),g(s,f("p","mlv-empty-note mlv-insp__no-evidence","No source evidence was authored for this "+i.noun+". Its basis and the coverage limitations describe what remains uncertain."));return}s.appendChild(Ae("Source evidence",Le(e.length,"quote","quotes"))),g(s,f("p","mlv-insp__caption",xp));let r=i.staleNote();if(r){let c=g(s,f("p","mlv-insp__stale-note"));c.appendChild(D("warning",12)),g(c,f("span","",r))}let l=0,a=()=>{};if(i.nav&&e.length>1){let c=g(s,f("div","mlv-insp__evidence-nav")),u=W("mlv-btn","Previous evidence"),h=W("mlv-btn","Next evidence");a=()=>{u.disabled=l===0,h.disabled=l===e.length-1,u.title=u.disabled?"This is the first evidence item":"Open the previous evidence item",h.title=h.disabled?"This is the last evidence item":"Open the next evidence item"},_(u,"click",()=>{l>0&&o.onOpen(e[--l]),a()}),_(h,"click",()=>{l{let h=g(d,f("li","mlv-quote")),p=n.staleReason&&c.file?n.staleReason(c.file):void 0;p&&(h.classList.add("is-stale"),h.setAttribute("data-stale",p));let m=g(h,f("div","mlv-quote__head"));g(m,f("span","mlv-quote__num",String(u+1))).setAttribute("aria-hidden","true");let v=g(m,f("span","mlv-quote__loc"));g(v,f("span","mlv-quote__file",c.file)),g(v,f("span","mlv-quote__at",Mp(c))),Re(c)&&(v.title=Re(c)),i.role&&g(m,f("span","mlv-quote__role",i.role(c))),m.appendChild(kp(c,p,n));let y=W("mlv-link mlv-quote__open","Open","Open "+de(c));y.setAttribute("data-evidence-id",c.evidenceId||""),p||(y.title="Open "+de(c)+" beside the diagram and highlight the cited lines; focus stays here (Alt+click moves it)."),Re(c)&&!p&&(y.title=Re(c)),Mt(y,c,(S,x)=>{l=u,a(),o.onOpen(S,x)},p),m.appendChild(y),c.snippet&&h.appendChild(Lp(c))})}function Mp(t){let e=Te(t),n=e?" › cell "+e.cell:"",o=t.endLine&&t.endLine>t.line?t.endLine:t.line;return n+(o>t.line?", lines "+t.line+"–"+o:", line "+t.line)}function kp(t,e,n){if(e){let s=f("span","mlv-quote__fresh mlv-insp__stale");return s.setAttribute("data-fresh","stale"),s.setAttribute("data-stale",e),s.appendChild(D("warning",12)),g(s,f("span","",me[e])),s}let o=!!(n.document&&n.document.verification&&t.file&&Object.prototype.hasOwnProperty.call(n.document.verification.files||{},t.file)),i=f("span","mlv-quote__fresh is-muted",o?"unchanged":"not checked");return i.setAttribute("data-fresh",o?"unchanged":"unchecked"),i.title=o?"Unchanged means these exact lines still exist as quoted. It does not mean they support the claim.":"This revision has no published hash for this file, so MLView cannot tell whether it changed.",i}function Lp(t){let e=f("pre","mlv-quote__text"),n=String(t.snippet||"").split(` -`);return n.forEach((o,i)=>{let s=g(e,f("span","mlv-quote__line"));g(s,f("span","mlv-quote__ln",String(t.line+i))).setAttribute("aria-hidden","true"),g(s,f("span","mlv-quote__code",o)),i{let h=g(a,f("li"));h.setAttribute("data-direction",u);let p=u==="in"?c.source:c.target;p===e.id?g(h,f("span","","Loops back to this step")):(g(h,f("span","",u==="in"?"Comes from ":"Feeds ")),h.appendChild(Ge(p,i.nodeById.get(p),o))),g(h,f("span","",": "));let m=W("mlv-link mlv-insp__flow-edge",c.label||"an unlabelled connection","Select the connection "+vi(i,c));m.setAttribute("data-edge-id",c.id),_(m,"click",()=>o.onShowEdge(c.id)),h.appendChild(m);let v=c.basis==="inferred"?" (inferred, not observed)":c.basis==="unresolved"?" (unresolved)":"";g(h,f("span",v?"mlv-insp__flow-basis":"",v+"."))};for(let c of s)d(c,"in");for(let c of r)c.source!==c.target&&d(c,"out")}function Ip(t,e,n,o){let i=n.index,s=i.laneChildren(e.id).filter(a=>i.nodeById.has(a));if(!s.length)return;let r=g(t,f("section","mlv-insp__section"));r.setAttribute("data-section","members"),r.appendChild(Ae("Steps in this group",Le(s.length,"step","steps")));let l=g(r,f("ul","mlv-insp__links"));for(let a of s){let d=g(l,f("li"));d.appendChild(Ge(a,i.nodeById.get(a),o));let c=i.descendantCount(a);c&&g(d,f("span","mlv-insp__aside"," · group of "+Le(c,"step","steps")))}}function Cp(t,e,n,o){let i=n.index,s=new Set,r=h=>{s.add(h);for(let p of i.laneChildren(h))r(p)};r(e.id);let l=[],a=[];for(let h of i.graph.edges){let p=s.has(h.source),m=s.has(h.target);if(p&&m){h.source===e.id&&h.target===e.id&&l.push(h);continue}m?l.push(h):p&&a.push(h)}if(!l.length&&!a.length)return;let d=g(t,f("section","mlv-insp__section mlv-insp__flow"));d.setAttribute("data-section","connections"),d.appendChild(Ae("Connections across its edge",Le(l.length+a.length,"connection","connections")));let c=g(d,f("ul","mlv-insp__sentences")),u=(h,p)=>{let m=g(c,f("li"));m.setAttribute("data-direction",p);let v=p==="in"?h.source:h.target,y=p==="in"?h.target:h.source;v===e.id&&y===e.id?g(m,f("span","","Loops back to this group")):(g(m,f("span","",p==="in"?"Comes from ":"Feeds ")),m.appendChild(Ge(v,i.nodeById.get(v),o)),y!==e.id&&(g(m,f("span","",p==="in"?" into ":" from ")),m.appendChild(Ge(y,i.nodeById.get(y),o)))),g(m,f("span","",": "));let S=W("mlv-link mlv-insp__flow-edge",h.label||"an unlabelled connection","Select the connection "+vi(i,h));S.setAttribute("data-edge-id",h.id),_(S,"click",()=>o.onShowEdge(h.id)),m.appendChild(S);let x=h.basis==="inferred"?" (inferred, not observed)":h.basis==="unresolved"?" (unresolved)":"";g(m,f("span",x?"mlv-insp__flow-basis":"",x+"."))};for(let h of l)u(h,"in");for(let h of a)u(h,"out")}function pi(t,e){let n=g(t,f("div","mlv-insp__actions")),o=W("mlv-btn mlv-insp__challenge","Challenge this claim","Copy a prompt that asks your assistant to check this claim against the code. MLView calls no model.");_(o,"click",()=>e.onChallenge());let i=W("mlv-btn mlv-insp__refine","Refine…","Copy a refinement prompt about this selection. MLView calls no model.");_(i,"click",()=>e.onRefine()),n.append(o,i)}function mi(t,e,n){let o=(e.index.graph.diagnostics||[]).filter(r=>r.kind==="workflow_limitation").length;if(!o)return;let i=g(t,f("p","mlv-insp__limits"));i.setAttribute("data-limitations",String(o)),g(i,f("span","",(o===1?"1 document-wide limitation applies":o+" document-wide limitations apply")+" to every claim. "));let s=W("mlv-link mlv-link--inline mlv-insp__limits-show",o===1?"Read it in About":"Read them in About");s.setAttribute("aria-label",o===1?"Read the document-wide limitation in About":"Read the "+o+" document-wide limitations in About"),_(s,"click",()=>n.onShowLimitations()),i.appendChild(s)}function Ge(t,e,n){let o=e?e.label||e.id:t,i=W("mlv-link mlv-insp__flow-step",o,"Select the step "+o);return i.setAttribute("data-node-id",t),_(i,"click",()=>n.onShowNode(t)),i}function gi(t,e){let n=t.nodeById.get(e.source),o=t.nodeById.get(e.target);return(n?n.label:e.source)+" → "+(o?o.label:e.target)}function vi(t,e){return(e.label?e.label+", ":"")+"from "+gi(t,e).replace(" → "," to ")}function Wl(t,e){let n=(t.graph.stages||[]).find(o=>o.id===e);return n&&n.label||e}function Tp(t){return t&&t[0].toUpperCase()+t.slice(1)}function Kl(t,e){let n=s=>s==="inferred"?" Inferred, not observed.":s==="unresolved"?" Unresolved.":"";if(e.kind==="node"){let s=t.nodeById.get(e.id);if(!s)return"";let r=s.detail?" "+gn(s.detail):"";return"Step: "+(s.label||s.id)+"."+n(s.basis)+r}if(e.kind==="edge"){let s=t.edgeById.get(e.id);return s?"Connection: "+(s.label?s.label+", ":"")+"from "+gi(t,s).replace(" → "," to ")+"."+n(s.basis):""}let o=t.issueById.get(e.id);if(!o)return"";let i=o.message?" "+gn(o.message):"";return"Finding "+(o.short?o.short+", ":"")+o.severity+" severity: "+o.title+"."+n(o.basis)+i}var Rp=280,Op=4,Hp=3,bi=/(^|[.!?]["')\]]?\s+|\n\s*)([A-Z][A-Za-z0-9&/(),' -]{0,47}?):\s+/g;function Np(t){let e=String(t||""),n=[];bi.lastIndex=0;for(let s=bi.exec(e);s;s=bi.exec(e)){let r=s[2].trim();if(r.split(/\s+/).filter(Boolean).length>6)continue;let a=s.index+s[1].length;n.push({at:a,textAt:s.index+s[0].length,head:r})}if(n.length{let l=r+1n&&e.push({text:t.slice(n,o.index),kv:!1}),e.push({text:i,kv:!0}),n=o.index+i.length,Pn.lastIndex=n}return nRp||n.request.question.split(` -`).length>Op){r.setAttribute("data-clamped","true");let b=W("mlv-link mlv-about__more","Show all","Show the whole question");b.setAttribute("aria-expanded","false"),b.setAttribute("aria-controls",r.id),_(b,"click",()=>{let M=r.getAttribute("data-clamped")==="true";r.setAttribute("data-clamped",M?"false":"true"),b.textContent=M?"Show less":"Show all",b.setAttribute("aria-expanded",M?"true":"false"),b.setAttribute("aria-label",M?"Show less of the question":"Show the whole question")}),s.appendChild(b)}let a=ze(i,"traced","What the model traced"),d=Np(n.coverage.summary);a.setAttribute("data-paragraphs",String(d.length));for(let b of d){let M=g(a,f("p","mlv-about__para"));b.head&&(g(M,f("strong","mlv-about__lead",b.head+":")),M.appendChild(document.createTextNode(" "))),M.appendChild(document.createTextNode(b.text))}let c=ze(i,"coverage","Coverage");g(c,f("p","mlv-about__status mlv-workflow__coverage",Fp(n.coverage.status))).setAttribute("data-status",n.coverage.status);let h=n.coverage.limitations;if(h.length){let b=g(c,f("details","mlv-about__limitations mlv-workflow__limitations"));b.open=!0;let M=h.length;g(b,f("summary","",M+(M===1?" coverage limitation":" coverage limitations")+" (apply to every claim)"));let T=g(b,f("ul"));for(let L of h)g(T,f("li","",L))}else g(c,f("p","mlv-about__note","The assistant listed no coverage limitations."));let p=ze(i,"scope","Scope");g(p,f("p","mlv-about__text",n.request.scope));let m=n.request.entrypoints&&n.request.entrypoints.length?n.request.entrypoints.join(", "):"not specified";g(p,f("p","mlv-about__meta","Entrypoints: "+m));let v=ze(i,"config","Run configuration"),y=n.request.configuration;if(y){let b=g(v,f("p","mlv-about__text mlv-about__config"));for(let M of Pp(y))M.kv?g(b,f("code","mlv-about__kv",M.text)):b.appendChild(document.createTextNode(M.text))}else g(v,f("p","mlv-about__meta","Configuration: not specified."));let S=ze(i,"files","Cited files"),x=[];for(let b of n.evidence||[])x.indexOf(b.file)<0&&x.push(b.file);if(!x.length)g(S,f("p","mlv-about__note","No file is cited."));else{let b=S.querySelector(".mlv-rail__heading");b&&g(b,f("span","mlv-rail__count",x.length+(x.length===1?" file":" files")));let M=g(S,f("ul","mlv-about__files")),T=n.verification?n.verification.files||{}:null;for(let L of x){let C=g(M,f("li","mlv-about__file"));C.setAttribute("data-file",L),g(C,f("span","mlv-about__path",L)).title=L;let N=e.staleReason?e.staleReason(L):void 0;if(N){let O=g(C,f("span","mlv-quote__fresh mlv-insp__stale"));O.setAttribute("data-fresh","stale"),O.setAttribute("data-stale",N),O.appendChild(D("warning",12)),g(O,f("span","",me[N]))}else{let O=!!T&&Object.prototype.hasOwnProperty.call(T,L);g(C,f("span","mlv-quote__fresh is-muted",O?"unchanged":"not checked")).setAttribute("data-fresh",O?"unchanged":"unchecked")}}g(S,f("p","mlv-about__note","Unchanged means the quoted lines still exist as published. It does not mean they support the claims."))}let w=ze(i,"provenance","Provenance"),E=[n.producer.host,n.producer.model||"model not named","revision "+n.revision.id+(n.revision.parent?" (after "+n.revision.parent+")":"")];E.push(n.verification?"published "+n.verification.publishedAt:"published without source hashes"),g(w,f("p","mlv-about__meta",E.join(" · "))),g(w,f("p","mlv-about__trust mlv-workflow__provenance","Model-authored; MLView checks citations, not the interpretation."))}function ze(t,e,n){let o=g(t,f("section","mlv-about__section"));o.setAttribute("data-about",e);let i=g(o,f("h4","mlv-rail__heading"));return g(i,f("span","mlv-rail__headtext",n)),o}var Ie=[{id:"about",label:"About",heading:"About this revision"},{id:"issues",label:"Findings",heading:"Findings"},{id:"inspector",label:"Selection",heading:"Selection"},{id:"outline",label:"Outline",heading:"Outline"}],Ul=.05,Dp=.15,jp=0,Fn=class{constructor(e){this.tabs=new Map;this.counts=new Map;this.panels=new Map;this.relationMode="outgoing";this.relationNodeId=null;this.current="about";this.mode="docked";this.open=!0;this.cb=e;let n="mlv"+ ++jp;this.root=f("aside","mlv-rail"),this.root.id=n+"-rail",this.root.setAttribute("data-mode","docked"),this.root.setAttribute("aria-labelledby",n+"-rail-heading"),this.heading=g(this.root,f("h2","mlv-sr","Side panel")),this.heading.id=n+"-rail-heading",this.grip=g(this.root,f("div","mlv-rail__grip")),this.grip.setAttribute("role","separator"),this.grip.setAttribute("aria-orientation","vertical"),this.grip.setAttribute("aria-label","Resize side panel"),this.grip.tabIndex=0,this.wireGrip(this.grip);let o=g(this.root,f("div","mlv-rail__bar")),i=g(o,f("div","mlv-rail__tabs"));i.setAttribute("role","tablist"),i.setAttribute("aria-label","Panel");for(let s=0;se.onTab(r.id)),_(l,"keydown",d=>{let c;if(d.key==="ArrowRight")c=(s+1)%Ie.length;else if(d.key==="ArrowLeft")c=(s+Ie.length-1)%Ie.length;else if(d.key==="Home")c=0;else if(d.key==="End")c=Ie.length-1;else return;d.preventDefault();let u=Ie[c].id;e.onTab(u),this.tabs.get(u)?.focus()}),i.appendChild(l),this.tabs.set(r.id,l);let a=f("div","mlv-rail__panel");a.id=n+"-panel-"+r.id,a.setAttribute("role","tabpanel"),a.setAttribute("aria-labelledby",l.id),a.setAttribute("data-tab",r.id),a.tabIndex=0,a.hidden=!0,this.root.appendChild(a),this.panels.set(r.id,a)}this.chevron=g(o,f("button","mlv-btn mlv-btn--icon mlv-rail__chevron")),this.chevron.type="button",this.chevron.setAttribute("aria-controls",this.root.id),this.chevron.appendChild(D("chevron",14)),this.chevron.hidden=!0,_(this.chevron,"click",()=>e.onSheetToggle()),_(this.root,"keydown",s=>{s.key!=="Escape"||s.defaultPrevented||this.mode!=="sheet"||!this.open||(s.preventDefault(),s.stopPropagation(),e.onSheetCollapse())}),this.applyShape()}setMode(e,n){this.mode=e,this.root.setAttribute("data-mode",e);let o=this.root.parentElement;o&&o.setAttribute("data-rail",e),e==="sheet"&&(this.root.style.width=""),this.setSheetFraction(n),this.applyShape()}setOpen(e){this.open=e,this.applyShape()}setSheetFraction(e){if(this.root.style.setProperty("--mlv-sheet-fraction",String(e)),this.mode==="sheet"){let n=this.cb.sheetFractionMax(),o=Math.round(Math.min(e,n)*100);this.grip.setAttribute("aria-valuemax",String(Math.round(n*100))),this.grip.setAttribute("aria-valuenow",String(o)),this.grip.setAttribute("aria-valuetext",this.open?o+" percent of the height":"collapsed")}}focusTab(e){let n=this.tabs.get(e);if(n)try{n.focus()}catch{}}activePanel(){return this.panels.get(this.current)}revealLimitations(){let e=this.panels.get("about"),n=e.querySelector(".mlv-about__limitations");if(!n)return e;n.open=!0;let o=n.querySelector("summary")||n;if(typeof n.scrollIntoView=="function")try{n.scrollIntoView({block:"nearest"})}catch{}return o}applyShape(){let e=this.mode==="sheet";if(this.root.hidden=!e&&!this.open,e?this.root.setAttribute("data-expanded",this.open?"true":"false"):this.root.removeAttribute("data-expanded"),this.heading.textContent=e?"Bottom panel":"Side panel",this.grip.setAttribute("aria-orientation",e?"horizontal":"vertical"),this.grip.setAttribute("aria-label",e?"Resize the bottom panel":"Resize side panel"),e)this.grip.setAttribute("aria-valuemin","25"),this.setSheetFraction(this.cb.sheetFraction());else for(let o of["aria-valuemin","aria-valuemax","aria-valuenow","aria-valuetext"])this.grip.removeAttribute(o);this.chevron.hidden=!e,this.chevron.setAttribute("aria-expanded",this.open?"true":"false");let n=this.open?"Collapse the bottom panel (Escape)":"Expand the bottom panel";this.chevron.setAttribute("aria-label",n),this.chevron.title=n;for(let[o,i]of this.panels)i.hidden=o!==this.current||e&&!this.open}wireGrip(e){let n=0,o=0,i=0,s=0,r=0,l=!1,a=!1,d=!0,c=0,u=0,h=m=>{if(!a){this.cb.onResize(i+(n-m.clientX));return}let v=o-m.clientY;!l&&Math.abs(v)<4||(l||(l=!0,!d&&v>0&&this.cb.onSheetToggle()),c=s+v/r,this.open&&this.cb.onSheetResize(c))},p=()=>{document.removeEventListener("pointermove",h),document.removeEventListener("pointerup",p),a&&(l?this.open&&c{a=this.mode==="sheet",n=m.clientX,o=m.clientY,i=this.root.getBoundingClientRect().width||360,r=Math.max(1,this.cb.bodyHeight()),d=this.open,u=this.cb.sheetFraction(),s=d?Math.min(u,this.cb.sheetFractionMax()):32/r,c=s,l=!1,document.addEventListener("pointermove",h),document.addEventListener("pointerup",p),m.preventDefault()}),_(e,"keydown",m=>{if(this.mode==="sheet"){if(m.key==="Enter"||m.key===" ")this.cb.onSheetToggle();else if(m.key==="ArrowUp"||m.key==="ArrowDown"){let y=m.key==="ArrowUp";this.open?this.cb.onSheetResize(this.cb.sheetFraction()+(y?Ul:-Ul)):y&&this.cb.onSheetToggle()}else return;m.preventDefault();return}let v=this.root.getBoundingClientRect().width||360;if(m.key==="ArrowLeft")this.cb.onResize(v+16);else if(m.key==="ArrowRight")this.cb.onResize(v-16);else return;m.preventDefault()})}update(e){let n=this.captureFocus();this.current=e.tab;for(let[i,s]of this.tabs){let r=i===e.tab;s.setAttribute("aria-selected",r?"true":"false"),s.tabIndex=r?0:-1}let o=this.counts.get("issues");if(o){let i=e.issues.length;o.textContent=e.index?" ("+i+")":"";let s=this.tabs.get("issues");s.title=e.index?i+(i===1?" finding":" findings")+" in this revision":"",e.index?s.setAttribute("aria-label","Findings, "+i+(i===1?" finding":" findings")):s.removeAttribute("aria-label")}this.applyShape();for(let[i,s]of this.panels)i!==e.tab&&H(s);e.tab==="about"?this.renderAbout(e):e.tab==="issues"?this.renderIssues(e):e.tab==="outline"?this.renderOutline(e):this.renderSelection(e),n()}captureFocus(){let e=typeof document<"u"?document.activeElement:null;if(!e||typeof e.closest!="function"||!this.root.contains(e))return()=>{};let n=e.closest('[role="tabpanel"]'),o=()=>{if(!n||e.isConnected)return;let l=this.root.ownerDocument;if(l.activeElement&&l.activeElement!==l.body)return;let a=this.panels.get(this.current);if(!(!a||a.hidden))try{a.focus()}catch{}},i=e.closest('[data-issue-id][role="option"], [data-outline-id], [data-outline-lane], [data-relation-id]');if(!i)return o;let s=i.hasAttribute("data-issue-id")?"data-issue-id":i.hasAttribute("data-outline-id")?"data-outline-id":i.hasAttribute("data-outline-lane")?"data-outline-lane":"data-relation-id",r=i.getAttribute(s)||"";return()=>{let l=this.root.querySelector("["+s+'="'+r+'"]');if(!l){o();return}if(l.closest('[role="tabpanel"]')?.hidden)return;let d=l.closest('[role="tree"], [role="listbox"]');if(d){let c=d.querySelectorAll('[role="treeitem"], [role="option"]');for(let u=0;uthis.cb.onSelectIssue(n,o),onOpenIssue:(n,o)=>this.cb.onOpenIssue(n,o),onOpen:(n,o)=>this.cb.onOpen(n,o),onClearFilters:()=>this.cb.onClearFilters()})}renderSelection(e){let n=this.panels.get("inspector");if(H(n),g(n,f("h3","mlv-sr","Selection")),!e.index){g(n,f("div","mlv-empty-note","No workflow loaded yet."));return}zl(n,{index:e.index,keep:e.keep,node:e.selectedNode,edge:e.selectedEdge,issue:e.selectedIssue,columns:e.columns,document:e.document,staleReason:e.staleReason},{onOpen:(o,i)=>this.cb.onOpen(o,i),onShowNode:o=>this.cb.onShowNode(o),onShowEdge:o=>this.cb.onShowEdge(o),onShowIssue:o=>this.cb.onShowIssue(o),onChallenge:()=>this.cb.onChallenge(),onRefine:()=>this.cb.onRefine(),onShowLimitations:()=>this.cb.onShowLimitations()})}renderOutline(e){let n=this.panels.get("outline");H(n),g(n,f("h3","mlv-sr","Outline"));let o=e.index;if(!o){g(n,f("div","mlv-empty-note","No workflow loaded yet."));return}e.selectedNode&&(this.relationNodeId=e.selectedNode.id);let i=this.relationNodeId&&o.nodeById.has(this.relationNodeId)?this.relationNodeId:null;Dl(n,{index:o,keep:e.keep,selectedNodeId:e.selectedNode?e.selectedNode.id:i,collapsed:e.collapsed,relationMode:this.relationMode},{onSelectNode:(s,r)=>this.cb.onSelectNode(s,r),onOpenNode:(s,r)=>this.cb.onOpenNode(s,r),onSelectEdge:s=>this.cb.onSelectEdge(s),onRelationMode:s=>{this.relationMode=s},onSelectLane:s=>this.cb.onSelectLane(s),onToggleCollapse:s=>this.cb.onToggleCollapse(s)})}};var Gp=[{group:"basis",key:"observed",label:"Observed",detail:"Directly supported by cited workspace source. Most claims are observed, so they carry no mark."},{group:"basis",key:"inferred",label:"Inferred",detail:'Reasoned from cited source and stated assumptions; the quotes do not show all of it. Dashed border or line, and an "inferred" tag.'},{group:"basis",key:"unresolved",label:"Unresolved",detail:'The available evidence does not settle this claim. It does not mean the step is absent. Dotted border or line, faint hatching, and a "? unresolved" tag.'}],zp=[{group:"edge",key:"arrow",label:"connection",detail:"Points from the step that produces something to the step that uses it. The line style shows the basis above, not the kind."},{group:"edge",key:"back",label:"loop back",detail:"The return leg of a loop, marked with a chevron."},{group:"edge",key:"kinds",label:"kind",detail:"What a connection carries (data, call, state…) is written in its hover card and the Selection tab."}],Vp=[{group:"phase",key:"order",label:"phase colour",detail:"Each phase gets a colour by its place in the document, shown on the lane's left rule, the swatch before its number and name, and the card's left edge. It means nothing else. Problems are the only other colour on the diagram."}],Wp=[{group:"freshness",key:"verified",label:"Unchanged",detail:"The status bar counts the cited files that still match the hashes published with this revision. Unchanged means the quoted lines still exist; it does not prove the interpretation."},{group:"freshness",key:"draft",label:"Not checked",detail:"This revision has no published source hashes, so the status bar says freshness is not checked."},{group:"freshness",key:"stale",label:"Changed or missing",detail:"A cited file no longer matches the published revision. Cards, connections, findings and quotes that cite it carry this mark and a border in the warning colour, and their jumps are blocked. It does not say whether the claim is still right. When the notice above says the workspace root is the wrong folder, the mark means the file is unchanged in another folder."}];function Kp(){return[{id:"basis",title:"Claim basis",rows:Gp},{id:"severity",title:"Findings",rows:Z.map(t=>({group:"severity",key:t,label:ye[t],detail:t==="high"?"High potential impact if the finding is correct.":t==="medium"?"Medium potential impact if the finding is correct.":"Low potential impact if the finding is correct. Severity does not express certainty."})).concat([{group:"severity",key:"short",label:"numbers",detail:"Findings numbered in document order. A new revision can renumber them; the hover card, the Selection tab and Refine prompts use the real id."}])},{id:"edges",title:"Connections",rows:zp},{id:"phases",title:"Phases",rows:Vp},{id:"freshness",title:"Source freshness",rows:Wp}]}function Xl(t,e=!1){let n=R("svg",{class:"mlv-legend__swatch",viewBox:"0 0 44 14",width:44,height:14,"aria-hidden":"true"}),o=R("g",{class:"mlv-edge mlv-edge--data"+(e?" mlv-edge--back":"")});return o.setAttribute("data-basis",t),o.appendChild(R("path",{class:"mlv-legend__edge",d:"M2 7 H 33"})),o.appendChild(R("path",{class:"mlv-arrow",d:pt.d,transform:"translate(31,2)"})),e&&o.appendChild(R("path",{class:"mlv-edge__loopmark",d:"M22 3 L18 7 L22 11",fill:"none",stroke:"var(--mlv-edge)","stroke-width":1.3,"stroke-linecap":"round"})),n.appendChild(o),n}function qp(t){let e=f("span","mlv-legend__basis"),n=g(e,f("span","mlv-legend__card"));n.setAttribute("data-basis",t);let o=re(t);return o&&g(n,f("span","mlv-legend__tag",o)).setAttribute("data-basis",t),e.appendChild(Xl(t)),e}function Up(){let t=f("span","mlv-legend__phases");t.setAttribute("aria-hidden","true");for(let e=0;e{this.setOpen(!1),e(!1)}),n.appendChild(this.toggleBtn),this.body=g(this.root,f("div","mlv-legend__body"));for(let o of Kp()){let i=g(this.body,f("section","mlv-legend__section"));i.setAttribute("data-legend-section",o.id),g(i,f("h3","mlv-legend__heading",o.title));let s=g(i,f("dl","mlv-legend__list"));for(let r of o.rows){let l=g(s,f("dt","mlv-legend__term"));l.setAttribute("data-legend-row",r.group+":"+r.key);let a=Xp(r);l.appendChild(a);let d=r.group==="basis";!d&&(a.textContent||"").trim()!==r.label&&g(l,f("span","mlv-legend__label",r.label));let c=g(s,f("dd","mlv-legend__desc"));d&&g(c,f("strong","mlv-legend__label",r.label+". ")),c.appendChild(c.ownerDocument.createTextNode(r.detail))}}this.setOpen(!1)}get open(){return this.openState}setKinds(e,n){let o=this.root.querySelector('[data-legend-row="edge:kinds"]'),i=o&&o.nextElementSibling;if(!i)return;let s=i.querySelector(".mlv-legend__authored");s&&s.remove();let r=[];if(e.length){let l=e.slice(0,8).map(a=>a.kind+" "+a.count).join(", ");r.push("Connections in this diagram, by kind: "+l+(e.length>8?", and "+(e.length-8)+" more kinds":"")+".")}n>0&&r.push(n+(n===1?" connection has":" connections have")+" no kind."),r.length&&g(i,f("span","mlv-legend__authored"," "+r.join(" ")))}setOpen(e){this.openState=e,this.root.hidden=!e}toggle(){return this.setOpen(!this.openState),this.openState}};var Yl=[{keys:["Mod+F","/"],action:"focusSearch",description:"Search steps, findings, IDs, or cited text"},{keys:["n","p"],action:"cycleIssue",description:"Next / previous finding (document order)"},{keys:["Enter"],action:"open",description:"Open the cited source beside the diagram; focus stays here"},{keys:["Alt+Enter"],action:"openFocus",description:"Open the cited source and move focus to the editor"},{keys:["Space"],action:"collapse",description:"Collapse or expand the selected group"},{keys:["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"],action:"move",description:"Move the selection"},{keys:["0"],action:"fit",description:"Readable view: the whole diagram if it fits at reading size, otherwise phase 1"},{keys:["Shift+0"],action:"overview",description:"Overview: collapse every group and fit the whole diagram"},{keys:["+","="],action:"zoomIn",description:"Zoom in"},{keys:["-","_"],action:"zoomOut",description:"Zoom out"},{keys:["z"],action:"zoomToSelection",description:"Zoom to the selection"},{keys:["f"],action:"focusMode",description:"Focus mode: light the full lineage of the selection"},{keys:["l"],action:"legend",description:"Show or hide the legend"},{keys:["a"],action:"flow",description:"Turn the connection flow animation on or off"},{keys:["Shift+A"],action:"replayFlow",description:"Play the connection flow again (it stops after two passes)"},{keys:["e","Shift+E"],action:"cycleConnections",description:"Next / previous connection of the selected step"},{keys:["1","2","3"],action:"toggleSeverity",description:"Toggle the high / medium / low filters"},{keys:["b"],action:"toggleRail",description:"Show or hide the side panel; open or collapse the bottom panel"},{keys:["t"],action:"railTab",description:"Go to the panel tabs, About / Findings / Selection / Outline (opens the panel); ← and → pick a tab"},{keys:["?"],action:"shortcuts",description:"Show this shortcut sheet"},{keys:["Escape"],action:"escape",description:"Close the menu, this sheet, the Refine popover or the legend, collapse the bottom panel, exit focus mode, clear the selection, leave the canvas"},{keys:["Tab","Shift+Tab"],action:"browser",description:"Move focus out of the diagram (never intercepted)"}];function Zl(t,e){let n=t.key,o=t.ctrlKey||t.metaKey,i=()=>(t.preventDefault(),!0);return n==="Tab"?!1:o?n==="f"||n==="F"?(e.focusSearch(),i()):!1:t.altKey&&n==="Enter"&&!t.shiftKey?e.openSelection(!0)?i():!1:t.altKey?!1:n==="/"?(e.focusSearch(),i()):n==="?"?(e.toggleShortcuts(),i()):n==="Escape"?(e.escape(),i()):n==="n"||n==="N"||n==="p"||n==="P"?e.cycleIssue(n==="p"||n==="P")?i():!1:n==="+"||n==="="?(e.zoom(1),i()):n==="-"||n==="_"?(e.zoom(-1),i()):n===")"||n==="0"&&t.shiftKey?(e.overview(),i()):n==="0"?(e.fit(),i()):n==="1"||n==="2"||n==="3"?(e.toggleSeverity(Number(n)-1),i()):n==="f"||n==="F"?(e.toggleFocusMode(),i()):n==="l"||n==="L"?(e.toggleLegend(),i()):n==="a"||n==="A"?t.shiftKey?e.replayFlow()?i():!1:(e.toggleFlow(),i()):n==="e"||n==="E"?e.cycleConnections(t.shiftKey)?i():!1:n==="z"||n==="Z"?(e.zoomToSelection(),i()):n==="b"||n==="B"?(e.toggleRail(),i()):n==="t"||n==="T"?(e.focusRailTabs(),i()):n===" "?e.toggleCollapse()?i():!1:n==="Enter"?e.openSelection(!1)?i():!1:n.indexOf("Arrow")===0?(e.move(n),i()):!1}var Dn=class{constructor(e){this.opener=null;this.root=f("div","mlv-sheet"),this.root.hidden=!0;let n=g(this.root,f("div","mlv-sheet__scrim"));_(n,"click",()=>e());let o=g(this.root,f("div","mlv-sheet__panel"));this.panel=o,o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.setAttribute("aria-label","Keyboard shortcuts");let i=g(o,f("div","mlv-sheet__head"));g(i,f("h2","mlv-sheet__title","Keyboard shortcuts")),this.closeBtn=ne("mlv-btn mlv-btn--icon","Close shortcuts"),this.closeBtn.appendChild(D("close")),_(this.closeBtn,"click",()=>e()),i.appendChild(this.closeBtn),_(this.root,"keydown",a=>{if(Bt(this.panel,a)){a.stopPropagation();return}a.key==="Escape"&&(a.preventDefault(),a.stopPropagation(),e())});let s=si();g(o,f("p","mlv-sheet__note","Click a card, connection, finding or Outline row to select it and read its claim. Double-click a step, connection, finding or Outline step, or press Enter on it, to open the cited source beside the diagram; focus stays here. Double-click a group to collapse it. "+(s?"Option":"Alt")+"+click an Open link, or press "+De("Alt+Enter",s)+", to move focus to the editor."));let l=g(o,f("dl","mlv-sheet__list"));for(let a of Yl){let d=g(l,f("dt","mlv-sheet__keys"));for(let c of a.keys)g(d,f("kbd","mlv-kbd",De(c,s)));g(l,f("dd","mlv-sheet__desc",a.description))}}get open(){return!this.root.hidden}show(){if(!this.open){let e=this.root.ownerDocument,n=e?e.activeElement:null;this.opener=n&&!this.root.contains(n)?n:null}this.root.hidden=!1;try{this.closeBtn.focus()}catch{}}hide(e=null){let n=this.open;if(this.root.hidden=!0,!n)return;let o=this.opener;this.opener=null,at(o,e)}toggle(){return this.open?this.hide():this.show(),this.open}};function Ql(t,e){return(t.id||"mlv-search-results")+"-opt-"+e}var Yp=52;function Zp(t,e=Yp){return t.length<=e?t:"…"+t.slice(t.length-(e-1))}var yi=(t,e,n)=>t+" "+(t===1?e:n);function Qp(t){let e=[];return t.totalNodes&&e.push(yi(t.totalNodes,"step","steps")),t.totalIssues&&e.push(yi(t.totalIssues,"finding","findings")),yi(t.total,"match","matches")+(e.length?": "+e.join(", "):"")}function $l(t,e,n){let{query:o,result:i,cursor:s,onPick:r}=n,l=i.hits;if(H(t),!o){t.hidden=!0,e.setAttribute("aria-expanded","false"),e.removeAttribute("aria-activedescendant");return}if(t.hidden=!1,e.setAttribute("aria-expanded","true"),!l.length){g(t,f("li","mlv-result__empty","No matches")),e.removeAttribute("aria-activedescendant");return}let a=g(t,f("li","mlv-result__count",Qp(i)));if(a.setAttribute("role","presentation"),a.setAttribute("data-search-count",String(i.total)),l.forEach((d,c)=>{let u=g(t,f("li"));u.setAttribute("role","presentation");let h=f("button","mlv-result");if(h.type="button",h.id=Ql(t,c),h.setAttribute("role","option"),h.tabIndex=-1,h.setAttribute("aria-selected",c===s?"true":"false"),d.severity&&h.appendChild(U(d.severity,12,"")),g(h,f("span","mlv-result__label",d.label)),d.meta){let p=g(h,f("span","mlv-result__meta",Zp(d.meta)));p.textContent!==d.meta&&(p.title=d.meta)}if(d.location){let p=d.location.line===null?"first in file":"line "+d.location.line;g(h,f("span","mlv-result__jump",d.location.exact?"jump to "+p:"nearest to "+p)).setAttribute("data-search-jump",d.location.exact?"exact":"nearest"),u.setAttribute("data-search-pinned","1")}_(h,"click",()=>r(d)),u.appendChild(h)}),i.truncated){let d=g(t,f("li","mlv-result__foot"));d.setAttribute("role","presentation"),d.setAttribute("data-search-truncated",String(i.total)),g(d,f("span","","showing "+l.length+" of "+i.total));let c=W("mlv-link mlv-link--inline","Show more","Show more search results");c.setAttribute("data-search-more","1"),_(c,"click",u=>{u.preventDefault(),n.onShowMore()}),d.appendChild(c)}s>=0&&s|]+\.[A-Za-z][A-Za-z0-9_]{0,7}$/;function ta(t){let e=(t||"").trim();if(!e)return null;let n=/^(.+?):(\d+)(?::\d+)?$/.exec(e);if(n){let o=n[1].trim(),i=Number(n[2]);return!ea.test(o)||!isFinite(i)||i<1?null:{path:xi(o),line:i}}return ea.test(e)?{path:xi(e),line:null}:null}function xi(t){let e=t.split("\\").join("/");for(;e.indexOf("./")===0;)e=e.slice(2);return e}function $p(t,e){let n=xi(t);return n===e?!0:n.length>e.length&&n.slice(n.length-e.length-1)==="/"+e}function na(t,e){let n=t.graph.nodes||[],o=null;for(let i of n){if(!i.loc||!$p(i.loc.file,e.path))continue;let s=i.loc.line,r=Math.max(s,i.loc.endLine||s),l=r-s,a,d;e.line===null?(a=!1,d=s):(a=e.line>=s&&e.line<=r,d=a?0:e.linem.hit),p=em(t,e);if(p){let m=h.findIndex(v=>v.kind==="node"&&v.id===p.id);m>=0&&h.splice(m,1),h.unshift(p),h.length>n&&(h.length=n)}return{hits:h,total:i.length+s.length,totalNodes:i.length,totalIssues:s.length,limit:n,truncated:i.length+s.length>h.length}}function em(t,e){let n=ta(e);if(!n||n.line===null)return null;let o=na(t,n);if(!o)return null;let i=o.node;return{kind:"node",id:i.id,label:i.label||i.qualname,meta:Ke(i.loc),stage:i.stage,location:{path:n.path,line:n.line,exact:o.contains}}}function wi(t){t.sort((e,n)=>e.score-n.score||e.at-n.at)}var kt=40,Ei={hits:[],total:0,totalNodes:0,totalIssues:0,limit:kt,truncated:!1},jn=class{constructor(e,n,o){this.result=Ei;this.limit=kt;this.cursor=-1;this.input=e,this.results=n,this.host=o}run(e){this.limit=kt,this.execute(e),this.host.onQueryChanged(e)}execute(e){let n=this.host.index();this.result=n?ia(n,e,this.limit):Ei,this.cursor=this.result.hits.length?0:-1,this.render()}showMore(){this.limit+=kt,this.execute(this.input.value)}get hits(){return this.result.hits}clear(){this.input.value="",this.result=Ei,this.limit=kt,this.cursor=-1,this.render()}focus(){this.input.focus(),this.input.select()}hideResults(){this.results.hidden=!0}handleKey(e){if(e.key==="Escape"){this.clear(),this.host.onQueryChanged(""),this.host.blurToCanvas();return}let n=this.result.hits;if(n.length){if(e.key==="ArrowDown"||e.key==="ArrowUp")e.preventDefault(),this.cursor=Jl(this.cursor,n.length,e.key==="ArrowDown"?1:-1),this.render();else if(e.key==="Enter"){e.preventDefault();let o=n[Math.max(0,this.cursor)];o&&(this.host.activate(o),this.hideResults())}}}render(){$l(this.results,this.input,{query:this.input.value,result:this.result,cursor:this.cursor,onPick:e=>{this.host.activate(e),this.hideResults()},onShowMore:()=>this.showMore()})}};var tm=new Set(["invalid","parse","missing","unreadable","obsolete","same-id-changed"]),nm=new Set(["stale","root-hint"]),Gn=class{constructor(e){this.cb=e,this.root=f("div","mlv-hostnotice"),this.root.setAttribute("role","status"),this.root.hidden=!0,this.icon=g(this.root,f("span","mlv-hostnotice__icon")),this.icon.setAttribute("aria-hidden","true"),this.text=g(this.root,f("p","mlv-hostnotice__text")),this.actions=g(this.root,f("div","mlv-hostnotice__actions"))}update(e,n){let o=n||[],i=o.some(s=>tm.has(s))?"error":o.some(s=>nm.has(s))?"warn":"info";if(this.root.hidden=!e,this.root.className="mlv-hostnotice mlv-hostnotice--"+i,this.root.setAttribute("data-codes",o.join(" ")),this.text.textContent=e,H(this.icon),i!=="info"&&this.icon.appendChild(D("warning",14)),H(this.actions),this.actions.hidden=!0,e&&o.indexOf("root-hint")>=0){let s=W("mlv-btn mlv-btn--primary","Add folder to workspace");s.setAttribute("data-workspace-hint","add"),_(s,"click",()=>this.cb.onWorkspaceHint("add"));let r=W("mlv-btn","Open folder");r.setAttribute("data-workspace-hint","open"),r.title="Open the folder the citations resolve against in a new window",_(r,"click",()=>this.cb.onWorkspaceHint("open")),this.actions.append(s,r),this.actions.hidden=!1}}};var sa={bg:"--mlv-bg",surface:"--mlv-surface",surface2:"--mlv-surface-2",border:"--mlv-border",borderStrong:"--mlv-border-strong",text:"--mlv-text",text2:"--mlv-text-2",text3:"--mlv-text-3",link:"--mlv-link",accent:"--mlv-accent",edge:"--mlv-edge",sevHigh:"--mlv-sev-high",sevMedium:"--mlv-sev-medium",sevLow:"--mlv-sev-low",sevHighInk:"--mlv-sev-high-ink",sevMediumInk:"--mlv-sev-medium-ink",sevLowInk:"--mlv-sev-low-ink",stageUnknown:"--mlv-stage-unknown",nodeEdge:"--mlv-node-edge"},om=["--mlv-phase-0","--mlv-phase-1","--mlv-phase-2","--mlv-phase-3","--mlv-phase-4","--mlv-phase-5","--mlv-phase-6","--mlv-phase-7"],im={light:.64,dark:.54,hc:1},sm={light:.58,dark:.46,hc:1},ra={laneTint:"--mlv-lane-tint",groupTint:"--mlv-group-tint"},_i={bg:"#FBFBFD",surface:"#FFFFFF",surface2:"#F3F4F8",border:"#E3E5EB",borderStrong:"#C9CDD6",text:"#16181D",text2:"#5A6070",text3:"#676E81",link:"#2B57C4",accent:"#3B6CF6",edge:"#8C93A3",sevHigh:"#D0342C",sevMedium:"#E8A317",sevLow:"#2F5FD0",sevHighInk:"#FFFFFF",sevMediumInk:"#3A2500",sevLowInk:"#FFFFFF",stageUnknown:"#7A8090",nodeEdge:"",phases:["#00796B","#6A4FB3","#0A693C","#A0388F","#558B2F","#0B7F99","#546E7A","#1F6FB2"],laneTint:.025,groupTint:0},rm={bg:"#131417",surface:"#1A1C21",surface2:"#22252C",border:"#2C3038",borderStrong:"#3B414C",text:"#E6E8EE",text2:"#9AA1B1",text3:"#969DAD",link:"#8FB0FF",accent:"#6E96FF",edge:"#79808F",sevHigh:"#FF6169",sevMedium:"#F2B03C",sevLow:"#6E96FF",sevHighInk:"#1A0405",sevMediumInk:"#2A1A00",sevLowInk:"#0B1020",stageUnknown:"#8B93A7",phases:["#4DB6AC","#A48BE0","#4CA871","#D684C5","#9CCC65","#4FC3D9","#90A4AE","#6FA8DC"],laneTint:.035,groupTint:0},lm={phases:["#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF"],bg:"#000000",surface:"#000000",surface2:"#000000",border:"#FFFFFF",borderStrong:"#FFFFFF",text:"#FFFFFF",text2:"#FFFFFF",text3:"#FFFFFF",link:"#6BB7FF",edge:"#FFFFFF",laneTint:0,groupTint:0},la={light:zn({..._i},"light"),dark:zn({..._i,...rm},"dark"),hc:zn({..._i,...lm},"hc")};function am(t){let e=la[t]||la.light;return{...e,phases:e.phases.slice()}}function Mi(t,e){if(e===void 0||!t.phases.length)return t.stageUnknown;let n=t.phases.length;return t.phases[(e%n+n)%n]||t.stageUnknown}function zn(t,e){return e==="hc"?(t.edge=t.border,t.nodeEdge=t.border,t):(t.edge=Si(t.text,t.bg,im[e])||t.edge,t.nodeEdge=Si(t.text,t.bg,sm[e])||t.border,t)}function aa(t){let e=t.trim().toLowerCase(),n=/^#([0-9a-f]{3,8})$/.exec(e);if(n){let i=n[1];return i.length===3||i.length===4?[0,1,2].map(s=>parseInt(i[s]+i[s],16)):i.length===6||i.length===8?[0,2,4].map(s=>parseInt(i.slice(s,s+2),16)):null}let o=/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/.exec(e);return o?[Number(o[1]),Number(o[2]),Number(o[3])].map(i=>Math.max(0,Math.min(255,i))):null}function dm(t){return"#"+t.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("").toUpperCase()}function Si(t,e,n){let o=aa(t),i=aa(e);return!o||!i?"":dm(o.map((s,r)=>s*n+i[r]*(1-n)))}function cm(t){let e=/^color-mix\(\s*in srgb\s*,\s*(.+?)\s+([\d.]+)%\s*,\s*(.+?)\s*\)$/i.exec(t.trim());return e?Si(e[1],e[3],Number(e[2])/100):""}function Ve(t,e){return e==="high"?t.sevHigh:e==="medium"?t.sevMedium:e==="low"?t.sevLow:t.edge}function ca(t,e){return e==="high"?t.sevHighInk:e==="medium"?t.sevMediumInk:t.sevLowInk}function ua(t,e){let o={...am(e)},i=um(t);if(!t||!i||typeof i.getComputedStyle!="function")return o;let s;try{s=i.getComputedStyle(t)}catch{return o}if(!s||typeof s.getPropertyValue!="function")return o;let r=new Set;for(let a of Object.keys(sa)){let d=da(s.getPropertyValue(sa[a]));d&&(o[a]=d,r.add(a))}om.forEach((a,d)=>{let c=da(s.getPropertyValue(a));c&&(o.phases[d]=c)});let l=zn({...o},e);r.has("edge")||(o.edge=l.edge),r.has("nodeEdge")||(o.nodeEdge=l.nodeEdge);for(let a of Object.keys(ra)){let d=ha(s.getPropertyValue(ra[a])),c=d?Number(d):NaN;isFinite(c)&&c>=0&&c<=1&&(o[a]=c)}return o}function um(t){if(!t)return null;let e=t.ownerDocument;return e?e.defaultView:null}function da(t){let e=(t||"").trim();return e.toLowerCase().startsWith("color-mix(")?cm(e):ha(e)}function ha(t){let e=(t||"").trim();return!e||e.indexOf("var(")>=0||e.indexOf("color-mix(")>=0?"":e}var ki="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",At="ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",hm=.51,fm=.55,pm=.6;function te(t,e,n,o){return t?'"+I(t)+"":""}function Li(t,e,n,o,i,s){return''}function Q(t,e,n,o){let i=n?pm:o?fm:hm;return t.length*e*i}function Ce(t,e,n,o,i){if(Q(t,n,o,i)<=e)return t;let s=t.length;for(;s>1&&Q(t.slice(0,s)+"…",n,o,i)>e;)s--;return t.slice(0,Math.max(1,s))+"…"}function Vn(t,e,n,o,i,s){let r=$(t),l=o/18,a=Ve(i,r),d=s?'':'',c='';return''+d+c+""}function fa(t,e,n,o,i,s=[]){let r=Y(t);if(!r)return"";let l=J(t),a=s.length?Jn(s):l>1?String(l):"",d=26+(a?Q(a,10.5,!1,!0)+4:0),c=e-d,u=['',Vn(r,c+4,n+1,18,o,i)];return a&&u.push(te(a,c+24,n+14,{size:10.5,fill:o.text2,weight:650})),u.join("")}function Wn(t,e,n,o,i,s){let r=[];for(let c of Z)t[c]>0&&r.push({sev:c,n:t[c]});if(!r.length)return"";let l=0;for(let c of r)l+=o+2+Q(String(c.n),10.5,!1,!1)+6;let a=e-l,d=[];for(let c of r)d.push(Vn(c.sev,a,n-o/2,o,i,s)),a+=o+2,d.push(te(String(c.n),a,n+3.5,{size:10.5,fill:i.text2})),a+=Q(String(c.n),10.5,!1,!1)+6;return d.join("")}function pa(t){if(!t.length)return{x:0,y:0,w:0,h:0};let e=t[0].x,n=t[0].y,o=t[0].x,i=t[0].y;for(let s of t)s.xo&&(o=s.x),s.y>i&&(i=s.y);return{x:e,y:n,w:o-e,h:i-n}}function ma(t,e){return{x:t.x-e,y:t.y-e,w:t.w+e*2,h:t.h+e*2}}function It(t,e){return t.xe.x&&t.ye.y}function ga(t){return{x:Lt(t.x),y:Lt(t.y),w:Lt(Math.max(1,t.w)),h:Lt(Math.max(1,t.h))}}function Lt(t){return Math.round((isFinite(t)?t:0)*100)/100}function k(t){return String(Lt(t))}function I(t){let e="",n=String(t??"");for(let o=0;o=55296&&s<=56319){let r=o+1=56320&&r<=57343?(e+=i+n.charAt(o+1),o++):e+="�";continue}if(s>=56320&&s<=57343||s===65534||s===65535){e+="�";continue}i==="&"?e+="&":i==="<"?e+="<":i===">"?e+=">":i==='"'?e+=""":i==="'"?e+="'":i<" "&&i!==` -`&&i!==" "?e+=" ":e+=i}return e}var mm=10,Ri=4,Ma=8,gm=12,vm=10,Oi=26,bm=8,va=Ri+Ma+Oi+bm,ba=13,rt=11,Ai=10.5,ya=10.5,Ct=13,ym=.05,st=10.5,Ii=12,xm=10,wm=22.5,xa=38.5,Em=52.5,_m=22,Sm=15,Mm=98,Ci=14,wa=3,Ea=6,Hi=14,km=10,_a=10,Lm="6 4",Am="1.5 3.5",Im=12,Ti=34;function ka(t){let{plan:e,palette:n,theme:o}=t,i=o==="hc",s=ga(t.region),r=[],l=[],a=[],d=[],c=[],u=[],h=0,p=w=>{u.indexOf(w)<0&&u.push(w)};r.push('');let m=[];for(let w of e.lanes){let E=w.lane,b={x:E.x,y:E.y,w:E.w,h:E.h};if(!It(b,s))continue;c.push(E.id);let M=Mi(n,w.phase);p(M),m.push(Cm(E,w.counts,M,w.phase,n,i))}let v=[];for(let w of e.nodes){let E=w.visual.box,b={x:E.x-8,y:E.y-8,w:E.w+16,h:E.h+16};if(!It(b,s))continue;l.push(w.visual.node.id);let M=Mi(n,w.visual.phase);p(M);let T=w.expandedGroup?Hm(w.visual,n,i):Tm(w.visual,M,n,i);w.expandedGroup?m.push(T):v.push(T)}let y=[];for(let w of e.edges){let E=w.route;if(!It(ma(pa(E.points),16),s))continue;a.push(E.id);for(let M of E.ids)d.push(M);let b=Nm(w,n,i);y.push(b.markup),b.labelled&&h++}r.push(''+m.join("")+""),r.push(''+y.join("")+""),r.push(''+v.join("")+"");let S='',x=""+I(t.title)+""+(t.desc?""+I(t.desc)+"":"");return{svg:` -`+S+x+r.join("")+` -`,width:s.w,height:s.h,region:s,nodeIds:l,edgeIds:a,edgeDocIds:d,laneIds:c,labels:h,stageColors:u}}function Cm(t,e,n,o,i,s){let r=[''];r.push(''),r.push(''),r.push(''),r.push('');let l=t.y+(t.headerH-8)/2;r.push('');let a=t.y+t.headerH/2+4.5,d=String(o+1),c=t.x+26;r.push(te(d,c,a,{size:Ct,fill:i.text2,weight:650}));let u=c+Q(d,Ct,!1,!0)+6,h=J(e),p=h>0?"· "+xt(h):"",m=p?Q(p,st,!1,!1):0,v=Pe(t.nodeCount),y=Q(v,st,!1,!1),S=Math.max(40,t.x+t.w-12-u-y-10-(p?m+96:0)),x=Ce(t.label||t.id,S,Ct,!1,!0);r.push(te(x,u,a,{size:Ct,fill:i.text,weight:650}));let w=u+Q(x,Ct,!1,!0)+10;if(r.push(te(v,w,a,{size:st,fill:i.text3})),p){let E=t.x+t.w-12;r.push(te(p,E-m,a,{size:st,fill:i.text3})),r.push(Wn(e,E-m-6,t.y+t.headerH/2,13,i,s))}return r.push(""),r.join("")}function Tm(t,e,n,o){let i=t.node,s=t.box,r=s.collapsed,l=Y(t.counts),a=Ni(i.basis),c=[""];c.push(""+I(yt(t))+"");let u=t.stale?n.sevMedium:l?Ve(n,l):a?n.text2:n.nodeEdge;c.push('"),c.push('');let h=s.x+Ri+Ma,p=s.y+vm;c.push(''),c.push(Li(fn(tt(i,r)),h+5,p+5,16/16,n.text2,1.3));let m=s.x+va,v=Math.max(24,s.w-va-gm),y=i.label||i.qualname||i.id,S=Po(y,s.w);S.forEach((E,b)=>{c.push(te(Ce(E,v,ba,!1,!0),m,s.y+wm+b*18,{size:ba,fill:n.text,weight:650}))});let x=(S.length-1)*18;if(!r&&$o(i)===2)Om(mn(i),v,2).forEach((E,b)=>{c.push(te(E,m,s.y+xa+x+b*Rm,{size:rt,fill:n.text2}))});else{let E=Ce(mn(i),v,rt,!1,!1);c.push(te(E,m,s.y+xa+x,{size:rt,fill:n.text2}));let b=lt(i.loc),M=Q(b.tail,Ai,!0,!1),T=Ce(b.head,Math.max(12,v-M),Ai,!0,!1)+b.tail;c.push(te(T,m,s.y+Em+x,{size:Ai,fill:n.text3,mono:!0}))}let w=r?[Pe(t.descendants)]:[];if(w.length&&s.h-x>=Mm){let E=s.y+s.h-_m,b=m;for(let M of w){let T=Q(M,ya,!1,!1)+12;if(b+T>m+v)break;c.push(''),c.push(te(M,b+6,E+11,{size:ya,fill:n.text2})),b+=T+4}}return l&&!r?c.push(fa(t.counts,s.x+s.w+6,s.y-6,n,o,t.findings||[])):r&&J(t.counts)>0&&c.push(Wn(t.counts,s.x+s.w+6,s.y+4,14,n,o)),c.push(La(i.basis,s.x+km,s.y+s.h-Hi/2,n)),c.push(""),c.join("")}var Rm=14.85;function Om(t,e,n){let o=t.replace(/\s+/g," ").trim().split(" ").filter(Boolean),i=[],s="";for(let r=0;r'+te(i,e+4,n+3.5,{size:_a,fill:o.text})+""}function Hm(t,e,n){let o=t.node,i=t.box,s=Y(t.counts),r=Ni(o.basis),l=['"];l.push(""+I(yt(t))+""),l.push('");let a=i.y+(Ti-14)/2;l.push(Li(fn(tt(o,!1)),i.x+8,a,14/16,e.text2,1.3));let d=i.x+28,c=i.y+Ti/2+4,u=Math.max(40,i.w-(s?170:96)),h=Ce(o.label||o.qualname,u,Ii,!1,!0);l.push(te(h,d,c,{size:Ii,fill:e.text,weight:650}));let p=Pe(t.descendants),m=Q(p,st,!1,!1)+12,v=d+Q(h,Ii,!1,!0)+8;return l.push(''),l.push(te(p,v+6,c,{size:st,fill:e.text2})),l.push(La(o.basis,v+m+6,c-3.5,e)),s&&l.push(Wn(t.counts,i.x+i.w-10,i.y+Ti/2,13,e,n)),l.push(""),l.join("")}function Nm(t,e,n){let o=t.route,i=t.severity?Ve(e,t.severity):e.edge,s=Ni(t.basis),r=t.weight&&t.weight>1?Math.floor(t.weight):1,l=r>=2,a=l?Math.max(Sa,jt(r)):Sa,d=['"];d.push('"),d.push(Fm(o.points,i));let c=!1,u=t.placement,h=!u||!u.hidden&&u.always;if(o.label&&h){let m=u?u.x:o.mid.x,v=u?u.y:o.mid.y-8,y=u?u.text:o.label;d.push(''+I(y)+""),c=!0}let p=u?u.marker:o.mid;if(t.severity){let m=$(t.severity);d.push(''),d.push(Vn(m,p.x-7,p.y-7,14,e,n))}else o.back&&d.push('');return l&&d.push(Pm(r,p,o.midAngle,!!t.severity||o.back,e)),d.push(""),{markup:d.join(""),labelled:c}}function Pm(t,e,n,o,i){let s=ht(t),r=zt(t),l=Gt(e,n,o);return''+I(s)+""}var Sa=1.5;function Fm(t,e){let n=pt;if(t.length<2)return"";let o=t[t.length-1],i=t[t.length-2];for(let a=t.length-2;a>=0;a--)if(t[a].x!==o.x||t[a].y!==o.y){i=t[a];break}let s=Math.atan2(o.y-i.y,o.x-i.x)*180/Math.PI,r="translate("+k(o.x)+","+k(o.y)+") rotate("+k(s)+") translate(-7.65,-4.5) scale(0.9)",l='fill="'+I(e)+'"';return'"}var Bm="image/svg+xml",Dm="image/png";function jm(t,e,n){let o=n,i=Math.max(t,e);i>0&&i*o>16384&&(o=16384/i);let s=t*e;return s>0&&s*o*o>268435456&&(o=Math.sqrt(268435456/s)),o>=1?o:null}function Gm(t){return"data:"+Bm+";charset=utf-8,"+encodeURIComponent(t)}function Aa(t,e,n,o=2){return new Promise(i=>{if(typeof document>"u"||typeof Image>"u"){i(null);return}let s=jm(e,n,o);if(s===null){i(null);return}let r=s,l,a;try{l=document.createElement("canvas"),l.width=Math.max(1,Math.round(e*r)),l.height=Math.max(1,Math.round(n*r)),a=typeof l.getContext=="function"?l.getContext("2d"):null}catch{i(null);return}if(!a||typeof l.toDataURL!="function"){i(null);return}let d=new Image,c=!1,u=h=>{c||(c=!0,i(h))};d.onload=()=>{try{a.setTransform(r,0,0,r,0,0),a.drawImage(d,0,0,e,n);let h=l.toDataURL(Dm),p=h.indexOf(","),m=p<0?"":h.slice(p+1);u(h==="data:,"||!m?null:{base64:m,width:l.width,height:l.height,scale:r})}catch{u(null)}},d.onerror=()=>u(null);try{d.src=Gm(t)}catch{u(null)}})}var Kn="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function zm(t){let e=[];for(let n=0;n=55296&&o<=56319){let i=n+1=56320&&i<=57343?(o=65536+(o-55296<<10)+(i-56320),n++):o=65533}else o>=56320&&o<=57343&&(o=65533);o<128?e.push(o):o<2048?e.push(192|o>>6,128|o&63):o<65536?e.push(224|o>>12,128|o>>6&63,128|o&63):e.push(240|o>>18,128|o>>12&63,128|o>>6&63,128|o&63)}return new Uint8Array(e)}function Vm(t){let e=typeof globalThis>"u"?{}:globalThis;if(typeof e.btoa=="function"){let o="";for(let i=0;i>2)+Kn.charAt((i&3)<<4|s>>4),n+=o+1>6):"=",n+=o+2!!o).join("-")+"."+e}function Ta(t){return String(t.workspace.root||"")||"workflow"}function qm(t){return String(t).toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)}function Ra(t,e,n){return{v:1,type:"exportFile",kind:t,name:e,base64:n,data:n,suggestedName:e,scope:"all"}}function Oa(t,e,n){return o=>{if(o.outcome==="done"){t.announce(e(o.name||n));return}if(o.outcome==="cancelled"){t.announce("Export cancelled.");return}let i="Export failed: "+(o.message||"the host did not say why");t.toast(i),t.announce(i)}}function Ha(t,e,n){let o=Ia(e.svg);if(!o){t.toast("This browser could not encode the SVG.");return}let i=e.nodeIds.length,s=e.edgeIds.length;t.announce("Saving SVG…"),t.request(Ra("svg",n,o),Oa(t,r=>"Exported "+i+" cards and "+s+" connections as "+r+".",n))}async function Na(t,e,n){let o=await Um(e);if(!o)return t.toast("Could not draw the PNG here — save the SVG instead."),t.announce("PNG export is not available in this host."),!1;let i=o.width+"×"+o.height;return t.announce("Saving PNG…"),t.request(Ra("png",n,o.base64),Oa(t,s=>"Exported "+i+" PNG as "+s+".",n)),!0}async function Pa(t,e){if(await Xm(e.svg)){let s="SVG copied — "+e.nodeIds.length+" cards, "+e.edgeIds.length+" connections.";return t.toast(s),t.announce(s),!0}let o=e.nodeIds.length,i=e.edgeIds.length;return t.request({v:1,type:"copy",text:e.svg},s=>{if(s.outcome==="done"){let r="SVG copied — "+o+" cards, "+i+" connections.";t.toast(r),t.announce(r)}else t.announce("The SVG could not be copied.")}),!1}async function Um(t){try{return await Aa(t.svg,t.width,t.height,Wm)}catch{return null}}async function Xm(t){let e=typeof navigator<"u"?navigator:null;if(!e||!e.clipboard||typeof e.clipboard.writeText!="function")return!1;try{return await e.clipboard.writeText(t),!0}catch{return!1}}function Ym(t){let e=t.view.scenePlan();return!e||!t.graph?null:{plan:e,palette:ua(t.root,t.themes.kind),theme:t.themes.kind,graph:t.graph,generatedAt:new Date().toISOString().slice(0,10)}}function Fa(t,e){let n={post:s=>t.bridge.post(s),request:(s,r)=>{t.postRequest(s,r)},toast:s=>t.view.toast(s),announce:s=>t.announce(s)},o=Ym(t);if(!o){t.view.toast("Nothing is drawn yet — there is nothing to export.");return}let i=Ca(o);e==="svg"?Ha(n,i,Pi(o,"svg")):e==="png"?Na(n,i,Pi(o,"png")):e==="copy-svg"&&Pa(n,i)}function Zm(t){let e={steps:0,connections:0,findings:0};if(!t.graph)return e;let n=o=>!!o&&o!=="observed";for(let o of t.graph.nodes)n(o.basis)&&e.steps++;for(let o of t.graph.edges)n(o.basis)&&e.connections++;for(let o of t.graph.issues)n(o.basis)&&e.findings++;return e}function Qm(t){let e=pe();if(!t.graph)return e;for(let n of t.graph.issues)e[$(n.severity)]++;return e}function q(t){let e=Zm(t);t.exceptionsOn&&e.steps+e.connections+e.findings===0&&(t.exceptionsOn=!1,t.view.canvasEl.removeAttribute("data-exceptions")),t.chrome.update({graph:t.graph,document:t.graph?t.workflowDocument:null,flowOn:t.flowOn,legendOpen:t.legendOpen,hasSelection:!!t.selection,filters:t.filters.value,visibleCounts:Qm(t),minimapCollapsed:t.view.minimapCollapsed,minimapUnavailable:t.view.minimapUnavailable(),railOpen:t.railOpen,freshness:Ui(t.workflowDocument,t.freshness),checking:t.freshness.checking,notObserved:e,exceptionsOn:t.exceptionsOn,railMode:t.railMode})}function X(t){let e=t.selection,n=t.selectedNodeId(),o=n&&t.index&&t.index.nodeById.get(n)||null,i=e&&e.kind==="edge"&&t.index&&t.index.edgeById.get(e.id)||null,s=e&&e.kind==="issue"&&t.index&&t.index.issueById.get(e.id)||null;t.paneColumns=t.selectionColumns(),t.rail.update({index:t.index,tab:t.railTab,issues:t.graph?t.graph.issues:[],selectedNode:o,selectedEdge:i,selectedIssueId:e&&e.kind==="issue"?e.id:null,selectedIssue:s,collapsed:t.view.collapsed,keep:t.filters.keep,staleReason:r=>t.freshness.reasonOf(r),document:t.graph?t.workflowDocument:null,columns:t.paneColumns})}function Ba(t,e,n,o=!1){t.graph=e;let i=new Dt(e);t.index=i,n&&n.collapsed?t.collapsedState=n.collapsed.slice():!t.collapsedState.length&&t.view.collapsed.size===0&&(t.collapsedState=i.defaultCollapsed());let s=new Set(e.nodes.map(l=>l.id));t.view.setIndex(i),t.view.flow.clear(),t.view.setCollapsed(t.collapsedState.filter(l=>s.has(l)&&i.isGroup(l))),n&&n.selection!==void 0&&(t.selection=n.selection),t.selection&&t.selection.kind==="node"&&!s.has(t.selection.id)&&(t.selection=null),t.selection&&t.selection.kind==="edge"&&!i.edgeById.has(t.selection.id)&&(t.selection=null),t.selection&&t.selection.kind==="issue"&&!i.issueById.has(t.selection.id)&&(t.selection=null),t.view.relayout();let r=n&&n.viewport?n.viewport:null;if(r?t.view.viewport.set(r):t.view.fit(),q(t),X(t),t.view.applySelection(t.selection),o){let l=(d,c,u)=>d+" "+(d===1?c:u),a=e.stats.issues.high;t.announce("Workflow loaded: "+l(e.nodes.length,"step","steps")+", "+l(e.edges.length,"connection","connections")+", "+l(e.issues.length,"finding","findings")+(a?" ("+a+" high severity)":"")+".")}t.saveSoon()}function qn(t){if(!t.graph)return;let e=new Set(t.graph.nodes.map(o=>o.id)),n=t.collapsedState.filter(o=>!e.has(o));t.collapsedState=n.concat(Array.from(t.view.collapsed)).sort()}function ja(t){let e=_l(t.root,t.themes.kind);t.releasePage=El(t.root),t.liveEl=e.live,t.view=new Cn(e,$m(t)),t.chrome=new On({onQuery:n=>t.search.run(n),onSearchKey:n=>t.search.handleKey(n),onSearchOpen:()=>t.focusSearch(),onSeverity:n=>t.applyFilters(()=>t.filters.toggleSeverity(n)),onToggleExceptions:n=>t.setExceptions(n),onAbout:n=>t.showAbout({focus:n}),onZoom:n=>t.view.zoomStep(n),onToggleLegend:n=>t.setLegend(n),onToggleFlow:n=>t.setFlow(n),onToggleMinimap:n=>t.setMinimapCollapsed(n),onToggleRail:()=>t.toggleRail(),onFitWhole:()=>t.view.fitWhole(),onZoomToSelection:()=>t.zoomToSelection(),onExport:n=>Fa(t,n),onShortcuts:()=>t.toggleShortcuts(!0),onMenuOpen:()=>q(t)},e.zoomBar),t.root.appendChild(t.chrome.header),t.notice=new Gn({onWorkspaceHint:n=>t.bridge.post({v:1,type:"workspaceHint",action:n})}),t.root.appendChild(t.notice.root),t.root.appendChild(e.body),e.body.appendChild(e.main),t.search=new jn(t.chrome.searchInput,t.chrome.results,{index:()=>t.index,activate:n=>t.activateHit(n),onQueryChanged:n=>{t.filters.patch({query:n}),t.saveSoon()},blurToCanvas:()=>t.view.canvasEl.focus()}),t.rail=new Fn({onTab:n=>t.setRailTab(n),onClearFilters:()=>t.clearFilters(),onSelectIssue:(n,o)=>{t.focusIssue(n,{fromList:"issues"}),t.doubleClick.arm(o,()=>t.openIssue(n,!1))},onOpenIssue:(n,o)=>t.openIssue(n,o),onSelectNode:(n,o)=>{t.select({kind:"node",id:n},{center:!0,reveal:!0,fromList:"outline"}),t.doubleClick.arm(o,()=>t.select({kind:"node",id:n},{center:!0,reveal:!0,open:!0,fromList:"outline"}))},onOpenNode:(n,o)=>t.select({kind:"node",id:n},{center:!0,reveal:!0,open:!0,focusEditor:o,fromList:"outline"}),onSelectEdge:n=>t.select({kind:"edge",id:n},{reveal:!0,fromList:"outline"}),onShowNode:n=>t.focusNode(n,{center:!0,pulse:!0}),onShowEdge:n=>t.select({kind:"edge",id:n},{reveal:!0}),onShowIssue:n=>t.focusIssue(n),onChallenge:()=>Da(t,"challenge"),onRefine:()=>Da(t,null),onOpen:(n,o)=>t.openLocation(n,o),onResize:n=>t.setRailWidth(n),onSelectLane:n=>t.selectLane(n),onToggleCollapse:n=>{t.index&&t.index.isGroup(n)&&t.view.toggleCollapse(n)},onShowLimitations:()=>t.showAbout({at:"limitations",focus:!0}),onSheetToggle:()=>t.toggleRail(),onSheetCollapse:()=>t.collapseSheet(!0),onSheetResize:n=>t.setSheetFraction(n),sheetFraction:()=>t.sheetFraction,sheetFractionMax:()=>t.sheetFractionMax(),bodyHeight:()=>e.body.getBoundingClientRect().height}),e.body.appendChild(t.rail.root),_(t.rail.root,"pointerdown",()=>{t.railChosen=!0}),_(t.rail.root,"keydown",()=>{t.railChosen=!0}),t.legend=new Bn(n=>t.setLegend(n)),e.canvas.appendChild(t.legend.root),t.sheet=new Dn(()=>t.toggleShortcuts(!1)),t.root.appendChild(t.sheet.root),t.root.appendChild(t.chrome.more.panel),t.root.appendChild(t.chrome.status),t.root.appendChild(t.liveEl),t.setRailWidth(t.railWidth),t.setRailOpen(t.railOpen)}function $m(t){let e=(o,i)=>t.select({kind:"node",id:o},{showClaim:!0,open:!0,focusEditor:i}),n=(o,i)=>t.select({kind:"edge",id:o},{showClaim:!0,open:!0,focusEditor:i});return{keep:t.filters.keep,activateNode:(o,i)=>{t.select({kind:"node",id:o},{showClaim:!0}),t.doubleClick.arm(i,()=>e(o,!1))},activateEdge:(o,i)=>{t.select({kind:"edge",id:o},{showClaim:!0}),t.doubleClick.arm(i,()=>n(o,!1))},openNode:e,openEdge:n,clearFilters:()=>t.clearFilters(),announce:o=>t.announce(o),afterCollapse:()=>{qn(t),t.view.applySelection(t.selection),X(t),t.saveSoon()},onViewportChange:o=>{t.viewportState={x:o.x,y:o.y,zoom:o.zoom},t.saveSoon()},onMinimapCollapsed:()=>{q(t),t.saveSoon()},onKeyDown:o=>t.onKeyDown(o),onBackgroundClick:()=>t.clearSelection(),keptTarget:()=>{let o=t.selection;if(!o)return null;if(o.kind!=="issue")return{kind:o.kind,id:o.id};let i=t.index?t.index.issueById.get(o.id):void 0;return i?i.nodeIds[0]?{kind:"node",id:i.nodeIds[0]}:i.edgeIds.length?{kind:"edge",id:i.edgeIds[0]}:null:null}}}function Da(t,e){let n=t.root.querySelector(".mlv-workflow__refine"),o=t.root.querySelector(".mlv-workflow__composer"),i=t.root.querySelector(".mlv-workflow__intent");!n||!o||!i||(o.hidden||n.click(),n.click(),e&&(i.value=e,i.dispatchEvent(new Event("change",{bubbles:!0}))))}function Ga(t,e,n=!1){if(!t.caps.canOpenSource)return;let o=e.evidenceId?t.freshness.reasonOf(e.file):void 0;if(o){let r=e.file+": "+me[o]+". Not opened"+(o==="elsewhere"?".":"; the cited lines may no longer be there.");t.view.toast(r),t.announce(r);return}t.bridge.saveState(t.getState());let i={v:1,type:"openLocation",file:e.file,absFile:e.absFile,line:e.line,col:e.col,endLine:e.endLine,endCol:e.endCol,preview:!0};e.evidenceId&&(i.evidenceId=e.evidenceId),e.evidenceId&&e.cell!==void 0&&(i.cell=e.cell),n&&(i.focus=!0),t.bridge.post(i);let s=de(e);t.announce(n?"Opening "+s+" in the editor.":"Opening "+s+" beside the diagram. Focus stays here; Alt+Enter moves it to the editor."),!n&&!t.openHintShown&&(t.openHintShown=!0,t.view.toast("Opening the cited lines beside the diagram. Focus stays here; Alt+Enter moves it to the editor."))}var Jm=["about","issues","inspector","outline"];function Un(t){if(!t||typeof t!="object")return null;let{kind:e,id:n}=t;return e!=="node"&&e!=="edge"&&e!=="issue"||typeof n!="string"||!n?null:{kind:e,id:n}}function Xn(t){return typeof t=="string"&&Jm.indexOf(t)>=0?t:null}function za(t){return{focusSearch:()=>t.focusSearch(),escape:()=>t.dismissTopmost(),cycleIssue:e=>{let n=t.visibleIssues();if(!n.length)return!1;let o=t.selectedIssueId(),s=((o?n.findIndex(r=>r.id===o):-1)+(e?-1:1)+n.length)%n.length;return t.focusIssue(n[s].id),!0},zoom:e=>t.zoom(e),fit:()=>t.fit(),toggleFocusMode:()=>t.toggleFocusMode(),zoomToSelection:()=>t.zoomToSelection(),overview:()=>t.overview(),toggleLegend:()=>t.toggleLegend(),toggleFlow:()=>t.toggleFlow(),replayFlow:()=>t.replayFlow(),toggleCollapse:()=>t.collapseSelection(),openSelection:e=>t.openSelection(e),move:e=>t.move(e),toggleSeverity:e=>{let n=Z[e];n&&t.toggleSeverity(n)},toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:()=>t.toggleShortcuts(),cycleConnections:e=>t.cycleConnections(e)}}function Va(t){return{focusSearch:()=>t.focusSearch(),dismissTopmost:()=>{if(t.sheetOpen())t.toggleShortcuts(!1);else{if(t.closeHeaderPanels())return;if(t.legendOpen())t.closeLegend();else{if(t.collapseSheet())return;t.view().isFocusLocked?t.view().toggleFocusMode(t.selection()):t.selection()?(t.view().blurFocusedEdge(),t.clearSelection()):t.view().canvasEl.blur()}}},visibleIssues:()=>t.visibleIssues(),selectedIssueId:()=>{let e=t.selection();return e&&e.kind==="issue"?e.id:null},focusIssue:e=>t.focusIssue(e),zoom:e=>t.view().zoomStep(e),fit:()=>t.view().fit(),toggleFocusMode:()=>t.view().toggleFocusMode(t.selection()),zoomToSelection:()=>t.zoomToSelection(),overview:()=>t.view().overview(),toggleLegend:()=>t.toggleLegend(),toggleFlow:()=>t.toggleFlow(),replayFlow:()=>t.view().flow.replay()?(t.announce("Playing the connection flow again."),!0):!1,collapseSelection:()=>{let e=t.selection(),n=t.index();if(!e||e.kind!=="node"||!n)return!1;let o=n.isGroup(e.id)?e.id:n.parentOf.get(e.id)||null;return!o||!n.isGroup(o)?!1:(t.view().toggleCollapse(o),!0)},openSelection:e=>t.openSelection(e),move:e=>t.move(e),toggleSeverity:e=>t.toggleSeverity(e),toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:()=>t.toggleShortcuts(),cycleConnections:e=>eg(t,e)}}function eg(t,e){let n=t.selection(),o=null;if(n&&n.kind==="node"?o=n.id:n&&n.kind==="edge"&&(o=t.edgeAnchor()),!o)return!1;let i=t.view().incidentRoutes(o);if(!i.length)return t.view().toast("That node has no connections in this view."),!0;let s=n&&n.kind==="edge"?n.id:null,r=-1;for(let a=0;at.view,index:()=>t.index,selection:()=>t.selection,edgeAnchor:()=>t.edgeAnchor,setEdgeAnchor:e=>{t.edgeAnchor=e},select:e=>t.select(e),clearSelection:()=>t.clearSelection(),focusSearch:()=>t.focusSearch(),visibleIssues:()=>ng(t),focusIssue:e=>t.focusIssue(e,{fromList:"issues"}),openSelection:e=>{let n=t.selection;if(!n)return!1;let o=t.locOf(n);return o&&t.openLocation(o,e),!0},zoomToSelection:()=>t.zoomToSelection(),move:e=>og(t,e),toggleSeverity:e=>t.applyFilters(()=>t.filters.toggleSeverity(e)),toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:e=>t.toggleShortcuts(e),sheetOpen:()=>t.sheet.open,closeHeaderPanels:()=>t.closeHeaderPanels(),collapseSheet:()=>t.collapseSheet(!1),announce:e=>t.announce(e),toggleLegend:()=>t.setLegend(!t.legendOpen),legendOpen:()=>t.legendOpen,closeLegend:()=>t.setLegend(!1),toggleFlow:()=>t.setFlow(!t.flowOn)})}function ng(t){return t.graph?t.graph.issues.filter(t.filters.keep):[]}function og(t,e){let n=t.selection,o=t.view.nextSelection(n&&n.kind==="node"?n.id:null,e);if(!o)return;t.select({kind:"node",id:o.id},{center:!o.visible});let i=t.view.nodeElement(o.id);i&&i.focus()}function Yn(t,e,n){if(!e||typeof e!="object")return;e.filters&&t.filters.restore(e.filters);let o=Xn(e.railTab);if(o&&(t.railTab=o),Array.isArray(e.collapsed)){t.collapsedState=e.collapsed.slice();let s=t.index;s&&t.view.setCollapsed(e.collapsed.filter(r=>s.isGroup(r)))}let i=Un(e.selection);n&&i&&(t.selection=i),typeof e.minimapCollapsed=="boolean"&&t.view.setMinimapCollapsed(e.minimapCollapsed),typeof e.flow=="boolean"&&t.setFlow(e.flow),typeof e.legendOpen=="boolean"&&t.setLegend(e.legendOpen),n&&t.index&&(t.view.relayout(),q(t),X(t),t.view.applySelection(t.selection)),e.viewport&&t.view.viewport.set(e.viewport)}function Ka(t){qn(t);let e={viewport:{...t.viewportState},selection:t.selection?{...t.selection}:null,collapsed:t.collapsedState.slice(),filters:t.filters.snapshot(),railTab:t.railTab,minimapCollapsed:t.view.minimapCollapsed};if(t.flowOn||(e.flow=!1),t.legendOpen&&(e.legendOpen=!0),t.graph&&t.workflowRevision){e.workflowRevision=t.workflowRevision;let n=xs(t.root);n&&(e.composer=n),t.railMode==="sheet"&&t.railOpen&&(e.sheetOpen=!0)}return e}function qa(t){try{return t.loadState()}catch{return null}}function Ua(t,e){Xi(e,{init:(n,o)=>{t.caps=o||t.caps,t.setTheme(n),q(t),X(t)},workflow:n=>{n!==t.workflowDocument&&t.setWorkflow(n)},workflowStatus:n=>Ss(t,n),hostNotice:(n,o)=>t.showHostNotice(n,o),stale:n=>t.setStale(n),actionResult:n=>t.onActionResult(n),theme:n=>t.setTheme(n),revealNode:(n,o)=>t.focusNode(n,{center:o,pulse:!0}),revealIssue:n=>t.focusIssue(n),restoreState:n=>Yn(t,n,!0),onUnknown:n=>t.bridge.post({v:1,type:"log",level:"debug",message:"ignored unknown message type: "+n})})}var ig=32,Fi=900,sg=620,rg=.47,Xa=.25,Ya=.75,Zn=class{constructor(e,n){this.graph=null;this.index=null;this.edgeAnchor=null;this.flowOn=!0;this.exceptionsOn=!1;this.filters=new Pt;this.viewportState={x:0,y:0,zoom:1};this.selection=null;this.collapsedState=[];this.railTab="about";this.legendOpen=!1;this.freshness=new Nt;this.openHintShown=!1;this.railOpen=!0;this.railChosen=!1;this.dockedHidden=!1;this.railWidth=360;this.railMode="docked";this.sheetFraction=rg;this.paneColumns=1;this.workflowDocument=null;this.workflowRevision=null;this.restoredView=null;this.restoredComposer=null;this.restoredTab=null;this.restoredSelection=null;this.restoredSheet=null;this.pending=new Map;this.requestSerial=0;this.disposers=[];this.destroyed=!1;this.releasePage=()=>{};this.saveSoon=Di(()=>this.bridge.saveState(this.getState()),250);this.root=e,this.bridge=n,this.caps=n.capabilities,this.themes=new Ft(e,n.theme||"light"),this.doubleClick=new Qt(e),this.disposers.push(()=>this.doubleClick.dispose()),ja(this);let o=qa(n);o&&Yn(this,o,!1),o&&typeof o.workflowRevision=="string"&&o.viewport&&(this.restoredView={revision:o.workflowRevision,viewport:{...o.viewport}});let i=o&&typeof o.workflowRevision=="string"?Es(o.composer):null;o&&i&&(this.restoredComposer={revision:o.workflowRevision,composer:i});let s=o&&typeof o.workflowRevision=="string"?Xn(o.railTab):null;o&&s&&(this.restoredTab={revision:o.workflowRevision,tab:s});let r=o&&typeof o.workflowRevision=="string"?Un(o.selection):null;o&&r&&(this.restoredSelection={revision:o.workflowRevision,selection:r}),o&&typeof o.workflowRevision=="string"&&o.sheetOpen===!0&&(this.restoredSheet=o.workflowRevision),this.disposers.push(n.onMessage(l=>this.onMessage(l))),typeof window<"u"&&this.disposers.push(_(window,"resize",()=>this.onResize())),this.disposers.push(_(e,"keydown",l=>{l.defaultPrevented||!(l.ctrlKey||l.metaKey)||l.altKey||l.key!=="f"&&l.key!=="F"||this.modalOpen(l.target)||(l.preventDefault(),this.focusSearch())})),this.chrome.setWidth(this.rootWidth()),this.view.setPanelWidth(this.rootWidth())}rootWidth(){let e=this.root.getBoundingClientRect().width;return e>0?e:0}onResize(){let e=this.chrome.headerLayout;this.chrome.setWidth(this.rootWidth())!==e&&q(this),this.view.setPanelWidth(this.rootWidth()),this.autoRail(),this.paneColumns!==this.selectionColumns()&&X(this)}selectionColumns(){return this.railMode==="sheet"&&this.rootWidth()>=sg?2:1}railShown(){return this.railOpen}refreshChrome(){q(this)}focusSearch(){this.chrome.setSearchOpen(!0),this.search.focus()}showAbout(e={}){this.railTab="about",this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),X(this),this.saveSoon();let n=e.at==="limitations"?this.rail.revealLimitations():e.focus?this.rail.activePanel():null;if(e.focus&&n)try{n.focus()}catch{}this.announce(e.at==="limitations"?"About: the coverage limitations.":"About this revision shown.")}modalOpen(e){if(this.sheet&&this.sheet.open||bo(this))return!0;let n=e;return!!n&&typeof n.closest=="function"&&!!n.closest(".mlv-sheet, .mlv-workflow__composer")}closeHeaderPanels(){return yo(this,!0)}headerPanelOpen(){return bo(this)}setWorkflow(e,n){let o=n;if(!n||!n.viewport){let s=null;this.graph&&this.workflowRevision===e.revision.id?s={...this.viewportState}:!this.graph&&this.restoredView&&this.restoredView.revision===e.revision.id&&(s={...this.restoredView.viewport}),s&&(o={...n||{},viewport:s})}this.restoredView=null;let i=this.restoredComposer&&this.restoredComposer.revision===e.revision.id?this.restoredComposer.composer:null;if(this.restoredComposer=null,this.workflowRevision!==e.revision.id){let s=!this.graph&&this.restoredTab&&this.restoredTab.revision===e.revision.id?this.restoredTab.tab:null;this.railTab=s||"about"}if(this.restoredTab=null,!this.graph){let s=r=>r===e.revision.id;this.restoredSelection&&s(this.restoredSelection.revision)&&(this.selection=this.restoredSelection.selection),s(this.restoredSheet)&&(this.railChosen=!0,this.railOpen=!0)}this.restoredSelection=null,this.restoredSheet=null,this.workflowDocument=e,this.workflowRevision=e.revision.id,this.autoRail(),Ba(this,Zt(e),o,!0),Ms(this,e,i),this.legendOtherKinds(),this.view.afterChromeChange()}postRequest(e,n){this.requestSerial+=1;let o="";for(let s=0;s<4;s++)o+=Math.floor(Math.random()*36).toString(36);let i="r"+this.requestSerial.toString(36)+"-"+o;for(this.pending.set(i,n);this.pending.size>ig;){let s=this.pending.keys().next().value;if(s===void 0)break;this.pending.delete(s)}return this.bridge.post({...e,requestId:i}),i}onActionResult(e){if(!e||typeof e.requestId!="string")return;let n=this.pending.get(e.requestId);n&&(this.pending.delete(e.requestId),n(e))}openLocation(e,n=!1){Ga(this,e,n)}setStale(e){this.freshness.set(e)&&(this.view.setStale(this.freshness.list()),this.index&&this.view.refresh(this.selection),q(this),X(this))}showHostNotice(e,n){let o=!!n&&n.length===1&&n[0]==="checking";if(this.freshness.checking!==o&&(this.freshness.checking=o,q(this)),o)return;let i=this.notice.root.hidden;this.notice.update(e,n);let s=this.root.querySelector(".mlv-body");s&&this.notice.root.nextSibling!==s&&this.root.insertBefore(this.notice.root,s),this.graph&&i!==this.notice.root.hidden&&this.view.afterChromeChange()}setMinimapCollapsed(e){this.view.setMinimapCollapsed(e),q(this),this.saveSoon(),this.announce("Overview minimap "+(e?"hidden":"shown")+".")}setLegend(e){this.legendOpen=e,this.legend.setOpen(e),q(this),this.saveSoon()}setRailTab(e){this.railTab=e,this.railMode==="sheet"&&!this.railOpen&&(this.railChosen=!0,this.setRailOpen(!0)),X(this),this.saveSoon()}showRailTab(e){this.railTab=e,this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),X(this),this.saveSoon(),this.railMode==="sheet"&&this.rail.focusTab(e)}focusRailTabs(){this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),X(this),this.rail.focusTab(this.railTab)}toggleRail(){this.railChosen=!0,this.railMode==="docked"&&(this.dockedHidden=this.railOpen),this.setRailOpen(!this.railOpen)}collapseSheet(e=!1){if(this.railMode!=="sheet"||!this.railOpen)return!1;if(this.railChosen=!0,this.setRailOpen(!1),this.announce("Bottom panel collapsed."),e)try{this.view.canvasEl.focus()}catch{}return!0}sheetFractionMax(){let e=this.root.querySelector(".mlv-body"),n=e?e.getBoundingClientRect().height:0;if(!(n>0))return Ya;let o=Math.min(240,.45*n);return Math.max(Xa,Math.min(Ya,(n-o)/n))}setSheetFraction(e){let n=Math.max(Xa,Math.min(this.sheetFractionMax(),e));Math.abs(n-this.sheetFraction)<.001||(this.sheetFraction=n,this.rail.setSheetFraction(n),this.railMode==="sheet"&&this.railOpen&&this.graph&&this.view.afterSheetToggle())}autoRail(){let e=this.root.getBoundingClientRect().width;if(!(e>0))return;let n=e-this.railWidth>=Fi?"docked":"sheet",o=this.railOpen;if(n!==this.railMode?o=n==="sheet"?this.railOpen&&this.railChosen:!this.dockedHidden:n==="docked"&&!this.railChosen&&(o=!0),n===this.railMode&&o===this.railOpen)return;let i=n!==this.railMode;this.railMode=n,this.rail.setMode(n,this.sheetFraction),this.setRailOpen(o,!0),i&&(this.saveSoon(),n==="docked"&&this.setRailWidth(this.railWidth),X(this),q(this)),this.graph&&this.view.handleResize()}legendOtherKinds(){let e=new Map,n=0;for(let i of this.graph?.edges||[])if(i.kind==="unknown")n++;else{let s=i.authoredKind||i.kind;e.set(s,(e.get(s)||0)+1)}let o=Array.from(e,([i,s])=>({kind:i,count:s})).sort((i,s)=>s.count-i.count||(i.kinds.kind?1:0));this.legend.setKinds(o,n)}showRailForFinding(){return this.railChosen=!0,this.railOpen?!1:(this.setRailOpen(!0),!0)}setRailOpen(e,n=!1){let o=this.railOpen!==e;this.railOpen=e,e&&this.railMode==="docked"&&(this.dockedHidden=!1),this.rail.setOpen(e),o&&(this.saveSoon(),this.chrome&&q(this),e&&this.rail&&X(this),!n&&this.railMode==="sheet"&&this.graph&&this.view.afterSheetToggle())}toggleShortcuts(e){if(typeof e=="boolean"?e:!this.sheet.open){this.sheet.show();return}this.sheet.hide(this.view.canvasEl)}setRailWidth(e){let n=this.rootWidth(),o=this.railMode==="docked"&&n-Fi>=280?n-Fi:560;this.railWidth=Math.max(280,Math.min(560,o,Math.round(e))),this.railMode==="docked"&&(this.rail.root.style.width=this.railWidth+"px")}setExceptions(e){this.exceptionsOn=e,e?this.view.canvasEl.setAttribute("data-exceptions","on"):this.view.canvasEl.removeAttribute("data-exceptions"),q(this),this.announce(e?"Observed claims faded; inferred and unresolved claims stand out.":"Observed claims shown normally.")}setFlow(e){this.flowOn=e,this.view.flow.setEnabled(e),this.view.flow.syncCanvas(),q(this),this.saveSoon()}select(e,n){e.kind!=="edge"&&(this.edgeAnchor=null),this.selection=e,this.railTab=n&&n.tab?n.tab:this.selectionTab(n&&n.fromList);let o=!1;if(e.kind==="issue"&&(o=this.showRailForFinding()),n&&n.showClaim&&(o=this.showRailForClaim()||o),this.view.applySelection(e),X(this),n&&n.reveal&&e.kind==="edge"?this.view.revealEdge(e.id):n&&n.center&&n.reveal?this.view.revealNode(e.id,!!n.pulse):n&&n.center?this.view.centerOnNode(e.id,!!n.pulse):o&&e.kind!=="issue"&&this.view.keepInView({kind:e.kind,id:e.id}),this.announceSelection(),n&&n.open){let i=this.locOf(e);i&&this.openLocation(i,!!n.focusEditor)}this.saveSoon()}showRailForClaim(){return this.railOpen?(this.railChosen=!0,!1):this.railMode==="docked"&&this.railChosen?!1:(this.railChosen=!0,this.setRailOpen(!0),!0)}selectionTab(e){return e&&this.railTab===e&&this.railShown()?e:"inspector"}clearSelection(){this.selection&&(this.selection=null,this.view.applySelection(null),X(this),this.saveSoon())}locOf(e){if(!this.index)return null;if(e.kind==="node"){let o=this.index.nodeById.get(e.id);return o&&o.loc.file?o.loc:null}if(e.kind==="edge"){let o=this.index.edgeById.get(e.id);return o&&o.loc.file?o.loc:null}let n=this.index.issueById.get(e.id);return n&&n.loc.file?n.loc:null}selectedNodeId(){let e=this.selection;if(!e||!this.index)return null;if(e.kind==="node")return e.id;if(e.kind!=="issue")return null;let n=this.index.issueById.get(e.id);return n&&n.nodeIds.length?n.nodeIds[0]:null}announceSelection(){let e=this.selection;if(!(!e||!this.index)){if(hl()){let n=Kl(this.index,e);n&&this.announce(n);return}if(e.kind==="node"){let n=this.view.labelOf(e.id);n&&this.announce("Selected "+n)}else if(e.kind==="issue"){let n=this.index.issueById.get(e.id);n&&this.announce("Finding "+(n.short?n.short+" ("+n.code+")":n.code)+", "+n.severity+" severity: "+n.title)}}}applyFilters(e){e(),q(this),this.index&&(this.view.render(),this.view.applySelection(this.selection)),X(this),this.saveSoon()}clearFilters(){this.search.clear(),this.applyFilters(()=>this.filters.reset())}selectLane(e){if(!this.index)return;let n=this.index.roots(e);if(!n.length){this.announce("That phase has no steps.");return}this.select({kind:"node",id:n[0]},{center:!0,pulse:!0,fromList:"outline"})}zoomToSelection(){let e=this.selectedNodeId();e&&this.view.zoomToNode(e)}announce(e){this.liveEl.textContent=e}activateHit(e){e.kind==="node"?this.focusNode(e.id,{center:!0,pulse:!0,showClaim:!0}):this.focusIssue(e.id)}onKeyDown(e){Wa(this,e)}onMessage(e){Ua(this,e)}focusNode(e,n){if(this.index){if(!this.index.nodeById.has(e)){this.view.toast("Node not found in this graph");return}this.view.expandAncestors(e),this.select({kind:"node",id:e},{center:n?n.center!==!1:!0,pulse:n?!!n.pulse:!1,showClaim:!!(n&&n.showClaim)})}}focusIssue(e,n){if(!this.index)return;let o=this.index.issueById.get(e);if(o){for(let i of o.nodeIds)this.view.expandAncestors(i);this.select({kind:"issue",id:e},{fromList:n&&n.fromList}),this.view.frameIssue(e)}}openIssue(e,n=!1){if(this.focusIssue(e,{fromList:"issues"}),!this.selection||this.selection.kind!=="issue"||this.selection.id!==e)return;let o=this.locOf({kind:"issue",id:e});o&&this.openLocation(o,n)}setFilters(e){this.applyFilters(()=>this.filters.patch(e))}setTheme(e){this.themes.apply(e)}getState(){return Ka(this)}destroy(){if(!this.destroyed){this.destroyed=!0,this.saveSoon.cancel(),this.pending.clear();for(let e of this.disposers)try{e()}catch{}this.disposers=[],this.themes.destroy(),this.chrome.destroy(),this.view.destroy(),this.releasePage(),H(this.root),this.root.classList.remove("mlv-root")}}};var Tt=null;function lg(){if(Tt)return Tt;let t=typeof window<"u"?window:{};if(typeof t.acquireVsCodeApi=="function")try{Tt=t.acquireVsCodeApi()}catch{Tt=null}return Tt}function ag(){if(typeof document>"u"||!document.body)return"light";let t=document.body.className||"";return t.indexOf("vscode-high-contrast")>=0?"hc":t.indexOf("vscode-dark")>=0?"dark":"light"}function dg(t){if(typeof window>"u")return()=>{};let e=n=>{let o=n&&n.data;o&&typeof o=="object"&&t(o)};return window.addEventListener("message",e),()=>window.removeEventListener("message",e)}function Za(){let t=lg(),e={canOpenSource:!0};return{host:"vscode",theme:ag(),capabilities:e,post(n){t&&typeof t.postMessage=="function"&&t.postMessage(n)},onMessage(n){return dg(n)},saveState(n){t&&typeof t.setState=="function"&&t.setState(n)},loadState(){if(t&&typeof t.getState=="function"){let n=t.getState();return n&&typeof n=="object"?n:null}return null}}}var cg="0.3.0";function ug(t,e,n){if(!t)throw new Error("MLView.mountWorkflow: a root element is required");if(!n)throw new Error("MLView.mountWorkflow: a HostBridge is required");let o=new Zn(t,n);return o.setWorkflow(e),o}var hg={vscode(){return Za()}};return nd(fg);})(); +"use strict";var MLView=(()=>{var No=Object.defineProperty;var xc=Object.getOwnPropertyDescriptor;var Ec=Object.getOwnPropertyNames;var kc=Object.prototype.hasOwnProperty;var _c=(t,e)=>{for(var n in e)No(t,n,{get:e[n],enumerable:!0})},Sc=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ec(e))!kc.call(t,i)&&i!==n&&No(t,i,{get:()=>e[i],enumerable:!(o=xc(e,i))||o.enumerable});return t};var Mc=t=>Sc(No({},"__esModule",{value:!0}),t);var Tb={};_c(Tb,{bridges:()=>Cb,mountWorkflow:()=>Ab,normalizeWorkflow:()=>wn,version:()=>Ib});function ke(t){if(!t||typeof t.evidenceId!="string")return null;let e=t.cell;return typeof e!="number"||!isFinite(e)||e<0||Math.trunc(e)!==e?null:{cell:e,line:t.line}}function dt(t){let e=It(t);return e.head+e.tail}function It(t){let e=ke(t);return e?{head:t.file,tail:" › cell "+e.cell+", line "+e.line}:{head:t.file,tail:":"+t.line}}function en(t){let e=ke(t);return e?t.file+" cell "+e.cell+" line "+e.line:t.file+" line "+t.line}function Ze(t){let e=ke(t);return e?"cell "+e.cell+", counted from 0 as the artifact records it (markdown cells count too); line "+e.line+" of that cell":""}var _e=["http","//www.w3.org/2000/svg"].join(":"),Ns=["http","//www.w3.org/1999/xlink"].join(":");function f(t,e,n){let o=document.createElement(t);return e&&(o.className=e),n!==void 0&&(o.textContent=n),o}function H(t,e){let n=document.createElementNS(_e,t);return e&&Se(n,e),n}function Se(t,e){for(let n of Object.keys(e))t.setAttribute(n,String(e[n]))}function D(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function m(t,e){return t.appendChild(e),e}function U(t,e,n){let o=f("button",t,e);return o.type="button",o.setAttribute("aria-label",n||e),n&&(o.title=n),o}function he(t,e){let n=f("button",t);return n.type="button",n.title=e,n.setAttribute("aria-label",e),n}function _(t,e,n,o){return t.addEventListener(e,n,o),()=>t.removeEventListener(e,n,o)}function Fs(t,e){let n=null,o=((...i)=>{n!==null&&clearTimeout(n),n=setTimeout(()=>{n=null,t(...i)},e)});return o.cancel=()=>{n!==null&&clearTimeout(n),n=null},o}function we(t){return dt(t)}function Qe(t,e,n="span"){let o=f(n,t+(t?" ":"")+"mlv-loc"),i=It(e);m(o,f("span","mlv-loc__file",i.head)),m(o,f("span","mlv-loc__at",i.tail));let s=ke(e);return s&&(o.setAttribute("data-cell",String(s.cell)),o.title=Ze(e)),o}var tn={low:"M9 2A7 7 0 1 0 9 16A7 7 0 0 0 9 2Z",medium:"M9.9 2.75a1.04 1.04 0 0 0-1.8 0L1.5 14.6a1.04 1.04 0 0 0 .9 1.56h13.2a1.04 1.04 0 0 0 .9-1.56Z",high:"M6.05 1.4h5.9L16.6 6.05v5.9L11.95 16.6h-5.9L1.4 11.95v-5.9Z"},Fo={low:"M9 4.3a1.08 1.08 0 1 0 0 2.16A1.08 1.08 0 0 0 9 4.3ZM7.94 7.65h2.12v5.7H7.94Z",medium:"M7.95 6.45h2.1l-.28 4.62h-1.54ZM9 12.1a1.12 1.12 0 1 0 0 2.24A1.12 1.12 0 0 0 9 12.1Z",high:"M7.94 4.5h2.12l-.3 6.05h-1.52ZM9 11.7a1.15 1.15 0 1 0 0 2.3A1.15 1.15 0 0 0 9 11.7Z"},ne=["high","medium","low"],ye={high:"high",medium:"medium",low:"low"};function Lc(t){return t==="low"||t==="medium"||t==="high"}function ee(t){return Lc(t)?t:"low"}function Me(){return{low:0,medium:0,high:0}}function Y(t){return t.low+t.medium+t.high}function oe(t){return t.high>0?"high":t.medium>0?"medium":t.low>0?"low":null}function Z(t,e=18,n){let o=ee(t),i=H("svg",{class:"mlv-glyph mlv-glyph--"+o,viewBox:"0 0 18 18",width:e,height:e,role:"img","aria-label":n||ye[o]+" severity"}),s=document.createElementNS(_e,"path");Se(s,{class:"mlv-glyph__shape",d:tn[o],"vector-effect":"non-scaling-stroke"});let r=document.createElementNS(_e,"path");return Se(r,{class:"mlv-glyph__ink",d:Fo[o]}),i.appendChild(s),i.appendChild(r),i}function Ds(t){let e=Y(t),n=oe(t);return e+(e===1?" finding":" findings")+", highest severity "+(n?ye[n]:"none")}function nn(t){let e=Y(t),n=ne.filter(o=>t[o]>0).map(o=>t[o]+" "+ye[o]);return e+(e===1?" finding":" findings")+": "+n.join(", ")}var Bs=2;function Bo(t){let e=t.slice(0,Bs).join(" "),n=t.length-Bs;return n>0?e+" +"+n:e}function js(t){return t.length?t.length===1?"finding "+t[0]:"findings "+t.slice(0,-1).join(", ")+" and "+t[t.length-1]:""}function Gs(t,e=18,n=[]){let o=oe(t);if(!o)return null;let i=Y(t),s=f("div","mlv-badge mlv-badge--"+o);return s.setAttribute("role","img"),s.setAttribute("aria-label",n.length?js(n).replace(/^f/,"F")+", highest severity "+ye[o]:Ds(t)),s.appendChild(Z(o,e,"")),n.length?s.appendChild(f("span","mlv-badge__ids",Bo(n))):i>1&&s.appendChild(f("span","mlv-badge__count",String(i))),s}function Ce(t,e=14,n){if(Y(t)===0)return null;let o=f("div","mlv-cluster");o.setAttribute("role","img"),o.setAttribute("aria-label",n||Ds(t)),o.title=n||nn(t);for(let i of ne){let s=t[i];if(s<=0)continue;let r=f("span","mlv-cluster__item mlv-cluster__item--"+i);r.appendChild(Z(i,e,"")),r.appendChild(f("span","mlv-cluster__count",String(s))),o.appendChild(r)}return o}function Do(t){return t/2+1.5}function Ws(t,e=16,n=[]){let o=ee(t),i=H("g",{class:"mlv-edge-marker mlv-edge-marker--"+o,role:"img"}),s=n.length?js(n)+" on this connection, highest severity "+ye[o]:ye[o]+" severity finding on this connection";if(i.setAttribute("aria-label",s.charAt(0).toUpperCase()+s.slice(1)),n.length){let l=H("title");l.textContent=i.getAttribute("aria-label")||"",i.appendChild(l)}let r=H("circle",{class:"mlv-edge-marker__disc",r:Do(e),cx:0,cy:0});i.appendChild(r);let a=Z(o,e,"");return a.setAttribute("x",String(-e/2)),a.setAttribute("y",String(-e/2)),a.removeAttribute("role"),a.removeAttribute("aria-label"),a.setAttribute("aria-hidden","true"),i.appendChild(a),i}var Ic=["changed","missing","unreadable","too-large","elsewhere"];var me={changed:"changed since publishing",missing:"file missing",unreadable:"file unreadable","too-large":"file too large to check",elsewhere:"in another folder; see the notice above"};function ae(t){return t.length>0&&t.every(e=>e==="elsewhere")}function zs(t){if(!Array.isArray(t))return[];let e=[],n=new Set;for(let o of t){if(e.length>=2e3)break;if(!o||typeof o!="object")continue;let i=o.path,s=o.reason;typeof i!="string"||!i||i.length>500||n.has(i)||typeof s!="string"||Ic.indexOf(s)<0||(n.add(i),e.push({path:i,reason:s}))}return e}var on=class{constructor(){this.reasons=new Map;this.checking=!1}set(e){let n=new Map(e.map(i=>[i.path,i.reason])),o=n.size===this.reasons.size;if(o)for(let[i,s]of n)this.reasons.get(i)!==s&&(o=!1);return this.reasons=n,!o}reasonOf(e){return e?this.reasons.get(e):void 0}get size(){return this.reasons.size}paths(){return Array.from(this.reasons.keys())}list(){return Array.from(this.reasons,([e,n])=>({path:e,reason:n}))}};function Vs(t,e){let n=t||[],o=0;for(let i of n)i.file&&e(i.file)&&o++;return{stale:o,total:n.length}}function Ac(t){if(ae(t))return"in another folder";let e=new Set(t);return e.size===1&&e.has("changed")?"changed":e.has("changed")?"changed or missing":"missing"}function Cc(t,e){if(!t||e.size===0)return null;let n=Array.from(new Set((t.evidence||[]).map(c=>c.file))),o=n.filter(c=>e.reasonOf(c)),i=e.paths().filter(c=>n.indexOf(c)<0),s=Ac(e.list().map(c=>c.reason)),r;o.length?(r=o.length+" of "+n.length+" cited files "+s,i.length&&(r+=" · "+i.length+" inspected")):r=i.length+" inspected "+(i.length===1?"file":"files")+" "+s;let a=e.list().slice(0,20).map(c=>c.path+" — "+me[c.reason]);e.size>20&&a.push("and "+(e.size-20)+" more");let d=(ae(e.list().map(c=>c.reason))?"Source files that are not under the workspace root but are unchanged in another folder; the notice above names it. Jumps into them are blocked.":"Source files that no longer match the published revision. Jumps into them are blocked.")+` +`+a.join(` +`);return{text:r,title:d}}function qs(t,e){if(!t)return null;let n=Cc(t,e);if(n)return{state:"stale",text:n.text,title:n.title};if(!t.verification)return{state:"unverified",text:"Freshness not checked",title:"This revision was published without source hashes, so MLView cannot tell whether the cited files changed since."};let o=new Set((t.evidence||[]).map(i=>i.file)).size;return{state:"unchanged",text:o+(o===1?" cited file unchanged":" cited files unchanged"),title:"The "+(o===1?"file":o+" files")+" the quotes cite still match the hashes published with revision "+t.revision.id+". Unchanged means the quoted lines still exist as published; it does not mean they support the claims."}}function Ks(t,e){if(!(!t||typeof t!="object"||t.v!==1))switch(t.type){case"init":e.init(t.theme,t.capabilities);return;case"workflow":e.workflow(t.document);return;case"workflowError":{let n=Array.isArray(t.codes)?t.codes.filter(o=>typeof o=="string"):void 0;e.workflowStatus(n),e.hostNotice(typeof t.message=="string"?t.message:"",n);return}case"stale":e.stale(zs(t.files));return;case"actionResult":e.actionResult(t);return;case"theme":e.theme(t.kind);return;case"reveal":{let n=t.id;if(typeof n!="string"||!n)return;t.kind==="node"?e.revealNode(n,!0):t.kind==="edge"?e.revealEdge(n):t.kind==="issue"&&e.revealIssue(n);return}case"revealNode":typeof t.nodeId=="string"&&t.nodeId&&e.revealNode(t.nodeId,t.center!==!1);return;case"revealIssue":typeof t.issueId=="string"&&t.issueId&&e.revealIssue(t.issueId);return;case"restoreState":e.restoreState(t.state);return;default:e.onUnknown(String(t.type))}}function Us(t,e){return!t||typeof t!="object"?{...e,severities:e.severities.slice()}:{severities:Array.isArray(t.severities)?t.severities.slice():e.severities.slice(),query:typeof t.query=="string"?t.query:""}}var Tc=["low","medium","high"];function jo(){return{severities:Tc.slice(),query:""}}var sn=class{constructor(){this.current=jo();this.keep=e=>this.current.severities.indexOf(ee(e.severity))>=0}get value(){return this.current}get query(){return this.current.query}patch(e){let n=this.snapshot();e.severities!==void 0&&(n.severities=e.severities.slice()),e.query!==void 0&&(n.query=String(e.query)),this.current=n}reset(){this.current=jo()}restore(e){this.current=Us(e,jo())}snapshot(){return{severities:this.current.severities.slice(),query:this.current.query}}toggleSeverity(e){let n=this.current.severities.slice(),o=n.indexOf(e);o>=0?n.splice(o,1):n.push(e),this.patch({severities:n})}};function Xs(t){return t==="light"||t==="dark"||t==="hc"}var rn=class{constructor(e,n){this.root=e,this.current=Xs(n)?n:"light"}get kind(){return this.current}apply(e){Xs(e)&&(this.current=e,this.root.setAttribute("data-theme",e))}destroy(){this.root.removeAttribute("data-theme")}};var Rc='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function Oc(t){let e=[];for(let n of Array.from(t.querySelectorAll(Rc))){if(n.tabIndex<0)continue;let o=!0;for(let i=n;i&&i!==t.parentElement;i=i.parentElement)if(i.hidden){o=!1;break}o&&e.push(n)}return e}function an(t,e){if(e.key!=="Tab"||e.altKey||e.ctrlKey||e.metaKey)return!1;let n=Oc(t),o=t.ownerDocument?t.ownerDocument.activeElement:null;if(!n.length)return e.preventDefault(),!0;let i=n[0],s=n[n.length-1],r=!!o&&t.contains(o),a=null;if(e.shiftKey&&(!r||o===i)?a=s:!e.shiftKey&&(!r||o===s)&&(a=i),!a)return!1;e.preventDefault();try{a.focus()}catch{}return!0}function At(t,e){let n=i=>!!i&&i.isConnected&&!i.disabled&&typeof i.focus=="function",o=i=>{if(!n(i)||i===i.ownerDocument.body)return!1;try{i.focus()}catch{return!1}return i.ownerDocument.activeElement===i};o(t)||o(e)}var Rt=8;function Ys(t){return(t%Rt+Rt)%Rt}var ln=class{constructor(e){this.nodeById=new Map;this.edgeById=new Map;this.issueById=new Map;this.issueRank=new Map;this.childrenOf=new Map;this.lexicalParentOf=new Map;this.laneChildrenOf=new Map;this.parentOf=new Map;this.rootsByLane=new Map;this.outEdges=new Map;this.inEdges=new Map;this.lanes=[];this.absentStages=[];this.phaseOrder=new Map;this.graph=e;for(let a of e.nodes||[])this.nodeById.set(a.id,a);for(let a of e.edges||[])this.edgeById.set(a.id,a);for(let a of e.issues||[])this.issueById.set(a.id,a),this.issueRank.has(a.id)||this.issueRank.set(a.id,this.issueRank.size);for(let a of e.nodes||[]){let l=a.parent&&this.nodeById.has(a.parent)?a.parent:null;this.lexicalParentOf.set(a.id,l),l&&Tt(this.childrenOf,l,a.id)}for(let a of e.nodes||[])this.hasCycle(a.id)&&this.lexicalParentOf.set(a.id,null);for(let a of e.nodes||[]){let l=this.lexicalParentOf.get(a.id)||null,d=l?this.nodeById.get(l):void 0,c=!!d&&d.stage===a.stage;this.parentOf.set(a.id,c?l:null),c?Tt(this.laneChildrenOf,l,a.id):Tt(this.rootsByLane,a.stage,a.id)}for(let a of e.edges||[])!this.nodeById.has(a.source)||!this.nodeById.has(a.target)||(Tt(this.outEdges,a.source,a),Tt(this.inEdges,a.target,a));let n=new Map;for(let a of e.stages||[])n.set(a.id,a);let o=(e.stages||[]).slice().sort((a,l)=>a.order-l.order||Hc(a.id,l.id));for(let a of o)(this.rootsByLane.get(a.id)||[]).length>0||a.present?this.lanes.push(a):this.absentStages.push(a);let i=new Set(this.lanes.map(a=>a.id)),s=[];for(let a of e.nodes||[])!n.has(a.stage)&&!i.has(a.stage)&&(i.add(a.stage),s.push(a.stage));s.sort();for(let a of o)this.phaseOrder.has(a.id)||this.phaseOrder.set(a.id,this.phaseOrder.size);for(let a of s)this.phaseOrder.set(a,this.phaseOrder.size);let r=this.lanes.length?this.lanes[this.lanes.length-1].order+1:0;for(let a of s)this.lanes.push({id:a,label:a,order:r++,present:!0,nodeCount:(this.rootsByLane.get(a)||[]).length,issueCounts:Me(),maxSeverity:null})}phaseIndexOf(e){return this.phaseOrder.get(e)??0}hasCycle(e){let n=this.lexicalParentOf.get(e)||null,o=0;for(;n;){if(n===e||++o>4096)return!0;n=this.lexicalParentOf.get(n)||null}return!1}children(e){return this.childrenOf.get(e)||[]}laneChildren(e){return this.laneChildrenOf.get(e)||[]}roots(e){return this.rootsByLane.get(e)||[]}ancestors(e){let n=[],o=this.parentOf.get(e)||null;for(;o;)n.push(o),o=this.parentOf.get(o)||null;return n}isGroup(e){return this.laneChildren(e).length>0}issuesOf(e,n){let o=this.nodeById.get(e);if(!o)return[];let i=[];for(let s of o.issueIds){let r=this.issueById.get(s);r&&n(r)&&i.push(r)}return i}issuesOfEdge(e,n){let o=this.edgeById.get(e);if(!o)return[];let i=[];for(let s of o.issueIds){let r=this.issueById.get(s);r&&n(r)&&i.push(r)}return i}issuesOfEdges(e,n){if(e.length===1)return this.issuesOfEdge(e[0],n);let o=new Set,i=[];for(let s of e)Ct(i,o,this.issuesOfEdge(s,n));return this.inDocumentOrder(i)}inDocumentOrder(e){return e.sort((n,o)=>(this.issueRank.get(n.id)??0)-(this.issueRank.get(o.id)??0))}countsFor(e){let n=Me();for(let o of e)n[ee(o.severity)]++;return n}ownCounts(e,n){return this.countsFor(this.issuesOf(e,n))}subtreeIssues(e,n){let o=[],i=l=>{o.push(l);for(let d of this.laneChildren(l))i(d)};i(e);let s=new Set(o),r=new Set,a=[];for(let l of o){Ct(a,r,this.issuesOf(l,n));for(let d of this.outEdges.get(l)||[])!s.has(d.target)||l===e&&d.target===e||Ct(a,r,this.issuesOfEdge(d.id,n))}return this.inDocumentOrder(a)}subtreeCounts(e,n){return this.countsFor(this.subtreeIssues(e,n))}descendantCount(e){let n=0;for(let o of this.laneChildren(e))n+=1+this.descendantCount(o);return n}laneCounts(e,n){let o=new Set,i=[];for(let s of this.graph.nodes||[])s.stage===e&&Ct(i,o,this.issuesOf(s.id,n));for(let s of this.graph.edges||[]){let r=this.nodeById.get(s.source);!r||r.stage!==e||Ct(i,o,this.issuesOfEdge(s.id,n))}return this.countsFor(i)}isHidden(e,n){let o=this.parentOf.get(e)||null;for(;o;){if(n.has(o))return!0;o=this.parentOf.get(o)||null}return!1}visibleRepresentative(e,n){let o=this.ancestors(e);for(let i=o.length-1;i>=0;i--)if(n.has(o[i]))return o[i];return e}defaultCollapsed(){let e=[],n=(this.graph.nodes||[]).length>120,o=this.findingTargetAncestors();for(let i of this.graph.nodes||[]){if(!this.isGroup(i.id)||o.has(i.id))continue;let s=this.descendantCount(i.id);(s>12||n&&s>4)&&e.push(i.id)}return e.sort()}findingTargetAncestors(){let e=new Set;for(let o of this.graph.issues||[]){for(let i of o.nodeIds||[])e.add(i);for(let i of o.edgeIds||[]){let s=this.edgeById.get(i);s&&(e.add(s.source),e.add(s.target))}}let n=new Set;for(let o of e)for(let i of this.ancestors(o))n.add(i);return n}};function Ct(t,e,n){for(let o of n)e.has(o.id)||(e.add(o.id),t.push(o))}function Tt(t,e,n){let o=t.get(e);o?o.push(n):t.set(e,[n])}function Hc(t,e){return te?1:0}function le(t,e){t.setAttribute("data-phase-index",String(e)),t.setAttribute("data-phase-tone",String(Ys(e)))}function Qs(t){return t.length||1}function dn(t){if(t<2)return 1.5;let e=1.5+Math.log2(t)*.75;return Math.round(Math.min(5,e)*100)/100}function Ot(t){return"×"+t}var Zs=13;function cn(t,e,n){if(!n)return{x:t.x,y:t.y};let o=typeof e=="number"&&isFinite(e)?e:0;return{x:t.x+Math.cos(o+Math.PI/2)*Zs,y:t.y+Math.sin(o+Math.PI/2)*Zs}}function un(t){return Math.round(Ot(t).length*5.6+10)}var Te=13;var $s={rankdir:"LR",ranksep:72,nodesep:24,edgesep:12,marginx:16,marginy:16,ranker:"network-simplex"},Pc={data:4,call:2,control:2,config:1};function Js(t){let e=Pc[t];return typeof e=="number"?e:1}var er=26,Go=264,hn=24,Wo=28,zo=2e3,tr=20,Vo=8,Nc=14,ge=8,fn=16,qo=12,Ko=Nc,nr=112,or=4,ir=13,sr=6,Uo=28;var Fc=6.02,gn=14,Bc=1,Dc=4,pn=8,rr=2,ar=20,lr=10,jc=3,Xo=14,Gc=.7,mn=7,Re=96;function Zo(t){return t.label?t.back?"↻ "+t.label:t.label:""}function Wc(t){return Oe(t.length*Fc+2*Dc)}function zc(t){return t.back||t.count>1||Vc.indexOf(t.kind)>=0}var Vc=["data","control","state","loop","output"];function Yo(t,e,n){return t.x-ne.x&&t.y-ne.y}var vn=class{constructor(){this.cells=new Map}add(e){let n=Math.floor(e.x/Re),o=Math.floor((e.x+e.w)/Re),i=Math.floor(e.y/Re),s=Math.floor((e.y+e.h)/Re);for(let r=n;r<=o;r++)for(let a=i;a<=s;a++){let l=r+":"+a,d=this.cells.get(l);d?d.push(e):this.cells.set(l,[e])}}hits(e,n){let o=Math.floor((e.x-n)/Re),i=Math.floor((e.x+e.w+n)/Re),s=Math.floor((e.y-n)/Re),r=Math.floor((e.y+e.h+n)/Re);for(let a=o;a<=i;a++)for(let l=s;l<=r;l++){let d=this.cells.get(a+":"+l);if(d){for(let c of d)if(Yo(e,c,n))return!0}}return!1}};function qc(t){let e=[];for(let n of t.boxes.values()){if(!n.isGroup||n.collapsed){e.push({x:n.x,y:n.y,w:n.w,h:n.h});continue}e.push({x:n.x,y:n.y,w:n.w,h:Math.max(n.headerH,1)})}return e}function Kc(t){let e=[];for(let n of t){let o=n.y+28,i=n.y+n.h-28;i-o=n.top&&e.y+e.h<=n.bottom)return n;return null}function Xc(t,e){return e.x>=0&&e.y>=0&&e.x+e.w<=t.width&&e.y+e.h<=t.height}function Yc(t,e){let n=[];for(let o=1;o0?ge:0,a=o.51){let c=s.x>i.x,u=Math.min(i.x,s.x)+(c?r:a),h=Math.max(i.x,s.x)-(c?a:r);if(h-ug.bottom||n.push({axis:"h",at:p,a:u,b:h,laneId:g.id,score:h-u,order:o})}else if(l<.51&&d>.51){let c=s.y>i.y,u=Math.min(i.y,s.y)+(c?r:a),h=Math.max(i.y,s.y)-(c?a:r);if(h-u.001?e.score-t.score:t.order-e.order}function Qc(t){if(t.length<2)return null;let e=t[0],n=t[1],o=Math.abs(n.x-e.x),i=Math.abs(n.y-e.y);return i<.51&&o>.51?{axis:"h",at:(e.y+n.y)/2,a:Math.min(e.x,n.x),b:Math.max(e.x,n.x),laneId:"",score:o,order:1}:o<.51&&i>.51?{axis:"v",at:(e.x+n.x)/2,a:Math.min(e.y,n.y),b:Math.max(e.y,n.y),laneId:"",score:i,order:1}:null}function $c(t,e){let n=e/2,o=t.axis==="h"?n:gn/2,i=(t.a+t.b)/2,s=t.a+o,r=t.b-o,a=r>s?Qo(i,s,r):i,l=r>s?(r-s)/2:0,d=[0,l*.6,-l*.6,l,-l],c=[];for(let u of d){if(c.length>=lr)break;if(u!==0&&Math.abs(u)<1)continue;let h=a+u;for(let p of[!1,!0]){if(c.length>=lr)break;let g,v;t.axis==="h"?(g=h,v=p?t.at+pn:t.at-pn):(g=p?t.at-pn-n:t.at+pn+n,v=h),c.push({x:Oe(g),y:Oe(v),rect:{x:Oe(g-n),y:Oe(v-Bc-gn/2),w:e,h:gn},flipped:p,shift:Math.abs(Oe(u))})}}return c}function dr(t){return{x:t.x-mn,y:t.y-mn,w:mn*2,h:mn*2}}function Jc(t){let e=0;for(let n=1;n=e||o===t.length-1){let s=i===0?0:Qo((e-n)/i,0,1);return{x:Oe(t[o-1].x+(t[o].x-t[o-1].x)*s),y:Oe(t[o-1].y+(t[o].y-t[o-1].y)*s)}}n+=i}return t[t.length-1]}function tu(t,e,n){if(!n||!Yo(dr(e),n,1))return{point:e,moved:!1};let o=Jc(t),i=o/2;for(let s of[18,-18,30,-30,44,-44]){let r=Qo(i+s,Math.min(8,o),Math.max(o-8,0)),a=eu(t,r);if(!Yo(dr(a),n,1))return{point:a,moved:!0}}return{point:e,moved:!1}}function cr(t,e){let n=new Map,o={labels:0,always:0,placed:0,hidden:0,flipped:0,shifted:0,fallback:0,nudged:0},i=Kc(t.lanes),s=new vn;for(let l of qc(t))s.add(l);let r=new vn,a=[[],[]];for(let l of e){if(!l.label)continue;o.labels++;let d=zc(l);d&&o.always++,a[d?0:1].push(l)}for(let l=0;l({run:I,fallback:!1}));g&&v.push({run:g,fallback:!0});let b=null,x=null,y=!1,k=null,E=null,w=null,S=null,C=0;for(let I of v){if(b||C>=ar)break;for(let F of $c(I.run,h)){if(C>=ar)break;if(C++,!Xc(t,F.rect))continue;let A=Uc(i,F.rect);if(A&&(E||(E=F,w=I.run,S=A),!s.hits(F.rect,rr)&&!r.hits(F.rect,rr))){b=F,x=I.run,y=I.fallback,k=A;break}}}if(!b&&!d&&E&&(b=E,x=w,k=S),!b){o.hidden++,n.set(c.id,{id:c.id,text:u,x:c.mid.x,y:c.mid.y,rect:{x:c.mid.x,y:c.mid.y,w:0,h:0},hidden:!0,flipped:!1,axis:v.length?v[0].run.axis:"h",laneId:null,fallback:y,shifted:0,marker:c.mid,always:d});continue}let L=tu(c.points,c.mid,b.rect);L.moved&&o.nudged++,o.placed++,b.flipped&&o.flipped++,b.shift>0&&o.shifted++,y&&o.fallback++,r.add(b.rect),n.set(c.id,{id:c.id,text:u,x:b.x,y:b.y,rect:b.rect,hidden:!1,flipped:b.flipped,axis:x?x.axis:"h",laneId:k?k.id:null,fallback:y,shifted:b.shift,marker:L.point,always:d})}}return{placements:n,stats:o}}function Qo(t,e,n){return tn?n:t}function Oe(t){return Math.round(t*100)/100}var nu=["data","call","control","config","state","loop","output"],ou={data:"data",dataflow:"data","data flow":"data",flow:"data",control:"control","control flow":"control",branch:"control","conditional call":"control",decision:"control",state:"state","state update":"state",update:"state","state read":"state","state copy":"state",gradient:"state","gradient flow":"state",optimizes:"state",ownership:"state","state ownership":"state",config:"config",configuration:"config","configuration flow":"config",call:"call",calls:"call",loop:"loop",loops:"loop",output:"output",outputs:"output"};function Jo(t){let e=String(t||"").trim();if(!e)return"unknown";let n=e.toLowerCase().replace(/[\s_-]+/g," ");return ou[n]||e}function Ht(t){return t&&t!=="unknown"?t:"kind not specified"}var ur=0;function hr(){return ur++,ur}function iu(t,e){let n="";for(let o=0;o=0?t:"unknown"}function ru(t,e,n,o){let i=H("marker",{id:t,viewBox:"0 0 10 10",refX:8.5,refY:5,markerWidth:9,markerHeight:9,markerUnits:"userSpaceOnUse",orient:"auto"}),s=H("path",{class:e+(o?"":" mlv-arrow--open"),d:n});return o||Se(s,{fill:"none"}),i.appendChild(s),i}var Pt={d:"M0.5 1 L9 5 L0.5 9 Z"},fr="mlv-arrow";function pr(){let t=H("defs");return t.appendChild(ru(fr,"mlv-arrow",Pt.d,!0)),t}var mr=14,ct=Do(mr);function ei(t){return t.placement?t.placement.marker:t.route.mid}function gr(t){let e=t.route,n=su(e.kind),o=t.weight&&t.weight>1?Math.floor(t.weight):1,i=o>=2,s=H("g",{class:"mlv-edge mlv-edge--"+n+(e.count>1?" mlv-edge--merged":"")+(i?" mlv-edge--weighted":"")+(e.back?" mlv-edge--back":""),"data-edge-id":e.id,"data-edge-kind":e.kind});s.setAttribute("data-edge-ids",e.ids.join(" ")),i&&(s.setAttribute("data-edge-weight",String(o)),s.style.setProperty("--mlv-edge-w",dn(o)+"px")),t.stage&&s.setAttribute("data-stage",t.stage),t.phase!==void 0&&le(s,t.phase),t.basis&&s.setAttribute("data-basis",t.basis),t.severity&&(s.setAttribute("data-sev",t.severity),s.classList.add("has-issue")),t.stale&&(s.classList.add("is-stale"),s.setAttribute("data-stale","1"));let r=H("path",{class:"mlv-edge__hit",d:e.d});r.setAttribute("tabindex","-1"),r.setAttribute("role","button");let a=t.staleElsewhere?" Its evidence cites a file in another folder.":" Its evidence cites a changed or missing file.";r.setAttribute("aria-label",du(e,t.sourceLabel,t.targetLabel,t.basis)+(t.stale?a:"")),s.appendChild(r);let l=H("path",{class:"mlv-edge__path",d:e.d});l.setAttribute("marker-end","url(#"+fr+")"),l.setAttribute("id",iu(t.mountSerial,e.id)),s.appendChild(l);let d=t.placement;if(e.label&&d&&d.hidden)s.setAttribute("data-label-hidden","1");else if(e.label){let u=d?d.x:e.mid.x,h=d?d.y:e.mid.y-8,p=H("text",{class:"mlv-edge-label",x:u,y:h});p.textContent=Zo(e),d&&(p.setAttribute("data-label-axis",d.axis),d.flipped&&p.setAttribute("data-label-flipped","1"),d.laneId&&p.setAttribute("data-label-lane",d.laneId)),s.appendChild(p),t.labelVisible&&s.classList.add("has-label")}let c=ei(t);if(t.severity){let u=Ws(t.severity,mr,t.findings||[]);u.setAttribute("transform","translate("+c.x+","+c.y+")"),s.appendChild(u)}else if(e.back){let u=H("path",{class:"mlv-edge__loopmark",d:"M4 -4 L0 0 L4 4",fill:"none",stroke:"var(--mlv-edge)","stroke-width":1.3,"stroke-linecap":"round","stroke-linejoin":"round",transform:"translate("+c.x+","+c.y+")"});s.appendChild(u)}return i&&s.appendChild(lu(o,c,e.midAngle,!!t.severity||e.back)),t.stale&&s.appendChild(au(c,!!t.severity||e.back||i)),s}function au(t,e){let n=t.x+(e?ct+10:0),o=H("g",{class:"mlv-edge__stale",transform:"translate("+bn(n)+","+bn(t.y)+")"});o.setAttribute("data-stale","1"),o.appendChild(H("circle",{class:"mlv-edge__stalebg",r:7.5})),o.appendChild(H("path",{class:"mlv-edge__staleicon",d:"M0 -4.6 4.9 3.9H-4.9ZM0 -1.5v2.6M0 2.7v.1",fill:"none"}));let i=H("title");return i.textContent="This connection cites a file that changed or is missing since publishing. Its jumps are blocked.",o.appendChild(i),o}function lu(t,e,n,o){let i=Ot(t),s=un(t),r=cn(e,n,o),a=H("g",{class:"mlv-edge__weight",transform:"translate("+bn(r.x)+","+bn(r.y)+")"});a.setAttribute("data-weight",String(t)),a.appendChild(H("rect",{class:"mlv-edge__weightbg",x:-s/2,y:-Te/2,width:s,height:Te,rx:Te/2}));let l=H("text",{class:"mlv-edge__weighttext",x:0,y:0});l.textContent=i,a.appendChild(l);let d=H("title");return d.textContent=t+" connections merged into this edge.",a.appendChild(d),a}function bn(t){return Math.round(t*100)/100}function du(t,e,n,o){let i=t.back?"loop-back connection":t.kind&&t.kind!=="unknown"?t.kind+" connection":"connection",s=t.label?" labelled "+t.label:"",r=e&&n?", flows from "+e+" to "+n:"",a=t.count>1?", "+t.count+" merged connections":"";return i+s+r+a+$e(o)+". Press Enter to open the cited source."}function $e(t){return t==="inferred"?", inferred, not observed":t==="unresolved"?", unresolved":""}function vr(t,e,n,o,i){let s=H("g",{class:"mlv-connector-group","data-sev":o}),r=(t.x+e.x)/2,a=(t.y+e.y)/2,l="M "+t.x+" "+t.y+" Q "+r+" "+(a-28)+" "+e.x+" "+e.y;s.appendChild(H("path",{class:"mlv-connector",d:l}));let d=H("circle",{class:"mlv-connector__badge",cx:r,cy:a-14,r:8});s.appendChild(d);let c=H("text",{class:"mlv-connector__label",x:r,y:a-14});c.textContent=String(n),s.appendChild(c);let u=H("title");return u.textContent=i,s.appendChild(u),s}function ut(t,e){let n=e.map(o=>t.get(o)).find(Boolean);return n?{file:n.file,absFile:"",line:n.line,col:0,endLine:n.endLine,endCol:0,snippet:n.quote,cell:n.cell,evidenceId:n.id}:{file:"",absFile:"",line:1,col:0,endLine:1,endCol:0}}function cu(t){return"F"+(t+1)}var uu="unspecified model";function wn(t){if(!t||t.workflowVersion!=="1.0")throw new Error("MLView.mountWorkflow: workflowVersion must be 1.0");let e=new Map((t.evidence||[]).map(u=>[u.id,u])),n=new Map,o=new Map;for(let u of t.findings||[])for(let h of u.nodeIds||[]){let p=n.get(h)||[];p.push(u.id),n.set(h,p)}for(let u of t.findings||[])for(let h of u.edgeIds||[]){let p=o.get(h)||[];p.push(u.id),o.set(h,p)}let i=new Map((t.phases||[]).map(u=>[u.id,u.label])),s=(t.nodes||[]).map(u=>({...typeof u.detail=="string"&&u.detail.trim()?{detail:u.detail}:{},...i.has(u.phase)?{phaseLabel:i.get(u.phase)}:{},id:u.id,kind:u.kind||"unknown",level:u.parent?"op":"unit",stage:u.phase,label:u.label,sublabel:u.detail||"",qualname:u.label,loc:ut(e,u.evidence),parent:u.parent||null,basis:u.basis,evidenceLocs:u.evidence.map(h=>ut(e,[h])).filter(h=>!!h.file),issueIds:n.get(u.id)||[]})),r=(t.edges||[]).map(u=>({id:u.id,kind:Jo(u.kind),source:u.source,target:u.target,...u.kind&&Jo(u.kind)!==u.kind.trim()?{authoredKind:u.kind}:{},label:u.label,loc:ut(e,u.evidence),basis:u.basis,evidenceLocs:u.evidence.map(h=>ut(e,[h])).filter(h=>!!h.file),issueIds:o.get(u.id)||[]})),a=(t.findings||[]).map((u,h)=>{let p=ut(e,u.evidence),g=(u.evidence||[]).map(v=>({item:e.get(v),role:"Supporting evidence"})).concat((u.counterEvidence||[]).map(v=>({item:e.get(v),role:"Counter-evidence"}))).filter(v=>!!v.item).map(({item:v,role:b})=>({...ut(e,[v.id]),role:b}));return{id:u.id,code:u.id,short:cu(h),severity:u.severity,basis:u.basis,title:u.title,message:u.message,fixHint:u.suggestion||"",loc:p,relatedLocs:g,nodeIds:u.nodeIds||[],edgeIds:u.edgeIds||[],stage:s.find(v=>u.nodeIds.includes(v.id))?.stage||""}}),l=new Map,d=new Map;for(let u of s)l.set(u.stage,(l.get(u.stage)||0)+1);for(let u of a){let h=d.get(u.stage)||Me();h[u.severity]++,d.set(u.stage,h)}let c=(t.phases||[]).map((u,h)=>{let p=d.get(u.id)||Me();return{id:u.id,label:u.label,order:h,present:!0,nodeCount:l.get(u.id)||0,issueCounts:p,maxSeverity:["high","medium","low"].find(g=>p[g])||null}});return{schemaVersion:"workflow-view/1",generator:{name:t.producer.host,version:t.producer.model||uu,rendererSha:t.revision.id,generatedAt:t.verification?.publishedAt||""},workspace:{root:t.title,entrypoints:t.request.entrypoints||[],filesAnalyzed:t.coverage.inspectedFiles.length},stages:c,nodes:s,edges:r,issues:a,diagnostics:t.coverage.limitations.map(u=>({kind:"workflow_limitation",message:u})),stats:{nodes:s.length,edges:r.length,issues:a.reduce((u,h)=>(u[h.severity]++,u),Me()),durationMs:0},authoredCoverage:{status:t.coverage.status,limitations:t.coverage.limitations.length}}}var ti=[["explain","Explain"],["expand","Expand"],["challenge","Challenge"],["trace","Trace"],["custom","Custom…"]];function br(t){let e=xr(t);if(!e)return;let n=wr(e.intent);if(!(!e.open&&n==="explain"&&!e.custom))return{open:e.open,intent:n,custom:e.custom}}var wr=t=>ti.find(([e])=>e===t)?.[0]??"explain";function yr(t){if(!t||typeof t!="object")return null;let e=t;return{open:e.open===!0,intent:wr(e.intent),custom:typeof e.custom=="string"?e.custom.slice(0,500):""}}function xr(t){let e=t.querySelector(".mlv-workflow"),n=t.querySelector(".mlv-workflow__composer"),o=t.querySelector(".mlv-workflow__intent"),i=t.querySelector(".mlv-workflow__custom"),s=t.querySelector(".mlv-workflow__refine");if(!e||!n||!o||!i)return null;let r=t.ownerDocument.activeElement,a=null;r&&r===s?a="refine":r&&n.contains(r)&&(a=r===o?"intent":r===i?"custom":"submit");let l=n.getAttribute("data-selection-kind"),d=n.getAttribute("data-selection-id");return{revision:e.getAttribute("data-revision"),open:!n.hidden,intent:o.value,custom:i.value,focus:a,selection:(l==="node"||l==="edge"||l==="issue")&&d?{kind:l,id:d}:void 0}}var hu=new Set(["checking","missing","unreadable","parse","invalid"]);function Er(t,e){if(!e||e.some(o=>hu.has(o)))return;let n=t.root.querySelector(".mlv-workflow__status");!n||!n.textContent||(n.textContent="",t.saveSoon())}function fu(t,e){let n=t.root.querySelector(".mlv-workflow__composer"),o=t.root.querySelector(".mlv-workflow__refine"),i=t.root.querySelector(".mlv-workflow__custom"),s=t.root.querySelector(".mlv-workflow__status");if(!(!n||!o||!i||!s)){if(e.outcome==="done"){n.hidden=!0,o.setAttribute("aria-expanded","false"),i.value="",s.textContent="";let r=He.get(n)||null;He.delete(n),At(r,o),t.saveSoon();return}n.hidden=!1,o.setAttribute("aria-expanded","true"),s.textContent=e.message||"The refinement prompt was not copied.",t.saveSoon()}}var He=new WeakMap;function pu(t,e){if(!e)return"Whole diagram";let n=t.index;if(e.kind==="node"){let i=n?n.nodeById.get(e.id):void 0;return"Step: "+(i?i.label||i.id:e.id)}if(e.kind==="edge"){let i=n?n.edgeById.get(e.id):void 0;if(!i)return"Connection: "+e.id;let s=n.nodeById.get(i.source),r=n.nodeById.get(i.target);return"Connection: "+(i.label||(s?s.label:i.source)+" → "+(r?r.label:i.target))}let o=n?n.issueById.get(e.id):void 0;return"Finding: "+(o?(o.short?o.short+" · ":"")+o.title:e.id)}function kr(t,e,n){t.root.classList.add("mlv-root--workflow"),t.root.setAttribute("data-workflow-revision",e.revision.id);let o=t.root.querySelector(".mlv-workflow");if(!o){o=f("section","mlv-workflow");let w=t.chrome.header;t.root.insertBefore(o,w.nextSibling)}let i=xr(t.root),s=i??(n?{revision:e.revision.id,open:n.open,intent:n.intent,custom:n.custom,focus:null,selection:void 0}:null),r=!i&&!!n,a=o.querySelector(".mlv-workflow__composer"),l=a&&He.get(a)||null;D(o),o.setAttribute("data-revision",e.revision.id),o.setAttribute("aria-label","Refinement");let d=f("button","mlv-btn mlv-btn--primary mlv-workflow__refine","Refine…");d.type="button",d.title="Copy a refinement prompt for your assistant about the selection or the whole diagram. MLView calls no model.",d.setAttribute("aria-expanded","false"),d.setAttribute("aria-haspopup","dialog");let c=t.chrome.refineSlot;D(c),c.appendChild(d),t.chrome.syncRoving();let u=m(o,f("form","mlv-workflow__composer"));u.hidden=!0,u.setAttribute("role","dialog"),u.setAttribute("aria-modal","true"),u.setAttribute("aria-label","Refine"),l&&He.set(u,l);let h=m(u,f("span","mlv-workflow__selection")),p=m(u,f("select","mlv-input mlv-workflow__intent"));p.setAttribute("aria-label","Refinement intent");for(let[w,S]of ti){let C=p.ownerDocument.createElement("option");C.value=w,C.textContent=S,p.appendChild(C)}let g=m(u,f("input","mlv-input mlv-workflow__custom"));g.type="text",g.maxLength=500,g.placeholder="What should the assistant refine?",g.setAttribute("aria-label","Custom refinement intent"),g.hidden=!0;let v=m(u,f("button","mlv-btn","Copy prompt"));v.type="submit";let b=m(u,f("span","mlv-workflow__status"));b.setAttribute("role","status"),m(u,f("p","mlv-workflow__hint","Copies a prompt for your assistant to paste. MLView calls no model."));let x=()=>t.selection?{kind:t.selection.kind,id:t.selection.id}:void 0,y,k=w=>{y=w,h.textContent=pu(t,w),w?(h.title=w.kind+" id: "+w.id,u.setAttribute("data-selection-kind",w.kind),u.setAttribute("data-selection-id",w.id)):(h.removeAttribute("title"),u.removeAttribute("data-selection-kind"),u.removeAttribute("data-selection-id"))},E=()=>k(x());_(d,"click",()=>{if(u.hidden){let w=d.ownerDocument.activeElement;w&&!u.contains(w)&&w!==d.ownerDocument.body?He.set(u,w):He.delete(u)}u.hidden=!u.hidden,d.setAttribute("aria-expanded",u.hidden?"false":"true"),u.hidden||(E(),p.focus()),t.saveSoon()}),_(u,"keydown",w=>{if(an(u,w)){w.stopPropagation();return}w.key==="Escape"&&(w.preventDefault(),w.stopPropagation(),yn(t,!0))}),_(p,"change",()=>{g.hidden=p.value!=="custom",g.hidden||g.focus(),t.saveSoon()}),_(g,"input",()=>t.saveSoon()),_(u,"submit",w=>{w.preventDefault();let S=p.value,C=g.value.trim();if(S==="custom"&&!C){g.focus();return}b.textContent="";let L={v:1,type:"refineWorkflow",revisionId:e.revision.id,intent:S};S==="custom"&&(L.customText=C),y&&(L.selection=y),t.postRequest(L,I=>fu(t,I))}),s&&(ti.some(([w])=>w===s.intent)&&(p.value=s.intent),g.value=s.custom,g.hidden=p.value!=="custom",s.open&&(u.hidden=!1,d.setAttribute("aria-expanded","true"),s.revision===e.revision.id&&!r?k(s.selection):E()),s.focus==="refine"?d.focus():s.focus==="intent"&&!u.hidden?p.focus():s.focus==="custom"&&!u.hidden&&!g.hidden?g.focus():s.focus==="submit"&&!u.hidden&&v.focus())}function ni(t){let e=t.root.querySelector(".mlv-workflow__composer");return!!e&&!e.hidden}function yn(t,e=!1){let n=t.root.querySelector(".mlv-workflow__composer"),o=t.root.querySelector(".mlv-workflow__refine");if(!n||n.hidden)return!1;n.hidden=!0;let i=He.get(n)||null;return He.delete(n),o&&o.setAttribute("aria-expanded","false"),e&&At(i,o),t.saveSoon(),!0}var xn=class{constructor(e){this.armed=null;this.consumed=!1;this.disposers=[];this.disposers.push(_(e,"mousedown",n=>{n.detail>=2&&(this.armed||this.consumed)&&n.preventDefault()},!0),_(e,"click",n=>this.onClick(n),!0),_(e,"dblclick",n=>{this.consumed&&(n.preventDefault(),n.stopPropagation())},!0))}arm(e,n){!e||e.detail!==1||(this.armed=n,this.consumed=!1)}dispose(){for(let e of this.disposers)e();this.disposers=[],this.armed=null}onClick(e){if(e.detail>=2&&(this.armed||this.consumed)){e.preventDefault(),e.stopPropagation();let n=this.armed;this.armed=null,this.consumed=!0,n&&n();return}this.armed=null,this.consumed=!1}};var zr=Object.defineProperty,mu=(t,e,n)=>e in t?zr(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,gu=(t,e)=>{for(var n in e)zr(t,n,{get:e[n],enumerable:!0})},vu=(t,e,n)=>mu(t,typeof e!="symbol"?e+"":e,n),Vr={};gu(Vr,{Graph:()=>ve,alg:()=>ui});var bu=Object.defineProperty,qr=(t,e)=>{for(var n in e)bu(t,n,{get:e[n],enumerable:!0})},ci=class{constructor(t){this._isDirected=!0,this._isMultigraph=!1,this._isCompound=!1,this._nodes={},this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={},this._nodeCount=0,this._edgeCount=0,this._defaultNodeLabelFn=()=>{},this._defaultEdgeLabelFn=()=>{},t&&(this._isDirected="directed"in t?t.directed:!0,this._isMultigraph="multigraph"in t?t.multigraph:!1,this._isCompound="compound"in t?t.compound:!1),this._isCompound&&(this._parent={},this._children={},this._children["\0"]={})}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(t){return this._label=t,this}graph(){return this._label}setDefaultNodeLabel(t){return typeof t!="function"?this._defaultNodeLabelFn=()=>t:this._defaultNodeLabelFn=t,this}nodeCount(){return this._nodeCount}nodes(){return Object.keys(this._nodes)}sources(){return this.nodes().filter(t=>Object.keys(this._in[t]).length===0)}sinks(){return this.nodes().filter(t=>Object.keys(this._out[t]).length===0)}setNodes(t,e){return t.forEach(n=>{e!==void 0?this.setNode(n,e):this.setNode(n)}),this}setNode(t,e){return t in this._nodes?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]="\0",this._children[t]={},this._children["\0"][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)}node(t){return this._nodes[t]}hasNode(t){return t in this._nodes}removeNode(t){if(t in this._nodes){let e=n=>this.removeEdge(this._edgeObjs[n]);delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],this.children(t).forEach(n=>{this.setParent(n)}),delete this._children[t]),Object.keys(this._in[t]).forEach(e),delete this._in[t],delete this._preds[t],Object.keys(this._out[t]).forEach(e),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this}setParent(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(e===void 0)e="\0";else{e+="";for(let n=e;n!==void 0;n=this.parent(n))if(n===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this}parent(t){if(this._isCompound){let e=this._parent[t];if(e!=="\0")return e}}children(t="\0"){if(this._isCompound){let e=this._children[t];if(e)return Object.keys(e)}else{if(t==="\0")return this.nodes();if(this.hasNode(t))return[]}return[]}predecessors(t){let e=this._preds[t];if(e)return Object.keys(e)}successors(t){let e=this._sucs[t];if(e)return Object.keys(e)}neighbors(t){let e=this.predecessors(t);if(e){let n=new Set(e),o=this.successors(t);if(o)for(let i of o)n.add(i);return Array.from(n.values())}}isLeaf(t){var e;let n;return this.isDirected()?n=this.successors(t):n=this.neighbors(t),((e=n?.length)!=null?e:0)===0}filterNodes(t){let e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph()),Object.entries(this._nodes).forEach(([i,s])=>{t(i)&&e.setNode(i,s)}),Object.values(this._edgeObjs).forEach(i=>{e.hasNode(i.v)&&e.hasNode(i.w)&&e.setEdge(i,this.edge(i))});let n={},o=i=>{let s=this.parent(i);return!s||e.hasNode(s)?(n[i]=s,s):s in n?n[s]:o(s)};return this._isCompound&&e.nodes().forEach(i=>e.setParent(i,o(i))),e}setDefaultEdgeLabel(t){return typeof t!="function"?this._defaultEdgeLabelFn=()=>t:this._defaultEdgeLabelFn=t,this}edgeCount(){return this._edgeCount}edges(){return Object.values(this._edgeObjs)}setPath(t,e){return t.reduce((n,o)=>(e!==void 0?this.setEdge(n,o,e):this.setEdge(n,o),o)),this}setEdge(t,e,n,o){let i,s,r,a,l=!1;typeof t=="object"&&t!==null&&"v"in t?(i=t.v,s=t.w,r=t.name,arguments.length===2&&(a=e,l=!0)):(i=t,s=e,r=o,arguments.length>2&&(a=n,l=!0)),i=""+i,s=""+s,r!==void 0&&(r=""+r);let d=Nt(this._isDirected,i,s,r);if(d in this._edgeLabels)return l&&(this._edgeLabels[d]=a),this;if(r!==void 0&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(i),this.setNode(s),this._edgeLabels[d]=l?a:this._defaultEdgeLabelFn(i,s,r);let c=wu(this._isDirected,i,s,r);return i=c.v,s=c.w,Object.freeze(c),this._edgeObjs[d]=c,_r(this._preds[s],i),_r(this._sucs[i],s),this._in[s][d]=c,this._out[i][d]=c,this._edgeCount++,this}edge(t,e,n){let o=arguments.length===1?oi(this._isDirected,t):Nt(this._isDirected,t,e,n);return this._edgeLabels[o]}edgeAsObj(t,e,n){let o=arguments.length===1?this.edge(t):this.edge(t,e,n);return typeof o!="object"||o===null?{label:o}:o}hasEdge(t,e,n){return(arguments.length===1?oi(this._isDirected,t):Nt(this._isDirected,t,e,n))in this._edgeLabels}removeEdge(t,e,n){let o=arguments.length===1?oi(this._isDirected,t):Nt(this._isDirected,t,e,n),i=this._edgeObjs[o];if(i){let s=i.v,r=i.w;delete this._edgeLabels[o],delete this._edgeObjs[o],Sr(this._preds[r],s),Sr(this._sucs[s],r),delete this._in[r][o],delete this._out[s][o],this._edgeCount--}return this}inEdges(t,e){return this.isDirected()?this.filterEdges(this._in[t],t,e):this.nodeEdges(t,e)}outEdges(t,e){return this.isDirected()?this.filterEdges(this._out[t],t,e):this.nodeEdges(t,e)}nodeEdges(t,e){if(t in this._nodes)return this.filterEdges({...this._in[t],...this._out[t]},t,e)}_removeFromParentsChildList(t){delete this._children[this._parent[t]][t]}filterEdges(t,e,n){if(!t)return;let o=Object.values(t);return n?o.filter(i=>i.v===e&&i.w===n||i.v===n&&i.w===e):o}};function _r(t,e){t[e]?t[e]++:t[e]=1}function Sr(t,e){t[e]!==void 0&&!--t[e]&&delete t[e]}function Nt(t,e,n,o){let i=""+e,s=""+n;if(!t&&i>s){let r=i;i=s,s=r}return i+""+s+""+(o===void 0?"\0":o)}function wu(t,e,n,o){let i=""+e,s=""+n;if(!t&&i>s){let a=i;i=s,s=a}let r={v:i,w:s};return o&&(r.name=o),r}function oi(t,e){return Nt(t,e.v,e.w,e.name)}var yu={};qr(yu,{read:()=>_u,write:()=>xu});function xu(t){let e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:Eu(t),edges:ku(t)},n=t.graph();return n!==void 0&&(e.value=structuredClone(n)),e}function Eu(t){return t.nodes().map(e=>{let n=t.node(e),o=t.parent(e),i={v:e};return n!==void 0&&(i.value=n),o!==void 0&&(i.parent=o),i})}function ku(t){return t.edges().map(e=>{let n=t.edge(e),o={v:e.v,w:e.w};return e.name!==void 0&&(o.name=e.name),n!==void 0&&(o.value=n),o})}function _u(t){let e=new ci(t.options);return t.value!==void 0&&e.setGraph(t.value),t.nodes.forEach(n=>{e.setNode(n.v,n.value),n.parent&&e.setParent(n.v,n.parent)}),t.edges.forEach(n=>{e.setEdge({v:n.v,w:n.w,name:n.name},n.value)}),e}var ui={};qr(ui,{CycleException:()=>kn,bellmanFord:()=>Kr,components:()=>Lu,dijkstra:()=>En,dijkstraAll:()=>Cu,findCycles:()=>Tu,floydWarshall:()=>Ou,isAcyclic:()=>Pu,postorder:()=>Fu,preorder:()=>Bu,prim:()=>Du,shortestPaths:()=>ju,tarjan:()=>Xr,topsort:()=>Yr});var Su=()=>1;function Kr(t,e,n,o){return Mu(t,String(e),n||Su,o||function(i){var s;return(s=t.outEdges(i))!=null?s:[]})}function Mu(t,e,n,o){let i={},s,r=0,a=t.nodes(),l=function(u){let h=i[u.v],p=i[u.w];if(!h||!p)return;let g=n(u);h.distance+gt.key)}has(t){return t in this._keyIndices}priority(t){let e=this._keyIndices[t];if(e!==void 0)return this._arr[e].priority}min(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key}add(t,e){let n=this._keyIndices,o=String(t);if(!(o in n)){let i=this._arr,s=i.length;return n[o]=s,i.push({key:o,priority:e}),this._decrease(s),!0}return!1}removeMin(){if(this.size()===0)throw new Error("Queue underflow");this._swap(0,this._arr.length-1);let t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key}decrease(t,e){let n=this._keyIndices[t];if(n===void 0)throw new Error(`Key not found: ${t}`);let o=this._arr[n].priority;if(e>o)throw new Error(`New priority is greater than current priority. Key: ${t} Old: ${o} New: ${e}`);this._arr[n].priority=e,this._decrease(n)}_heapify(t){let e=this._arr,n=2*t,o=n+1,i=t;n>1,!(e[o].priority1;function En(t,e,n,o){let i=function(s){var r;return(r=t.outEdges(s))!=null?r:[]};return Au(t,String(e),n||Iu,o||i)}function Au(t,e,n,o){let i={},s=new Ur,r,a,l=function(d){let c=d.v!==r?d.v:d.w,u=i[c];if(!u)return;let h=n(d),p=a.distance+h;if(h<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+d+" Weight: "+h);p0;){r=s.removeMin();let d=i[r];if(!d||d.distance===Number.POSITIVE_INFINITY)break;a=d,o(r).forEach(l)}return i}function Cu(t,e,n){return t.nodes().reduce(function(o,i){return o[i]=En(t,i,e,n),o},{})}function Xr(t){let e=0,n=[],o={},i=[];function s(r){var a;let l=o[r]={onStack:!0,lowlink:e,index:e++};if(n.push(r),(a=t.successors(r))==null||a.forEach(function(d){if(d in o){let c=o[d];c!=null&&c.onStack&&(l.lowlink=Math.min(l.lowlink,c.index))}else{s(d);let c=o[d];c&&(l.lowlink=Math.min(l.lowlink,c.lowlink))}}),l.lowlink===l.index){let d=[],c;do{c=n.pop();let u=o[c];u&&(u.onStack=!1),d.push(c)}while(r!==c);i.push(d)}}return t.nodes().forEach(function(r){r in o||s(r)}),i}function Tu(t){return Xr(t).filter(function(e){var n;let o=e[0];return o?e.length>1||e.length===1&&((n=t.outEdges(o,o))!=null?n:[]).length>0:!1})}var Ru=()=>1;function Ou(t,e,n){return Hu(t,e||Ru,n||function(o){var i;return(i=t.outEdges(o))!=null?i:[]})}function Hu(t,e,n){let o={},i=t.nodes();return i.forEach(function(s){let r={};o[s]=r,r[s]={distance:0,predecessor:""},i.forEach(function(a){s!==a&&(r[a]={distance:Number.POSITIVE_INFINITY,predecessor:""})}),n(s).forEach(function(a){let l=a.v===s?a.w:a.v,d=e(a);r[l]={distance:d,predecessor:s}})}),i.forEach(function(s){let r=o[s];r&&i.forEach(function(a){let l=o[a];l&&i.forEach(function(d){let c=l[s],u=r[d],h=l[d];if(c&&u&&h){let p=c.distance+u.distance;p{var l;return(l=t.isDirected()?t.successors(a):t.neighbors(a))!=null?l:[]}),r={};return e.forEach(function(a){if(!t.hasNode(a))throw new Error("Graph does not have node: "+a);i=Zr(t,a,n==="post",r,s,o,i)}),i}function Zr(t,e,n,o,i,s,r){return e in o||(o[e]=!0,n||(r=s(r,e)),i(e).forEach(function(a){r=Zr(t,a,n,o,i,s,r)}),n&&(r=s(r,e))),r}function Qr(t,e,n){return Nu(t,e,n,function(o,i){return o.push(i),o},[])}function Fu(t,e){return Qr(t,e,"post")}function Bu(t,e){return Qr(t,e,"pre")}function Du(t,e){var n;let o=new ci,i={},s=new Ur,r;function a(c){let u=c.v===r?c.w:c.v,h=s.priority(u);if(h!==void 0){let p=e(c);p0;){if(r=s.removeMin(),r in i)o.setEdge(r,i[r]);else{if(d)throw new Error("Input graph is not connected: "+t);d=!0}(n=t.nodeEdges(r))==null||n.forEach(a)}return o}function ju(t,e,n,o){return Gu(t,e,n,o??(i=>{var s;return(s=t.outEdges(i))!=null?s:[]}))}function Gu(t,e,n,o){if(n===void 0)return En(t,e,n,o);let i=!1,s=t.nodes();for(let r=0;re.setNode(n,t.node(n))),t.edges().forEach(n=>{let o=e.edge(n.v,n.w)||{weight:0,minlen:1},i=t.edge(n);e.setEdge(n.v,n.w,{weight:o.weight+i.weight,minlen:Math.max(o.minlen,i.minlen)})}),e}function $r(t){let e=new ve({multigraph:t.isMultigraph()}).setGraph(t.graph());return t.nodes().forEach(n=>{t.children(n).length||e.setNode(n,t.node(n))}),t.edges().forEach(n=>{e.setEdge(n,t.edge(n))}),e}function Mr(t,e){let n=t.x,o=t.y,i=e.x-n,s=e.y-o,r=t.width/2,a=t.height/2;if(!i&&!s)throw new Error("Not possible to find intersection inside of the rectangle");let l,d;return Math.abs(s)*r>Math.abs(i)*a?(s<0&&(a=-a),l=a*i/s,d=a):(i<0&&(r=-r),l=r,d=r*s/i),{x:n+l,y:o+d}}function Bt(t){let e=Ft(ea(t)+1).map(()=>[]);return t.nodes().forEach(n=>{let o=t.node(n),i=o.rank;i!==void 0&&(e[i]||(e[i]=[]),e[i][o.order]=n)}),e}function zu(t){let e=t.nodes().map(o=>{let i=t.node(o).rank;return i===void 0?Number.MAX_VALUE:i}),n=xe(Math.min,e);t.nodes().forEach(o=>{let i=t.node(o);Object.hasOwn(i,"rank")&&(i.rank-=n)})}function Vu(t){let e=t.nodes().map(r=>t.node(r).rank).filter(r=>r!==void 0),n=xe(Math.min,e),o=[];t.nodes().forEach(r=>{let a=t.node(r).rank-n;o[a]||(o[a]=[]),o[a].push(r)});let i=0,s=t.graph().nodeRankFactor;Array.from(o).forEach((r,a)=>{r===void 0&&a%s!==0?--i:r!==void 0&&i&&r.forEach(l=>t.node(l).rank+=i)})}function Lr(t,e,n,o){let i={width:0,height:0};return arguments.length>=4&&(i.rank=n,i.order=o),ft(t,"border",i,e)}function qu(t,e=Jr){let n=[];for(let o=0;oJr){let n=qu(e);return t(...n.map(o=>t(...o)))}else return t(...e)}function ea(t){let e=t.nodes().map(n=>{let o=t.node(n).rank;return o===void 0?Number.MIN_VALUE:o});return xe(Math.max,e)}function Ku(t,e){let n={lhs:[],rhs:[]};return t.forEach(o=>{e(o)?n.lhs.push(o):n.rhs.push(o)}),n}function Uu(t,e){let n=Date.now();try{return e()}finally{console.log(t+" time: "+(Date.now()-n)+"ms")}}function ta(t,e){return e()}var Xu=0;function hi(t){let e=++Xu;return t+(""+e)}function Ft(t,e,n=1){e==null&&(e=t,t=0);let o=s=>seo[e]:n=e,Object.entries(t).reduce((o,[i,s])=>(o[i]=n(s,i),o),{})}function Yu(t,e){return t.reduce((n,o,i)=>(n[o]=e[i],n),{})}var Mn="\0";function na(t,e,n){var o,i,s,r,a,l;if(!(t&&e&&n&&e.dummy==="edge"&&n.dummy==="edge"&&e.edgeObj&&n.edgeObj&&t[e.edgeObj.v]&&t[n.edgeObj.v]&&t[e.edgeObj.w]&&t[n.edgeObj.w]))return 0;let d=!0;e.edgeObj.w===n.edgeObj.w&&(d=!1);let c=d?(i=(o=t[e.edgeObj.v])==null?void 0:o.rank)!=null?i:NaN+1:(r=(s=t[e.edgeObj.w])==null?void 0:s.rank)!=null?r:NaN-1,u=Object.entries(t).find(v=>{var b,x;return((b=v[1].edgeObj)==null?void 0:b.v)===e.edgeObj.v&&((x=v[1].edgeObj)==null?void 0:x.w)===e.edgeObj.w&&v[1].rank===c}),h=Object.entries(t).find(v=>{var b,x;return((b=v[1].edgeObj)==null?void 0:b.v)===n.edgeObj.v&&((x=v[1].edgeObj)==null?void 0:x.w)===n.edgeObj.w&&v[1].rank===c});if(!u||!h)return 0;let p=(a=u[1].order)!=null?a:NaN,g=(l=h[1].order)!=null?l:NaN;return isNaN(p-g)?0:p-g}var Zu="3.1.1",Qu=class{constructor(){vu(this,"_sentinel");let t={};t._next=t._prev=t,this._sentinel=t}dequeue(){let t=this._sentinel,e=t._prev;if(e!==t)return Ir(e),e}enqueue(t){let e=this._sentinel;t._prev&&t._next&&Ir(t),t._next=e._next,e._next._prev=t,e._next=t,t._prev=e}toString(){let t=[],e=this._sentinel,n=e._prev;for(;n!==e;)t.push(JSON.stringify(n,$u)),n=n._prev;return"["+t.join(", ")+"]"}};function Ir(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function $u(t,e){if(t!=="_next"&&t!=="_prev")return e}var Ju=Qu,eh=()=>1;function th(t,e){if(t.nodeCount()<=1)return[];let n=oh(t,e||eh);return nh(n.graph,n.buckets,n.zeroIdx).flatMap(o=>t.outEdges(o.v,o.w)||[])}function nh(t,e,n){var o;let i=[],s=e[e.length-1],r=e[0],a;for(;t.nodeCount();){for(;a=r.dequeue();)ii(t,e,n,a);for(;a=s.dequeue();)ii(t,e,n,a);if(t.nodeCount()){for(let l=e.length-2;l>0;--l)if(a=(o=e[l])==null?void 0:o.dequeue(),a){i=i.concat(ii(t,e,n,a,!0)||[]);break}}}return i}function ii(t,e,n,o,i){let s=[],r=i?s:void 0;return(t.inEdges(o.v)||[]).forEach(a=>{let l=t.edge(a),d=t.node(a.v);i&&s.push({v:a.v,w:a.w}),d.out-=l,di(e,n,d)}),(t.outEdges(o.v)||[]).forEach(a=>{let l=t.edge(a),d=a.w,c=t.node(d);c.in-=l,di(e,n,c)}),t.removeNode(o.v),r}function oh(t,e){let n=new ve,o=0,i=0;t.nodes().forEach(a=>{n.setNode(a,{v:a,in:0,out:0})}),t.edges().forEach(a=>{let l=n.edge(a.v,a.w)||0,d=e(a),c=l+d;n.setEdge(a.v,a.w,c);let u=n.node(a.v),h=n.node(a.w);i=Math.max(i,u.out+=d),o=Math.max(o,h.in+=d)});let s=ih(i+o+3).map(()=>new Ju),r=o+1;return n.nodes().forEach(a=>{di(s,r,n.node(a))}),{graph:n,buckets:s,zeroIdx:r}}function di(t,e,n){var o,i,s;n.out?n.in?(s=t[n.out-n.in+e])==null||s.enqueue(n):(i=t[t.length-1])==null||i.enqueue(n):(o=t[0])==null||o.enqueue(n)}function ih(t){let e=[];for(let n=0;n{let i=t.edge(o);t.removeEdge(o),i.forwardName=o.name,i.reversed=!0,t.setEdge(o.w,o.v,i,hi("rev"))});function n(o){return i=>o.edge(i).weight}}function rh(t,e){let n=[],o={},i={};function s(l){Object.hasOwn(i,l)||(i[l]=!0,o[l]=!0,t.outEdges(l).forEach(d=>{Object.hasOwn(o,d.w)?n.push(d):s(d.w)}),delete o[l])}function r(l){var d;Object.hasOwn(i,l)||(i[l]=!0,o[l]=!0,(d=t.outEdges(l))==null||d.forEach(c=>{var u,h;Object.hasOwn(o,c.w)||((u=e.node(l))==null?void 0:u.rank)>((h=e.node(c.w))==null?void 0:h.rank)&&lh(t,c.w,c)?n.push(c):r(c.w)}),delete o[l])}let a=s;return e&&typeof e.node=="function"&&(a=r),t.sources().forEach(a),t.nodes().forEach(a),n}function ah(t){t.edges().forEach(e=>{let n=t.edge(e);if(n.reversed){t.removeEdge(e);let o=n.forwardName;delete n.reversed,delete n.forwardName,t.setEdge(e.w,e.v,n,o)}})}function lh(t,e,n){let o=new Set;function i(s){var r;if(t.sources().includes(s))return!0;o.add(s);for(let a of(r=t.inEdges(s))!=null?r:[])if(!(a.v===n.v&&a.w===n.w)&&!o.has(a.v)&&i(a.v))return!0;return!1}return i(e)}function dh(t){t.graph().dummyChains=[],t.edges().forEach(e=>ch(t,e))}function ch(t,e){let n=e.v,o=t.node(n).rank,i=e.w,s=t.node(i).rank,r=e.name,a=t.edge(e),l=a.labelRank;if(s===o+1)return;t.removeEdge(e);let d,c,u;for(u=0,++o;o{let n=t.node(e),o=n.edgeLabel,i;for(t.setEdge(n.edgeObj,o);n.dummy;)i=t.successors(e)[0],t.removeNode(e),o.points.push({x:n.x,y:n.y}),n.dummy==="edge-label"&&(o.x=n.x,o.y=n.y,o.width=n.width,o.height=n.height),e=i,n=t.node(e)})}function fi(t){let e={};function n(o){let i=t.node(o);if(Object.hasOwn(e,o))return i.rank;e[o]=!0;let s=t.outEdges(o),r=s?s.map(l=>l==null?Number.POSITIVE_INFINITY:n(l.w)-t.edge(l).minlen):[],a=xe(Math.min,r);return a===Number.POSITIVE_INFINITY&&(a=0),i.rank=a}t.sources().forEach(n)}function ht(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}var oa=hh;function hh(t){let e=new ve({directed:!1}),n=t.nodes();if(n.length===0)throw new Error("Graph must have at least one node");let o=n[0],i=t.nodeCount();e.setNode(o,{});let s,r;for(;fh(e,t){let r=s.v,a=o===r?s.w:r;!t.hasNode(a)&&!ht(e,s)&&(t.setNode(a,{}),t.setEdge(o,a,{}),n(a))})}return t.nodes().forEach(n),t.nodeCount()}function ph(t,e){return e.edges().reduce((n,o)=>{let i=Number.POSITIVE_INFINITY;return t.hasNode(o.v)!==t.hasNode(o.w)&&(i=ht(e,o)),ie.node(o).rank+=n)}var{preorder:gh,postorder:vh}=ui,bh=Je;Je.initLowLimValues=mi;Je.initCutValues=pi;Je.calcCutValue=ia;Je.leaveEdge=ra;Je.enterEdge=aa;Je.exchangeEdges=la;function Je(t){t=Wu(t),fi(t);let e=oa(t);mi(e),pi(e,t);let n,o;for(;n=ra(e);)o=aa(e,t,n),la(e,t,n,o)}function pi(t,e){let n=vh(t,t.nodes());n=n.slice(0,n.length-1),n.forEach(o=>wh(t,e,o))}function wh(t,e,n){let o=t.node(n).parent,i=t.edge(n,o);i.cutvalue=ia(t,e,n)}function ia(t,e,n){let o=t.node(n).parent,i=!0,s=e.edge(n,o),r=0;s||(i=!1,s=e.edge(o,n)),r=s.weight;let a=e.nodeEdges(n);return a&&a.forEach(l=>{let d=l.v===n,c=d?l.w:l.v;if(c!==o){let u=d===i,h=e.edge(l).weight;if(r+=u?h:-h,xh(t,n,c)){let p=t.edge(n,c).cutvalue;r+=u?-p:p}}}),r}function mi(t,e){arguments.length<2&&(e=t.nodes()[0]),sa(t,{},1,e)}function sa(t,e,n,o,i){let s=n,r=t.node(o);e[o]=!0;let a=t.neighbors(o);return a&&a.forEach(l=>{Object.hasOwn(e,l)||(n=sa(t,e,n,l,o))}),r.low=s,r.lim=n++,i?r.parent=i:delete r.parent,n}function ra(t){return t.edges().find(e=>t.edge(e).cutvalue<0)}function aa(t,e,n){let o=n.v,i=n.w;e.hasEdge(o,i)||(o=n.w,i=n.v);let s=t.node(o),r=t.node(i),a=s,l=!1;return s.lim>r.lim&&(a=r,l=!0),e.edges().filter(d=>l===Ar(t,t.node(d.v),a)&&l!==Ar(t,t.node(d.w),a)).reduce((d,c)=>ht(e,c)!t.node(i).parent);if(!n)return;let o=gh(t,[n]);o=o.slice(1),o.forEach(i=>{let s=t.node(i).parent,r=e.edge(i,s),a=!1;r||(r=e.edge(s,i),a=!0),e.node(i).rank=e.node(s).rank+(a?r.minlen:-r.minlen)})}function xh(t,e,n){return t.hasEdge(e,n)}function Ar(t,e,n){return n.low<=e.lim&&e.lim<=n.lim}var Eh=kh;function kh(t){let e=t.graph().ranker;if(typeof e=="function")return e(t);switch(e){case"network-simplex":Cr(t);break;case"tight-tree":Sh(t);break;case"longest-path":_h(t);break;case"none":break;default:Cr(t)}}var _h=fi;function Sh(t){fi(t),oa(t)}function Cr(t){bh(t)}var Mh=Lh;function Lh(t){let e=Ah(t),n=t.graph();Array.isArray(n.dummyChains)&&n.dummyChains.forEach(o=>{let i=t.node(o),s=i.edgeObj,r=Ih(t,e,s.v,s.w),a=r.path,l=r.lca,d=0,c=a[d],u=!0;for(;o!==s.w;){if(i=t.node(o),u){for(;(c=a[d])!==l&&t.node(c).maxRankr||a>e[l].lim));let d=l,c=o;for(;(c=t.parent(c))!==d;)s.push(c);return{path:i.concat(s.reverse()),lca:d}}function Ah(t){let e={},n=0;function o(i){let s=n;t.children(i).forEach(o),e[i]={low:s,lim:n++}}return t.children(Mn).forEach(o),e}function Ch(t){let e=ft(t,"root",{},"_root"),n=Th(t),o=Object.values(n),i=xe(Math.max,o)-1,s=2*i+1;t.graph().nestingRoot=e,t.edges().forEach(a=>t.edge(a).minlen*=s);let r=Rh(t)+1;t.children(Mn).forEach(a=>{da(t,e,s,r,i,n,a)}),t.graph().nodeRankFactor=s}function da(t,e,n,o,i,s,r){var a;let l=t.children(r);if(!l.length){r!==e&&t.setEdge(e,r,{weight:0,minlen:n});return}let d=Lr(t,"_bt"),c=Lr(t,"_bb"),u=t.node(r);t.setParent(d,r),u.borderTop=d,t.setParent(c,r),u.borderBottom=c,l.forEach(h=>{var p;da(t,e,n,o,i,s,h);let g=t.node(h),v=g.borderTop?g.borderTop:h,b=g.borderBottom?g.borderBottom:h,x=g.borderTop?o:2*o,y=v!==b?1:i-((p=s[r])!=null?p:0)+1;t.setEdge(d,v,{weight:x,minlen:y,nestingEdge:!0}),t.setEdge(b,c,{weight:x,minlen:y,nestingEdge:!0})}),t.parent(r)||t.setEdge(e,d,{weight:0,minlen:i+((a=s[r])!=null?a:0)})}function Th(t){let e={};function n(o,i){let s=t.children(o);s&&s.length&&s.forEach(r=>n(r,i+1)),e[o]=i}return t.children(Mn).forEach(o=>n(o,1)),e}function Rh(t){return t.edges().reduce((e,n)=>e+t.edge(n).weight,0)}function Oh(t){let e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,t.edges().forEach(n=>{t.edge(n).nestingEdge&&t.removeEdge(n)})}var Hh=Ph;function Ph(t){function e(n){let o=t.children(n),i=t.node(n);if(o.length&&o.forEach(e),i&&Object.hasOwn(i,"minRank")){i.borderLeft=[],i.borderRight=[];for(let s=i.minRank,r=i.maxRank+1;sRr(t.node(e))),t.edges().forEach(e=>Rr(t.edge(e)))}function Rr(t){let e=t.width;t.width=t.height,t.height=e}function Bh(t){t.nodes().forEach(e=>si(t.node(e))),t.edges().forEach(e=>{var n;let o=t.edge(e);(n=o.points)==null||n.forEach(si),Object.hasOwn(o,"y")&&si(o)})}function si(t){t.y=-t.y}function Dh(t){t.nodes().forEach(e=>ri(t.node(e))),t.edges().forEach(e=>{var n;let o=t.edge(e);(n=o.points)==null||n.forEach(ri),Object.hasOwn(o,"x")&&ri(o)})}function ri(t){let e=t.x;t.x=t.y,t.y=e}function jh(t,e=null){let n={},o=t.nodes().filter(d=>!t.children(d).length),i=o.map(d=>t.node(d).rank),s=xe(Math.max,i),r=Ft(s+1).map(()=>[]);function a(d){if(n[d])return;n[d]=!0;let c=t.node(d);r[c.rank].push(d);let u=t.successors(d);u&&[...u].sort((h,p)=>l(h,p)).forEach(a)}o.sort((d,c)=>t.node(d).rank-t.node(c).rank).forEach(a);function l(d,c){let u=t.node(d),h=t.node(c);return na(e,u,h)}return r}function Gh(t,e){let n=0;for(let o=1;oc)),i=e.flatMap(d=>{let c=t.outEdges(d);return c?c.map(u=>({pos:o[u.w],weight:t.edge(u).weight})).sort((u,h)=>u.pos-h.pos):[]}),s=1;for(;s{let c=d.pos+s;a[c]+=d.weight;let u=0;for(;c>0;)c%2&&(u+=a[c+1]),c=c-1>>1,a[c]+=d.weight;l+=d.weight*u}),l}function zh(t,e=[]){return e.map(n=>{let o=t.inEdges(n);if(!o||!o.length)return{v:n};{let i=o.reduce((s,r)=>{let a=t.edge(r),l=t.node(r.v);return{sum:s.sum+a.weight*l.order,weight:s.weight+a.weight}},{sum:0,weight:0});return{v:n,barycenter:i.sum/i.weight,weight:i.weight}}})}function Vh(t,e){let n={};t.forEach((i,s)=>{let r={indegree:0,in:[],out:[],vs:[i.v],i:s};i.barycenter!==void 0&&(r.barycenter=i.barycenter,r.weight=i.weight),n[i.v]=r}),e.edges().forEach(i=>{let s=n[i.v],r=n[i.w];s!==void 0&&r!==void 0&&(r.indegree++,s.out.push(r))});let o=Object.values(n).filter(i=>!i.indegree);return qh(o)}function qh(t){let e=[];function n(i){return s=>{s.merged||(s.barycenter===void 0||i.barycenter===void 0||s.barycenter>=i.barycenter)&&Kh(i,s)}}function o(i){return s=>{s.in.push(i),--s.indegree===0&&t.push(s)}}for(;t.length;){let i=t.pop();e.push(i),i.in.reverse().forEach(n(i)),i.out.forEach(o(i))}return e.filter(i=>!i.merged).map(i=>_n(i,["vs","i","barycenter","weight"]))}function Kh(t,e){let n=0,o=0;t.weight&&(n+=t.barycenter*t.weight,o+=t.weight),e.weight&&(n+=e.barycenter*e.weight,o+=e.weight),t.vs=e.vs.concat(t.vs),t.barycenter=n/o,t.weight=o,t.i=Math.min(e.i,t.i),e.merged=!0}function Uh(t,e,n,o,i){let s={},r=null,a=null,l=i;typeof e=="boolean"?(l=e,s={}):e&&(s=e,r=n??null,a=o??null);let d=Ku(t,x=>Object.hasOwn(x,"barycenter")),c=d.lhs,u=d.rhs.sort((x,y)=>y.i-x.i),h=[],p=0,g=0,v=0;c.sort(Xh(a,r,!!l));for(let[x,y]of Object.entries(s)){let k=c.findIndex(E=>E.vs[0]===x);c.splice(k+1,0,y)}v=Or(h,u,v),c.forEach(x=>{v+=x.vs.length,h.push(x.vs),p+=x.barycenter*x.weight,g+=x.weight,v=Or(h,u,v)});let b={vs:h.flat(1)};return g&&(b.barycenter=p/g,b.weight=g),b}function Or(t,e,n){let o;for(;e.length&&(o=e[e.length-1]).i<=n;)e.pop(),t.push(o.vs),n++;return n}function Xh(t,e,n){return(o,i)=>{if(o.barycenteri.barycenter)return 1;if(t&&(typeof o.vs[0]=="string"||typeof i.vs[0]=="string")){let s=t.node(o.vs[0]),r=t.node(i.vs[0]),a=na(e,s,r);if(a!==0)return a}return n?i.i-o.i:o.i-i.i}}function ua(t,e,n,o,i){var s,r,a,l,d,c,u,h,p,g,v;let b=null,x=i;typeof o=="boolean"?(x=o,b=null):o!==void 0&&(b=o);let y=t.children(e),k=t.node(e),E=k?k.borderLeft:void 0,w=k?k.borderRight:void 0,S={};E&&(y=y.filter(P=>P!==E&&P!==w));let C=zh(t,y);C.forEach(P=>{if(t.children(P.v).length){let{result:N}=ua(t,P.v,n,b,x);S[P.v]=N,Object.hasOwn(N,"barycenter")&&Zh(P,N)}});let L=Vh(C,n);Yh(L,S);let I={},F=!1;for(let P=0;P{n.vs=n.vs.flatMap(o=>e[o]?e[o].vs:o)})}function Zh(t,e){t.barycenter!==void 0?(t.barycenter=(t.barycenter*t.weight+e.barycenter*e.weight)/(t.weight+e.weight),t.weight+=e.weight):(t.barycenter=e.barycenter,t.weight=e.weight)}function Qh(t,e,n,o){o||(o=t.nodes());let i=$h(t),s=new ve({compound:!0}).setGraph({root:i}).setDefaultNodeLabel(r=>t.node(r));return o.forEach(r=>{let a=t.node(r),l=t.parent(r);if(a.rank===e||a.minRank<=e&&e<=a.maxRank){s.setNode(r),s.setParent(r,l||i);let d=t[n](r);d&&d.forEach(c=>{let u=c.v===r?c.w:c.v,h=s.edge(u,r),p=h!==void 0?h.weight:0;s.setEdge(u,r,{weight:t.edge(c).weight+p})}),Object.hasOwn(a,"minRank")&&s.setNode(r,{borderLeft:a.borderLeft[e],borderRight:a.borderRight[e]})}}),s}function $h(t){let e;for(;t.hasNode(e=hi("_root")););return e}function Jh(t,e,n){let o={},i;n.forEach(s=>{let r=t.parent(s),a,l;for(;r;){if(a=t.parent(r),a?(l=o[a],o[a]=r):(l=i,i=r),l&&l!==r){e.setEdge(l,r);return}r=a}})}function ha(t,e={},n=null){if(typeof e.customOrder=="function"){e.customOrder(t,ha);return}let o=ea(t),i=Hr(t,Ft(1,o+1),"inEdges"),s=Hr(t,Ft(o-1,-1,-1),"outEdges"),r=jh(t,n);if(Pr(t,r),e.disableOptimalOrderHeuristic)return;let a=Number.POSITIVE_INFINITY,l,d=e.constraints||[];for(let c=0,u=0;u<4;++c,++u){ef(c%2?i:s,c%4>=2,d,n),r=Bt(t);let h=Gh(t,r);h{o.has(s)||o.set(s,[]),o.get(s).push(r)};for(let s of t.nodes()){let r=t.node(s);if(typeof r.rank=="number"&&i(r.rank,s),typeof r.minRank=="number"&&typeof r.maxRank=="number")for(let a=r.minRank;a<=r.maxRank;a++)a!==r.rank&&i(a,s)}return e.map(function(s){return Qh(t,s,n,o.get(s)||[])})}function ef(t,e,n,o){let i=!0,s=new ve;t.forEach(function(r){n.forEach(c=>s.setEdge(c.left,c.right));let a=r.graph().root,{result:l,usedBias:d}=ua(r,a,s,o,i);e&&d&&(i=!i),l.vs.forEach((c,u)=>r.node(c).order=u),Jh(r,s,l.vs)})}function Pr(t,e){Object.values(e).forEach(n=>n.forEach((o,i)=>t.node(o).order=i))}function tf(t,e){let n={};function o(i,s){let r=0,a=0,l=i.length,d=s[s.length-1];return s.forEach((c,u)=>{let h=of(t,c),p=h?t.node(h).order:l;(h||c===d)&&(s.slice(a,u+1).forEach(g=>{let v=t.predecessors(g);v&&v.forEach(b=>{let x=t.node(b),y=x.order;(y{let u=s[c];if(u!==void 0&&t.node(u).dummy){let h=t.predecessors(u);h&&h.forEach(p=>{if(p===void 0)return;let g=t.node(p);g.dummy&&(g.orderd)&&fa(n,p,u)})}})}function i(s,r){let a=-1,l=-1,d=0;return r.forEach((c,u)=>{if(t.node(c).dummy==="border"){let h=t.predecessors(c);if(h&&h.length){let p=h[0];if(p===void 0)return;l=t.node(p).order,o(r,d,u,a,l),d=u,a=l}}o(r,d,r.length,l,s.length)}),r}return e.length&&e.reduce(i),n}function of(t,e){if(t.node(e).dummy){let n=t.predecessors(e);if(n)return n.find(o=>t.node(o).dummy)}}function fa(t,e,n){if(e>n){let i=e;e=n,n=i}let o=t[e];o||(t[e]=o={}),o[n]=!0}function sf(t,e,n){if(e>n){let i=e;e=n,n=i}let o=t[e];return o!==void 0&&Object.hasOwn(o,n)}function rf(t,e,n,o,i){let s={},r={},a={};return e.forEach(l=>{l.forEach((d,c)=>{s[d]=d,r[d]=d,a[d]=c})}),e.forEach(l=>{let d=-1,c=-1,u=!1,h=l,p=l.findIndex(g=>i?.includes(g)||Nr(g,t,i));p>0&&(h=[l[p],...l.slice(0,p),...l.slice(p+1)],u=!0),h.forEach(g=>{var v;let b=o(g);if(b&&b.length){i!=null&&i.includes(g)&&(b=b.filter(k=>Nr(k,t,i)));let x=b.sort((k,E)=>{let w=a[k],S=a[E];return(w!==void 0?w:0)-(S!==void 0?S:0)}),y=(x.length-1)/2;for(let k=Math.floor(y),E=Math.ceil(y);k<=E;++k){let w=x[k];if(w===void 0)continue;let S=a[w];if(S!==void 0&&r[g]===g&&d{var x;let y=(x=s[b.v])!=null?x:0,k=r.edge(b);return Math.max(v,y+(k!==void 0?k:0))},0):s[p]=0}function c(p){let g=r.outEdges(p),v=Number.POSITIVE_INFINITY;g&&(v=g.reduce((x,y)=>{let k=s[y.w],E=r.edge(y);return Math.min(x,(k!==void 0?k:0)-(E!==void 0?E:0))},Number.POSITIVE_INFINITY));let b=t.node(p);v!==Number.POSITIVE_INFINITY&&b.borderType!==a&&(s[p]=Math.max(s[p]!==void 0?s[p]:0,v))}function u(p){return r.predecessors(p)||[]}function h(p){return r.successors(p)||[]}return l(d,u),l(c,h),Object.keys(o).forEach(p=>{var g;let v=n[p];v!==void 0&&(s[p]=(g=s[v])!=null?g:0)}),s}function lf(t,e,n,o){let i=new ve,s=t.graph(),r=ff(s.nodesep,s.edgesep,o);return e.forEach(a=>{let l;a.forEach(d=>{let c=n[d];if(c!==void 0){if(i.setNode(c),l!==void 0){let u=n[l];if(u!==void 0){let h=i.edge(u,c);i.setEdge(u,c,Math.max(r(t,d,l),h||0))}}l=d}})}),i}function df(t,e){return Object.values(e).reduce((n,o)=>{let i=Number.NEGATIVE_INFINITY,s=Number.POSITIVE_INFINITY;Object.entries(o).forEach(([a,l])=>{let d=pf(t,a)/2;i=Math.max(l+d,i),s=Math.min(l-d,s)});let r=i-s;return r{["l","r"].forEach(r=>{let a=s+r,l=t[a];if(!l||l===e)return;let d=Object.values(l),c=o-xe(Math.min,d);r!=="l"&&(c=i-xe(Math.max,d)),c&&(t[a]=Sn(l,u=>u+c))})})}function uf(t,e=void 0){let n=t.ul;return n?Sn(n,(o,i)=>{var s,r;if(e){let l=e.toLowerCase(),d=t[l];if(d&&d[i]!==void 0)return d[i]}let a=Object.values(t).map(l=>{let d=l[i];return d!==void 0?d:0}).sort((l,d)=>l-d);return(((s=a[1])!=null?s:0)+((r=a[2])!=null?r:0))/2}):{}}function hf(t,e){let n=Bt(t),o=Object.assign(tf(t,n),nf(t,n)),i={},s;["u","d"].forEach(a=>{s=a==="u"?n:Object.values(n).reverse(),["l","r"].forEach(l=>{l==="r"&&(s=s.map(u=>Object.values(u).reverse()));let d=rf(t,s,o,u=>(a==="u"?t.predecessors(u):t.successors(u))||[],e),c=af(t,s,d.root,d.align,l==="r");l==="r"&&(c=Sn(c,u=>-u)),i[a+l]=c})});let r=df(t,i);return cf(i,r),uf(i,t.graph().align)}function ff(t,e,n){return(o,i,s)=>{let r=o.node(i),a=o.node(s),l=0,d;if(l+=r.width/2,Object.hasOwn(r,"labelpos"))switch(r.labelpos.toLowerCase()){case"l":d=-r.width/2;break;case"r":d=r.width/2;break}if(d&&(l+=n?d:-d),d=void 0,l+=(r.dummy?e:t)/2,l+=(a.dummy?e:t)/2,l+=a.width/2,Object.hasOwn(a,"labelpos"))switch(a.labelpos.toLowerCase()){case"l":d=a.width/2;break;case"r":d=-a.width/2;break}return d&&(l+=n?d:-d),l}}function pf(t,e){return t.node(e).width}function Nr(t,e,n){var o;if(!n)return!1;let i=(o=e.node(t))==null?void 0:o.edgeObj;if(!i||e.node(t).edgeLabel.reversed)return!1;let s=n.indexOf(i?.v),r=n.indexOf(i?.w);return s!==-1&&r!==-1&&s===(r+1)%n.length||s===(r-1)%n.length}function mf(t,e){t=$r(t),gf(t),Object.entries(hf(t,e)).forEach(([n,o])=>t.node(n).x=o)}function gf(t){let e=Bt(t),n=t.graph(),o=n.ranksep,i=n.rankalign,s=0;e.forEach(r=>{let a=r.reduce((l,d)=>{var c;let u=(c=t.node(d).height)!=null?c:0;return l>u?l:u},0);r.forEach(l=>{let d=t.node(l);i==="top"?d.y=s+d.height/2:i==="bottom"?d.y=s+a-d.height/2:d.y=s+a/2}),s+=a+o})}var Fr=new WeakMap;function vf(t,e={}){return pa(t,ta,e),t}function Br(t,e,n){let o=e;for(;o!==void 0;){let i=t.parent(o);if(i===n)return o;o=i}}function pa(t,e,n){var o;let i=t.nodes().filter(y=>t.children(y).length),s={};i.forEach(y=>{let k=t.node(y);if(k&&k.rankdir){let E=new ve({multigraph:!0,compound:!0});E.setGraph({rankdir:k.rankdir});let w=t.children(y);w.forEach(z=>{let j={...t.node(z)};E.setNode(z,j);let V=t.parent(z);V&&V!==y&&w.includes(V)&&E.setParent(z,V)});let S=new Set;t.edges().forEach(z=>{let j=Br(t,z.v,y),V=Br(t,z.w,y);if(j&&V&&j!==V){let $=`${j}\0${V}`;S.has($)||(S.add($),E.setEdge(j,V,{...t.edge(z)}))}}),pa(E,e,n);let C=Wr(E);Dr(C,e,n,null),jr(E,C);let L=1/0,I=1/0,F=-1/0,A=-1/0;E.nodes().forEach(z=>{if(z===y)return;let j=E.node(z);j&&typeof j.x=="number"&&typeof j.y=="number"&&typeof j.width=="number"&&typeof j.height=="number"&&(L=Math.min(L,j.x-j.width/2),F=Math.max(F,j.x+j.width/2),I=Math.min(I,j.y-j.height/2),A=Math.max(A,j.y+j.height/2))}),(!isFinite(L)||!isFinite(I)||!isFinite(F)||!isFinite(A))&&(L=I=0,F=A=0);let P=F-L,N=A-I;s[y]={minX:L,minY:I,maxX:F,maxY:A,width:P,height:N,offsetX:L,offsetY:I},k._dagreClusterSubgraph=E}});let r=[],a=y=>{let k=[],E=(t.children(y)||[]).filter(w=>w!==y);for(;E.length>0;){let w=E.shift();k.push(w),(t.children(w)||[]).filter(S=>S!==w).forEach(S=>E.push(S))}return k},l=new Map;i.forEach(y=>{let k=t.node(y);k&&k.rankdir&&s[y]&&l.set(y,(t.children(y)||[]).filter(E=>E!==y))});let d=new Set([...l.values()].flat()),c=new Map;l.forEach((y,k)=>{d.has(k)||c.set(k,a(k))});let u=new Set([...c.values()].flat()),h=y=>{for(let[k,E]of c)if(E.includes(y))return k;return y},p=[];t.edges().forEach(y=>{(u.has(y.v)||u.has(y.w))&&p.push({edge:y,label:t.edge(y)})});let g=new Map;u.forEach(y=>{let k=t.parent(y);g.set(y,typeof k=="string"?k:void 0)}),c.forEach((y,k)=>{let E=t.node(k),w=[];y.forEach(L=>{let I=t.node(L);I&&(w.push({id:L,node:I,parent:g.get(L)}),t.removeNode(L))});let S=p.filter(({edge:L})=>y.includes(L.v)||y.includes(L.w)),C=s[k];E&&(r.push({clusterId:k,subgraph:E._dagreClusterSubgraph,bounds:C,children:y,removedNodes:w,removedEdges:S}),E.width=C.width,E.height=C.height)});let v=new Set;p.forEach(({edge:y,label:k})=>{let E=h(y.v),w=h(y.w);if(E!==w&&t.hasNode(E)&&t.hasNode(w)){let S=`${E}\0${w}`;v.has(S)||(v.add(S),t.setEdge(E,w,{...k,width:0,height:0}))}});let b=Wr(t),x=Dr(b,e,n,(o=Fr.get(t))!=null?o:null);Fr.set(t,x),jr(t,b),v.forEach(y=>{let k=y.indexOf("\0"),E=y.slice(0,k),w=y.slice(k+1);t.hasEdge(E,w)&&t.removeEdge(E,w)}),r.forEach(({clusterId:y,subgraph:k,bounds:E,removedNodes:w,removedEdges:S})=>{var C,L;let I=t.node(y),F=(C=I?.x)!=null?C:0,A=(L=I?.y)!=null?L:0,P=(E.minX+E.maxX)/2,N=(E.minY+E.maxY)/2;w.forEach(({id:z,node:j,parent:V})=>{t.setNode(z,j),V!==void 0&&t.setParent(z,V)}),S.forEach(({edge:z,label:j})=>{t.setEdge(z,j)}),k.nodes().forEach(z=>{if(z===y)return;let j=k.node(z),V=t.node(z);V&&j&&typeof j.x=="number"&&typeof j.y=="number"&&(V.x=F+(j.x-P),V.y=A+(j.y-N))}),delete I._dagreClusterSubgraph}),i.forEach(y=>{var k,E;let w=t.node(y),S=s[y];if(w&&w.rankdir&&w._dagreClusterSubgraph&&S){let C=w._dagreClusterSubgraph,L=(k=w.x)!=null?k:0,I=(E=w.y)!=null?E:0,F=(S.minX+S.maxX)/2,A=(S.minY+S.maxY)/2;C.nodes().forEach(P=>{if(P===y)return;let N=C.node(P),z=t.node(P);if(z&&N&&typeof N.x=="number"&&typeof N.y=="number"){let j=N.x-F,V=N.y-A;z.x=L+j,z.y=I+V}}),delete w._dagreClusterSubgraph}})}function Dr(t,e,n,o=null){var i,s;let r=n?.useDynamic!==!1,a=r&&(i=o?.graph)!=null?i:null,l=r&&(s=o?.rawNodes)!=null?s:null;e(" makeSpaceForEdgeLabels",()=>Sf(t)),e(" removeSelfEdges",()=>Hf(t)),e(" acyclic",()=>sh(t,a)),e(" nestingGraph.run",()=>Ch(t)),e(" rank",()=>Eh($r(t))),e(" injectEdgeLabelProxies",()=>Mf(t)),e(" removeEmptyRanks",()=>Vu(t)),e(" nestingGraph.cleanup",()=>Oh(t)),e(" normalizeRanks",()=>zu(t)),e(" assignRankMinMax",()=>Lf(t)),e(" removeEdgeLabelProxies",()=>If(t)),e(" normalize.run",()=>dh(t)),e(" parentDummyChains",()=>Mh(t)),e(" addBorderSegments",()=>Hh(t)),e(" order",()=>ha(t,n,l)),e(" insertSelfEdges",()=>Pf(t)),e(" adjustCoordinateSystem",()=>Nh(t)),e(" position",()=>mf(t,n.corePath)),e(" positionSelfEdges",()=>Nf(t));let d=JSON.parse(JSON.stringify(t._nodes));return e(" removeBorderNodes",()=>Of(t)),e(" normalize.undo",()=>uh(t)),e(" fixupEdgeLabelCoords",()=>Tf(t)),e(" undoCoordinateSystem",()=>Fh(t)),e(" translateGraph",()=>Af(t)),e(" assignNodeIntersects",()=>Cf(t)),e(" reversePoints",()=>Rf(t)),e(" acyclic.undo",()=>ah(t)),{graph:t,rawNodes:d}}function jr(t,e){t.nodes().forEach(n=>{let o=t.node(n),i=e.node(n);o&&(o.x=i.x,o.y=i.y,o.order=i.order,o.rank=i.rank,e.children(n).length&&(o.width=i.width,o.height=i.height))}),t.edges().forEach(n=>{let o=t.edge(n),i=e.edge(n);o.points=i.points,Object.hasOwn(i,"x")&&(o.x=i.x,o.y=i.y)}),t.graph().width=e.graph().width,t.graph().height=e.graph().height}var bf=["nodesep","edgesep","ranksep","marginx","marginy"],wf={ranksep:50,edgesep:20,nodesep:50,rankdir:"TB",rankalign:"center"},yf=["acyclicer","ranker","rankdir","align","rankalign"],xf=["width","height","rank"],Gr={width:0,height:0},Ef=["minlen","weight","width","height","labeloffset"],kf={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},_f=["labelpos"];function Wr(t){let e=new ve({multigraph:!0,compound:!0}),n=li(t.graph());return e.setGraph(Object.assign({},wf,ai(n,bf),_n(n,yf))),t.nodes().forEach(o=>{let i=li(t.node(o)),s=ai(i,xf);Object.keys(Gr).forEach(a=>{s[a]===void 0&&(s[a]=Gr[a])}),e.setNode(o,s);let r=t.parent(o);r!==void 0&&e.setParent(o,r)}),t.edges().forEach(o=>{let i=li(t.edge(o));e.setEdge(o,Object.assign({},kf,ai(i,Ef),_n(i,_f)))}),e}function Sf(t){let e=t.graph();e.ranksep/=2,t.edges().forEach(n=>{var o;let i=t.edge(n);i.minlen*=2,((o=i.labelpos)!=null?o:"r").toLowerCase()!=="c"&&(e.rankdir==="TB"||e.rankdir==="BT"?i.width+=i.labeloffset:i.height+=i.labeloffset)})}function Mf(t){t.edges().forEach(e=>{let n=t.edge(e);if(n.width&&n.height){let o=t.node(e.v),i={rank:(t.node(e.w).rank-o.rank)/2+o.rank,e};ft(t,"edge-proxy",i,"_ep")}})}function Lf(t){let e=0;t.nodes().forEach(n=>{let o=t.node(n);o.borderTop&&(o.minRank=t.node(o.borderTop).rank,o.maxRank=t.node(o.borderBottom).rank,e=Math.max(e,o.maxRank))}),t.graph().maxRank=e}function If(t){t.nodes().forEach(e=>{let n=t.node(e);if(n.dummy==="edge-proxy"){let o=n;t.edge(o.e).labelRank=n.rank,t.removeNode(e)}})}function Af(t){let e=Number.POSITIVE_INFINITY,n=0,o=Number.POSITIVE_INFINITY,i=0,s=t.graph(),r=s.marginx||0,a=s.marginy||0;function l(d){let c=d.x,u=d.y,h=d.width,p=d.height;e=Math.min(e,c-h/2),n=Math.max(n,c+h/2),o=Math.min(o,u-p/2),i=Math.max(i,u+p/2)}t.nodes().forEach(d=>l(t.node(d))),t.edges().forEach(d=>{let c=t.edge(d);Object.hasOwn(c,"x")&&l(c)}),e-=r,o-=a,t.nodes().forEach(d=>{let c=t.node(d);c.x-=e,c.y-=o}),t.edges().forEach(d=>{let c=t.edge(d);c.points.forEach(u=>{u.x-=e,u.y-=o}),Object.hasOwn(c,"x")&&(c.x-=e),Object.hasOwn(c,"y")&&(c.y-=o)}),s.width=n-e+r,s.height=i-o+a}function Cf(t){t.edges().forEach(e=>{if(e.v===e.w)return;let n=t.edge(e),o=t.node(e.v),i=t.node(e.w),s,r;n.points?(s=n.points[0],r=n.points[n.points.length-1]):(n.points=[],s=i,r=o),n.points.unshift(Mr(o,s)),n.points.push(Mr(i,r))})}function Tf(t){t.edges().forEach(e=>{let n=t.edge(e);if(Object.hasOwn(n,"x"))switch((n.labelpos==="l"||n.labelpos==="r")&&(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset;break}})}function Rf(t){t.edges().forEach(e=>{let n=t.edge(e);n.reversed&&n.points.reverse()})}function Of(t){t.nodes().forEach(e=>{if(t.children(e).length){let n=t.node(e),o=t.node(n.borderTop),i=t.node(n.borderBottom),s=t.node(n.borderLeft[n.borderLeft.length-1]),r=t.node(n.borderRight[n.borderRight.length-1]);n.width=Math.abs(r.x-s.x),n.height=Math.abs(i.y-o.y),n.x=s.x+n.width/2,n.y=o.y+n.height/2}}),t.nodes().forEach(e=>{t.node(e).dummy==="border"&&t.removeNode(e)})}function Hf(t){t.edges().forEach(e=>{if(e.v===e.w){let n=t.node(e.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e,label:t.edge(e)}),t.removeEdge(e)}})}function Pf(t){Bt(t).forEach(e=>{let n=0;e.forEach((o,i)=>{let s=t.node(o);typeof s.rank!="number"&&(s.rank=0),s.order=i+n,(s.selfEdges||[]).forEach(r=>{ft(t,"selfedge",{width:r.label.width,height:r.label.height,rank:s.rank,order:i+ ++n,e:r.e,edgeLabel:r.label},"_se"),(!Array.isArray(r.label.points)||r.label.points.length!==7)&&(r.label.points=[{x:0,y:-10},{x:0,y:-10},{x:0,y:0},{x:0,y:10},{x:0,y:10},{x:0,y:0},{x:0,y:0}])}),delete s.selfEdges})})}function Nf(t){t.nodes().forEach(e=>{let n=t.node(e),o=i=>typeof i=="number"&&isFinite(i);if(n.dummy==="selfedge"){let i=n,s=t.node(i.e.v),r=o(s?.x)?s.x:0,a=o(s?.y)?s.y:0,l=o(s?.width)?s.width:0,d=o(s?.height)?s.height:0,c=o(n.x)?n.x:r,u=o(n.y)?n.y:a,h=l/2,p=d/2;i.edgeLabel.points=[{x:c+h,y:u-p},{x:c+h,y:u-p},{x:c,y:u},{x:c-h,y:u+p},{x:c-h,y:u+p},{x:c,y:u},{x:c,y:u}],i.edgeLabel.x=c,i.edgeLabel.y=u,t.setEdge(i.e,i.edgeLabel),t.removeNode(e)}else n&&Array.isArray(n.selfEdges)&&n.selfEdges.forEach(i=>{if(!Array.isArray(i.label.points)||i.label.points.length!==7){let s=o(n.x)?n.x:0,r=o(n.y)?n.y:0,a=o(n.width)?n.width:0,l=o(n.height)?n.height:0,d=a/2,c=l/2;i.label.points=[{x:s+d,y:r-c},{x:s+d,y:r-c},{x:s,y:r},{x:s-d,y:r+c},{x:s-d,y:r+c},{x:s,y:r},{x:s,y:r}]}})})}function ai(t,e){return Sn(_n(t,e),Number)}function li(t){let e={};return t&&Object.entries(t).forEach(([n,o])=>{typeof n=="string"&&(n=n.toLowerCase()),e[n]=o}),e}function Ff(t){let e=Bt(t),n=new ve({compound:!0,multigraph:!0}).setGraph({});return t.nodes().forEach(o=>{n.setNode(o,{label:o}),n.setParent(o,"layer"+t.node(o).rank)}),t.edges().forEach(o=>n.setEdge(o.v,o.w,{},o.name)),e.forEach((o,i)=>{let s="layer"+i;n.setNode(s,{rank:"same"}),o.reduce((r,a)=>(n.setEdge(r,a,{style:"invis"}),a))}),n}var Bf={graphlib:Vr,version:Zu,layout:vf,debug:Ff,util:{time:Uu,notime:ta}},gi=Bf;var zf=58,Vf=13*.55;function wi(t,e,n=3){let o=e+"|"+n+"|"+t,i=Ln.get(o);if(i)return i;let s=Kf(t,e,n);return Ln.size>=qf&&Ln.clear(),Ln.set(o,s),s}var qf=8192,Ln=new Map;function Kf(t,e,n){let o=Math.max(4,Math.floor((e-zf)/Vf)),i=String(t||"").trim().split(/\s+/).filter(Boolean),s=[],r="",a=!1;for(let l=0;lo;){let u=r?o-r.length-1:o;if(u>=4&&(r=r?r+" "+d.slice(0,u):d.slice(0,u),d=d.slice(u)),s.push(r),r="",s.length===n)break}if(s.length===n){a=!0;break}let c=r?r+" "+d:d;if(c.length<=o){r=c;continue}if(s.push(r),r=d,s.length===n){a=!0;break}}if(!a&&r)s.push(r);else if(a&&s.length){let l=s[s.length-1];s[s.length-1]=(l.length>=o?l.slice(0,o-1):l)+"…"}return s.length>n&&(s.length=n),s.length?s:[""]}function yi(t,e){return wi(t.label||t.qualname||t.id,e).length}function Uf(t=!1){return t}function xi(t){let e=t.loc;return!!(e&&(e.file||e.line))}function Xf(t,e=!1){let n=xi(t)||e?72:60,o=(yi(t,ga(t,e))-1)*18;return n+o+(Uf(e)?26:0)}function ga(t,e=!1){return e?248:t.kind==="model"||t.kind==="entrypoint"||t.level==="stage"?248:t.kind==="layer"?176:216}function Ei(t,e=!1){return{w:ga(t,e),h:Xf(t,e)}}function va(t,e){let n=[];for(let o of e){let i=t.node(o);i&&isFinite(i.x)&&isFinite(i.y)&&n.push({id:o,n:i})}return n}function ba(t){let e=new Map;for(let n of t){let o=Math.round(n.n.x*100)/100,i=e.get(o);i?i.push(n):e.set(o,[n])}return{keys:Array.from(e.keys()).sort((n,o)=>n-o),columns:e}}function wa(t,e){let n=va(t,e);if(n.length<2)return;let{keys:o,columns:i}=ba(n),s=0;for(let r of o){let a=i.get(r);for(let v of a)v.n.x+=s;if(a.length<2)continue;a.sort((v,b)=>v.n.y-b.n.y||(v.idb.id?1:0));let l=0,d=0;for(let v of a)l+=v.n.height,d=Math.max(d,v.n.width);if(l+=(a.length-1)*hn,l<=Go)continue;let c=Math.ceil(l/Go),u=Math.max(1,Math.ceil(a.length/c)),h=Math.min.apply(null,a.map(v=>v.n.y-v.n.height/2)),p=0,g=h;for(let v=0;v0&&v%u===0&&(p++,g=h);let b=a[v];b.n.x=r+s+p*(d+Wo),b.n.y=g+b.n.height/2,g+=b.n.height+hn}s+=p*(d+Wo)}}function ya(t,e){let n=va(t,e);if(n.length<2)return!1;let{keys:o,columns:i}=ba(n);if(o.length<2)return!1;let s=[];for(let h of o){let p=i.get(h),g=1/0,v=-1/0;for(let b of p)g=Math.min(g,b.n.x-b.n.width/2),v=Math.max(v,b.n.x+b.n.width/2);s.push({members:p,left:g,right:v})}let r=s[0].left;if(s[s.length-1].right-r<=zo)return!1;let a=[],l=[],d=r;for(let h of s)l.length>0&&h.right-d>zo&&(a.push(l),l=[],d=h.left),l.push(h);if(l.length>0&&a.push(l),a.length<2)return!1;let c=1/0;for(let h of n)c=Math.min(c,h.n.y-h.n.height/2);let u=c;for(let h of a){let p=1/0;for(let x of h)for(let y of x.members)p=Math.min(p,y.n.y-y.n.height/2);let g=r-h[0].left,v=u-p,b=u;for(let x of h)for(let y of x.members)y.n.x+=g,y.n.y+=v,b=Math.max(b,y.n.y+y.n.height/2);u=b+hn}return!0}function mt(t){return t.kind==="control"&&t.subkind==="back"}function _a(t,e){let n=new Map,o=[],i=new Map,s=new Map;for(let w of t.graph.nodes||[])s.set(w.id,w.stage);let r=(t.graph.edges||[]).filter(w=>!mt(w)),a=new Set;for(let w of t.graph.edges||[]){if(!mt(w))continue;let S=t.nodeById.get(w.source);S&&a.add(S.stage);let C=t.nodeById.get(w.target);C&&a.add(C.stage)}let l=new Set;{let w=new Map;t.lanes.forEach((S,C)=>w.set(S.id,C));for(let S of t.graph.edges||[]){if(mt(S))continue;let C=t.nodeById.get(S.source),L=t.nodeById.get(S.target);if(!C||!L)continue;let I=w.get(C.stage),F=w.get(L.stage);I===void 0||F===void 0||Math.abs(I-F)>1&&l.add(C.stage+">"+L.stage)}}let d=l.size,c=d>0,u=fn+d*Ko+qo,h=c?Math.min(u,nr):0,p=Math.max(0,h-fn-qo),g=c?Math.max(or,Math.min(Ko,Jf(p/Math.max(1,d)))):0,v=c?Math.max(1,Math.min(d,Math.floor(p/g+1e-6))):0,b=32+h,x=32,y=320;t.lanes.forEach((w,S)=>{i.set(w.id,S);let C=t.roots(w.id).filter(N=>!t.isHidden(N,e)),L=Sa(t,e,C,0,w.id,r,n),I=Math.max(L.w,264),F=(a.has(w.id)?er:0)+tr,A=Math.max(L.h,40)+F,P=28+A+28;L.place(b+28,x+28),o.push({id:w.id,label:w.label||w.id,order:w.order,index:S,x:b,y:x,w:I+56,h:P,headerH:28,nodeCount:$f(t,w.id)}),y=Math.max(y,I+56),x+=P+40});let k=b+y+32,E=Math.max(x-40+32,64);return{lanes:o,laneIndex:i,boxes:n,width:k,height:E,channelX:32+fn,hasChannel:c,channelPairs:d,channelSlots:v,channelStep:g,channelW:h}}function $f(t,e){let n=0;for(let o of t.graph.nodes||[])o.stage===e&&n++;return n}function Sa(t,e,n,o,i,s,r){if(n.length===0)return{w:0,h:0,place:()=>{}};let a=new Set(n),l=new Map,d=new Map,c=new gi.graphlib.Graph;c.setGraph({...$s}),c.setDefaultEdgeLabel(()=>({}));for(let k of n){let E=t.nodeById.get(k);if(!E)continue;let w=t.isGroup(k),S=e.has(k);if(w&&!S){let L=t.laneChildren(k),I=Sa(t,e,L,o+1,i,s,r);l.set(k,I);let F=Math.max(200,I.w+32),A=Math.max(56,34+I.h+16);d.set(k,{w:F,h:A})}else w&&S?d.set(k,Ei(E,!0)):d.set(k,Ei(E,!1));let C=d.get(k);c.setNode(k,{width:C.w,height:C.h})}let u=new Map;for(let k of s){let E=ka(t,k.source,a),w=ka(t,k.target,a);if(!E||!w||E===w)continue;let S=E+"\0"+w,L=(u.get(S)||0)+Js(k.kind);u.set(S,L),c.setEdge(E,w,{weight:L,minlen:1})}gi.layout(c),wa(c,n),ya(c,n);let h=1/0,p=1/0,g=-1/0,v=-1/0,b=new Map;for(let k of n){let E=c.node(k);if(!E)continue;let w=E.width,S=E.height,C=E.x-w/2,L=E.y-S/2;b.set(k,{x:C,y:L,w,h:S}),h=Math.min(h,C),p=Math.min(p,L),g=Math.max(g,C+w),v=Math.max(v,L+S)}isFinite(h)||(h=0,p=0,g=0,v=0);let x=Math.max(0,g-h),y=Math.max(0,v-p);return{w:x,h:y,place(k,E){for(let w of n){let S=b.get(w);if(!S)continue;let C=Cn(k+(S.x-h)),L=Cn(E+(S.y-p)),I=t.isGroup(w),F=e.has(w);r.set(w,{id:w,x:C,y:L,w:S.w,h:S.h,laneId:i,depth:o,isGroup:I,collapsed:I&&F,headerH:I&&!F?34:0});let A=l.get(w);if(A){let P=C+Math.max(16,(S.w-A.w)/2);A.place(Cn(P),Cn(L+34))}}}}}function ka(t,e,n){if(n.has(e))return e;let o=t.parentOf.get(e)||null,i=0;for(;o;){if(n.has(o))return o;if(++i>4096)return null;o=t.parentOf.get(o)||null}return null}function Cn(t){return Math.round(t*100)/100}function Jf(t){return Math.floor(t*100)/100}function Dt(t,e){let n=t.lanes[e],o=t.lanes[e+1];return n&&o?(n.y+n.h+o.y)/2:n?n.y+n.h+12:o?o.y-12:0}function La(t,e,n=ir,o=sr){if(e<=1)return 0;let i=Math.min(o,2*n/(e-1));return Tn((t-(e-1)/2)*i)}function ep(t,e,n){let o=Math.abs(t.exitY-t.entryY),i=Math.abs(e.exitY-e.entryY);if(Math.abs(o-i)>.5)return i-o;let s=n.get(t.key)??0,r=n.get(e.key)??0;return Math.abs(s-r)>.5?s-r:t.keye.key?1:0}function Ia(t,e){let n=new Map,o=new Map,i=new Map,s=new Map,r=[];for(let c of e){let u=c.sBox.laneId+">"+c.tBox.laneId,h=s.get(u);h?h.push(c):(s.set(u,[c]),r.push(u))}let a=new Map,l=[];for(let c of r){let u=s.get(c);u.sort((w,S)=>Si(w.tBox)-Si(S.tBox)||Ma(w.tBox)-Ma(S.tBox)||tp(w.key,S.key));let h=u[0],p=t.laneIndex.get(h.sBox.laneId)??0,g=t.laneIndex.get(h.tBox.laneId)??0,v=g>p,b=Math.abs(g-p)===1,x=Tn(b?Dt(t,Math.min(p,g)):v?Dt(t,p):Dt(t,p-1)),y=Tn(b?x:v?Dt(t,g-1):Dt(t,g)),k={key:c,sourceLane:h.sBox.laneId,targetLane:h.tBox.laneId,adjacent:b,down:v,members:u.map(w=>w.key),trunkX:t.channelX,entryY:x,exitY:y,slot:0};n.set(c,k);let E=0;for(let w of u)E+=Si(w.tBox);a.set(c,u.length?E/u.length:0),u.forEach((w,S)=>{o.set(w.key,k),i.set(w.key,S)}),b||l.push(k)}let d=Math.max(1,t.channelSlots);return l.sort((c,u)=>ep(c,u,a)),l.forEach((c,u)=>{c.slot=u,c.trunkX=Tn(t.channelX+Math.min(u,d-1)*t.channelStep)}),{pairs:n,pairOf:o,indexOf:i}}function Ma(t){return t.x+t.w/2}function Si(t){return t.y+t.h/2}function tp(t,e){return te?1:0}function Tn(t){return Math.round(t*100)/100}function Ie(t,e){if(t.length<2)return"";let n=["M "+t[0].x+" "+t[0].y];for(let i=1;i=o||i===t.length-1){let r=s===0?0:(o-n)/s,a=Mi(t[i-1],t[i],Math.max(0,Math.min(1,r))),l=Math.atan2(t[i].y-t[i-1].y,t[i].x-t[i-1].x)*180/Math.PI;return{point:{x:gt(a.x),y:gt(a.y)},angle:l}}n+=s}return{point:t[t.length-1],angle:0}}function gt(t){return Math.round(t*100)/100}function Rn(t,e){return Math.hypot(e.x-t.x,e.y-t.y)}function Mi(t,e,n){return{x:t.x+(e.x-t.x)*n,y:t.y+(e.y-t.y)*n}}function np(t,e){let n=new Map,o=[],i=new Map;for(let r of e.boxes.values()){o.push(r);let a=n.get(r.laneId);a?a.push(r):n.set(r.laneId,[r]);let l=new Set([r.id]);for(let d of t.ancestors(r.id))l.add(d);i.set(r.id,l)}for(let r of n.values())r.sort((a,l)=>a.x-l.x||hp(a.id,l.id));let s=new Map;for(let r of e.lanes){let a=r.y+r.headerH;for(let l of n.get(r.id)||[])a=Math.max(a,l.y+l.h);s.set(r.id,se(Math.min(a+9,r.y+r.h-5)))}return{index:t,frame:e,laneBoxes:n,ancestry:i,band:s,all:o}}function Ca(t,e,n,o){let i=t.ancestry.get(e.id);if(!i||i.has(n)||i.has(o))return!1;let s=t.ancestry.get(n);if(s&&s.has(e.id))return!1;let r=t.ancestry.get(o);return!(r&&r.has(e.id))}function op(t,e){return{x0:Math.min(t.x,e.x),x1:Math.max(t.x,e.x),y0:Math.min(t.y,e.y),y1:Math.max(t.y,e.y)}}function ip(t,e){return t.x1>e.x+1&&t.x0e.y+1&&t.y0=d||Ca(t,v,i,s)&&c.push({a:v.x-Vo,b:v.x+v.w+Vo});c.sort((v,b)=>v.a-b.a);let u=a?a.x+4:0,h=a?a.x+a.w-4:u,p=[],g=u;for(let v of c)if(!(v.b<=g)&&(v.a>g&&p.push({a:g,b:Math.min(v.a,h)}),g=Math.max(g,v.b),g>=h))break;return gv.b-v.a>=10&&v.b>u&&v.a=10&&l.push(se((h+Math.min(u.b,h+40))/2))}else{let h=Math.min(u.b,e.x-4);h-u.a>=10&&l.push(se((Math.max(u.a,h-40)+h)/2))}l.sort((u,h)=>n?u-h:h-u);let d=G(e),c=n?e.x+e.w:e.x;for(let u of l)if(!Ae(t,[T(c,d),T(u,d)],s,r))return u;return null}function vt(t,e,n,o,i,s,r){let a=n>=q(e),l=Ai(t,e,a,o,i,s,r);return l!==null?l:Ai(t,e,!a,o,i,s,r)}function Ta(t,e,n){let o=np(t,e),i=new Map,s=[];for(let h of t.graph.edges||[]){let p=t.visibleRepresentative(h.source,n),g=t.visibleRepresentative(h.target,n);if(p===g&&!(h.source===h.target&&p===h.source)||!e.boxes.has(p)||!e.boxes.has(g))continue;let v=p+"\0"+g+"\0"+h.kind+"\0"+(h.subkind||""),b=i.get(v);b||(b={edges:[],s:p,t:g},i.set(v,b),s.push(v)),b.edges.push(h)}let r=[];for(let h of s){let p=i.get(h);if(mt(p.edges[0]))continue;let g=e.boxes.get(p.s),v=e.boxes.get(p.t);g.laneId!==v.laneId&&(t.ancestors(p.s).indexOf(p.t)>=0||t.ancestors(p.t).indexOf(p.s)>=0||r.push({key:h,sBox:g,tBox:v}))}let a=Ia(e,r),l=new Map,d=new Map,c=new Map,u=[];for(let h of s){let p=i.get(h),g=p.edges[0],v=e.boxes.get(p.s),b=e.boxes.get(p.t),x=mt(g),y=v.laneId!==b.laneId,k=t.ancestors(p.s).indexOf(p.t)>=0,E=t.ancestors(p.t).indexOf(p.s)>=0,w,S;if(p.s===p.t)w=up(v,Ii(c,p.s));else if(x){let I=Ii(l,v.laneId);w=cp(o,t,e,v,b,I)}else if(E||k)w=rp(o,v,b,E);else if(y){let I=a.pairOf.get(h),F=a.indexOf.get(h)||0,A=dp(o,v,b,I,F);w=A.points,A.joinFrom>=0&&A.joinTo>A.joinFrom&&(S={key:I.key,sourceLane:I.sourceLane,targetLane:I.targetLane,index:F,size:I.members.length,axis:I.adjacent?"x":"y",channelX:I.trunkX,entryY:I.entryY,exitY:I.adjacent?I.entryY:I.exitY,joinFrom:A.joinFrom,joinTo:A.joinTo})}else{let I=Ii(d,"in:"+v.laneId);w=ap(o,e,v,b,I)}let C=p.edges.length>1?"×"+p.edges.length:g.label||"",L=Li(w);u.push({id:g.id,ids:p.edges.map(I=>I.id),kind:g.kind,subkind:g.subkind,source:p.s,target:p.t,label:C,points:w,d:Ie(w,ge),mid:L.point,midAngle:L.angle,crossLane:y,back:x,count:p.edges.length,trunk:S})}return u}function Ii(t,e){let n=(t.get(e)||0)+1;return t.set(e,n),n-1}function q(t){return t.x+t.w/2}function G(t){return t.y+t.h/2}function rp(t,e,n,o){let i=o?e:n,s=o?n:e,r=se(i.x+12),a=i.y+Math.min(28,i.h/2),l=o?[T(r,a),T(r,G(s)),T(s.x,G(s))]:[T(s.x,G(s)),T(r,G(s)),T(r,a)];if(!Ae(t,l,e.id,n.id))return l;let d=Ai(t,s,!1,G(s),G(s),e.id,n.id),c=d===null?r:d,u=d===null||d<=s.x?s.x:s.x+s.w,h=o?[T(r,a),T(r,G(s)),T(c,G(s)),T(u,G(s))]:[T(u,G(s)),T(c,G(s)),T(r,G(s)),T(r,a)];return Ae(t,h,e.id,n.id)?l:Hn(h)}function ap(t,e,n,o,i){let s=n.x+n.w,r=Aa(n,i),a=o.x,l=Aa(o,i),d;if(a>=s+16){let u=se((s+a)/2);d=Math.abs(r-l)<.5?[T(s,r),T(a,l)]:[T(s,r),T(u,r),T(u,l),T(a,l)]}else{let u=e.lanes[e.laneIndex.get(n.laneId)??0],h=Math.max(n.y+n.h,o.y+o.h)+14+i*6,p=u?u.y+u.h-6:h,g=se(Math.min(h,p));d=[T(q(n),n.y+n.h),T(q(n),g),T(q(o),g),T(q(o),o.y+o.h)]}if(!Ae(t,d,n.id,o.id))return d;let c=lp(t,n,o,i);return c&&!Ae(t,c,n.id,o.id)?c:d}function lp(t,e,n,o){let i=se((t.band.get(e.laneId)??Math.max(e.y+e.h,n.y+n.h)+12)+o*5),s=vt(t,e,q(n),G(e),i,e.id,n.id),r=vt(t,n,q(e),i,G(n),e.id,n.id);if(s===null||r===null)return null;let a=s>=q(e)?e.x+e.w:e.x,l=r>=q(n)?n.x+n.w:n.x;return Hn([T(a,G(e)),T(s,G(e)),T(s,i),T(r,i),T(r,G(n)),T(l,G(n))])}function dp(t,e,n,o,i){let s=o.down,r=La(i,o.members.length),a=s?e.y+e.h:e.y,l=s?n.y:n.y+n.h,d=se(o.entryY+r),c=d,u=se(o.exitY+r),h=o.trunkX,p=o.adjacent?[T(q(e),a),T(q(e),d),T(q(n),d),T(q(n),l)]:[T(q(e),a),T(q(e),c),T(h,c),T(h,u),T(q(n),u),T(q(n),l)],g=o.adjacent?[p[1],p[2]]:[p[2],p[3]];if(!Ae(t,p,e.id,n.id))return On(p,g);let v=o.adjacent?d:c,b=o.adjacent?d:u,x=vt(t,e,q(n),G(e),v,e.id,n.id),y=vt(t,n,q(e),b,G(n),e.id,n.id);if(x===null||y===null)return On(p,g);let k=x>=q(e)?e.x+e.w:e.x,E=y>=q(n)?n.x+n.w:n.x,w=o.adjacent?[T(k,G(e)),T(x,G(e)),T(x,d),T(y,d),T(y,G(n)),T(E,G(n))]:[T(k,G(e)),T(x,G(e)),T(x,c),T(h,c),T(h,u),T(y,u),T(y,G(n)),T(E,G(n))],S=o.adjacent?[w[2],w[3]]:[w[3],w[4]];return Ae(t,w,e.id,n.id)?On(p,g):On(Hn(w),S)}function On(t,e){return{points:t,joinFrom:t.indexOf(e[0]),joinTo:t.indexOf(e[1])}}function cp(t,e,n,o,i,s){let r=n.lanes[n.laneIndex.get(o.laneId)??0],a=Math.max(o.y+o.h,i.y+i.h),l=new Set(e.ancestors(o.id)),d=new Set(e.ancestors(i.id));for(let y of n.boxes.values()){if(y.laneId!==o.laneId||y.id!==o.id&&y.id!==i.id&&(l.has(y.id)||d.has(y.id)))continue;let k=Math.min(q(o),q(i)),E=Math.max(q(o),q(i));y.x+y.wE||(a=Math.max(a,y.y+y.h))}let c=se(Math.min(a+12+s*7,r?r.y+r.h-6:a+12)),u=d.has(o.id)||l.has(i.id)?se(i.x+Math.min(28,i.w/3)):q(i),h=[T(q(o),o.y+o.h),T(q(o),c),T(u,c),T(u,i.y+i.h)];if(!Ae(t,h,o.id,i.id))return h;let p=vt(t,o,q(i),G(o),c,o.id,i.id),g=vt(t,i,q(o),c,G(i),o.id,i.id);if(p===null||g===null)return h;let v=p>=q(o)?o.x+o.w:o.x,b=g>=q(i)?i.x+i.w:i.x,x=Hn([T(v,G(o)),T(p,G(o)),T(p,c),T(g,c),T(g,G(i)),T(b,G(i))]);return Ae(t,x,o.id,i.id)?h:x}function up(t,e){let n=14+e*6,o=t.x+t.w,i=t.y+t.h,s=i-Math.min(14+e*6,t.h/2),r=o-Math.min(28+e*6,t.w/2);return[T(o,s),T(o+n,s),T(o+n,i+n),T(r,i+n),T(r,i)]}function Aa(t,e){if(e===0)return se(G(t));let n=Math.min(t.h-16,40),o=n/4,i=(e+1>>1)*(e%2===1?1:-1);return se(G(t)+Math.max(-n/2,Math.min(n/2,i*o)))}function Hn(t){let e=[];for(let n of t){let o=e[e.length-1];o&&Math.abs(o.x-n.x)<.01&&Math.abs(o.y-n.y)<.01||e.push(n)}return e.length>=2?e:t}function hp(t,e){return te?1:0}function T(t,e){return{x:se(t),y:se(e)}}function se(t){return Math.round(t*100)/100}function Ci(t){let e=Array.from(t.boxes.values());return e.length?e.slice().sort((n,o)=>n.y-o.y||n.x-o.x)[0]:null}function Ra(t,e,n,o){let i=Array.from(e.boxes.values()),s=e.boxes.get(n);if(!s||!i.length)return Ci(e);let r=t.parentOf.get(n)||null,a=i.filter(u=>(t.parentOf.get(u.id)||null)===r&&u.laneId===s.laneId);a.length<2&&(a=i.filter(u=>u.laneId===s.laneId));let l=o==="ArrowRight"||o==="ArrowLeft",d=o==="ArrowRight"||o==="ArrowDown",c=u=>{let h=u.slice().sort((g,v)=>l?g.x-v.x||g.y-v.y:g.y-v.y||g.x-v.x),p=h.findIndex(g=>g.id===n);return p<0?null:h[p+(d?1:-1)]||null};return c(a)||c(i)}function Oa(t,e,n,o){let i=e&&t.boxes.get(e)||null,s=n&&t.boxes.get(n)||null;if(!i||!s)return i||s;let r=o==="ArrowRight"||o==="ArrowLeft",a=o==="ArrowRight"||o==="ArrowDown",l=c=>r?c.x+c.w/2:c.y+c.h/2,d=l(s)-l(i);return d===0?a?s:i:d>0===a?s:i}var fp=["is-hover","is-selected","is-lit","is-flowing","is-traced"];function pp(t){let e=t.bundle,n=t.sourceLabel||e.sourceLane,o=t.targetLabel||e.targetLane;return(e.edgeCount===1?"1 connection":e.edgeCount+" connections")+" from "+n+" to "+o+". Hover to separate them."}function Ha(t){let e=t.bundle,n=e.kinds.length===1?e.kinds[0]:"mixed",o=H("g",{class:"mlv-bundle mlv-bundle--"+n,"data-bundle-id":e.id,"data-count":String(e.edgeCount),"data-members":String(e.count),"data-axis":e.axis});o.setAttribute("data-lane-pair",e.sourceLane+">"+e.targetLane),t.stage&&o.setAttribute("data-stage",t.stage),t.phase!==void 0&&le(o,t.phase),t.severity&&(o.setAttribute("data-sev",t.severity),o.classList.add("has-issue"));let i=H("title");i.textContent=pp(t),o.appendChild(i);for(let d of e.spurs)o.appendChild(H("path",{class:"mlv-bundle__spur",d:d.d}));o.appendChild(H("path",{class:"mlv-bundle__trunk",d:e.d}));let s=String(e.edgeCount),r=15+6*s.length,a=H("g",{class:"mlv-bundle__badge",transform:"translate("+e.badge.x+","+e.badge.y+")"});a.appendChild(H("rect",{class:"mlv-bundle__badge-box",x:-r/2,y:-8,width:r,height:16,rx:8}));let l=H("text",{class:"mlv-bundle__badge-text",x:0,y:0});return l.textContent=s,a.appendChild(l),o.appendChild(a),o}var Pn=class{constructor(){this.els=new Map;this.memberOf=new Map}adopt(e,n){this.els=e,this.memberOf=new Map;for(let o of n)for(let i of o.memberIds)this.memberOf.set(i,o.id)}sync(e){if(this.els.size===0)return;let n=new Set;for(let[o,i]of this.memberOf){let s=e.get(o);s&&mp(s)&&n.add(i)}for(let[o,i]of this.els)i.classList.toggle("is-expanded",n.has(o));for(let[o,i]of this.memberOf){let s=e.get(o);s&&s.classList.toggle("is-bundled",!n.has(i))}}};function mp(t){let e=t.classList;for(let n of fp)if(e.contains(n))return!0;return!1}var Ti={entrypoint:"M5 3.2 12.4 8 5 12.8Z",config:"M2.2 4.5h11.6M2.2 8h11.6M2.2 11.5h11.6M5.6 3.1v2.8M10.4 6.6v2.8M7 10.1v2.8",dataset:"M3 4.3c0-1 2.24-1.8 5-1.8s5 .8 5 1.8-2.24 1.8-5 1.8-5-.8-5-1.8ZM3 4.3v3.4c0 1 2.24 1.8 5 1.8s5-.8 5-1.8V4.3M3 7.7v3.4c0 1 2.24 1.8 5 1.8s5-.8 5-1.8V7.7",dataloader:"M2 4.2h2.6l6.8 7.6H14M2 11.8h2.6l6.8-7.6H14M11.6 2.4 14 4.2l-2.4 1.8M11.6 10 14 11.8l-2.4 1.8",split:"M2 8h3.6l3.4-4.2H14M9 12.2 5.6 8H2M11.8 2 14 3.8l-2.2 1.8M11.8 10.4 14 12.2l-2.2 1.8",transform:"M2.4 13.6 10 6M8.4 4.4l3.2 3.2M12 1.8v2.8M10.6 3.2h2.8M4 2v1.8M3.1 2.9h1.8",augment:"M5.2 2.2 6.1 5 8.9 5.9 6.1 6.8 5.2 9.6 4.3 6.8 1.5 5.9 4.3 5ZM11.4 8.2l.7 1.9 1.9.7-1.9.7-.7 1.9-.7-1.9-1.9-.7 1.9-.7Z",model:"M8 2 14 5 8 8 2 5ZM2 8.2 8 11.2 14 8.2M2 11.2 8 14.2 14 11.2",layer:"M2.4 4.6h11.2v2.4H2.4ZM2.4 9h11.2v2.4H2.4Z",loss:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM8 5.4A2.6 2.6 0 1 0 8 10.6 2.6 2.6 0 0 0 8 5.4ZM8 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2Z",optimizer:"M2.6 12.2a6.4 6.4 0 1 1 10.8 0M8 8.4 11.2 5.2M7.2 12.2h1.6",scheduler:"M2 12.4 5.6 8.6l2.4 2.2L13.4 4.6M9.6 4.6h3.8v3.8",scaler:"M2.4 4h11.2v8H2.4ZM5.4 4v2.4M8 4v3.6M10.6 4v2.4",train_loop:"M13.2 8A5.2 5.2 0 1 1 11.5 4.1M13.6 2.6v3.2h-3.2",eval_loop:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM5.2 8.1 7.2 10.2 11 6",metric:"M3 13V8.4M6.4 13V4.4M9.8 13v-3.4M13.2 13V6.4M2 13.8h12",checkpoint:"M2.6 3h7.6l3.2 3.2v7.4H2.6ZM5.6 3v3.4h4.6V3M5.6 13.6v-3.6h4.6v3.6",tracker:"M8 6.8A1.2 1.2 0 1 0 8 9.2 1.2 1.2 0 0 0 8 6.8ZM5.2 5.2a4 4 0 0 0 0 5.6M10.8 5.2a4 4 0 0 1 0 5.6M3.1 3.1a7 7 0 0 0 0 9.8M12.9 3.1a7 7 0 0 1 0 9.8",predict:"M9.2 1.6 4 8.6h3.3l-.7 5.8L12 7.2H8.5Z",function:"M6.6 2.8c-1.7 0-2.1.9-2.1 2.3v1.1c0 1-.5 1.6-1.5 1.6 1 0 1.5.6 1.5 1.6v1.1c0 1.4.4 2.3 2.1 2.3M9.4 2.8c1.7 0 2.1.9 2.1 2.3v1.1c0 1 .5 1.6 1.5 1.6-1 0-1.5.6-1.5 1.6v1.1c0 1.4-.4 2.3-2.1 2.3",class:"M3 2.6h10v10.8H3ZM3 6.2h10M5.4 8.8h5.2M5.4 11h3.2",artifact:"M8 2 14 5v6l-6 3-6-3V5ZM2 5l6 3 6-3M8 8v6",external:"M9 2.8h4.2V7M13.2 2.8 7.4 8.6M11.6 9.4v3.8H2.8V4.4h3.8",unknown:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM6.2 6.3a1.85 1.85 0 1 1 2.7 1.7c-.6.3-.9.8-.9 1.5M8 11.2v.9"},gp={step:"M8 5.6A2.4 2.4 0 1 0 8 10.4 2.4 2.4 0 0 0 8 5.6Z",group:"M2.6 2.6h4.4V7H2.6ZM9 2.6h4.4V7H9ZM2.6 9h4.4v4.4H2.6ZM9 9h4.4v4.4H9Z"},bt={operation:"function",data:"dataset",model:"model",state:"layer",objective:"loss",optimizer:"optimizer",evaluation:"eval_loop",metric:"metric",output:"external",config:"config",loop:"train_loop",branch:"split",group:"group",entrypoint:"entrypoint",artifact:"artifact",op:"function",step:"function",function:"function",method:"function",compute:"function",computation:"function",process:"function",construction:"function",initialization:"function",hook:"function",callback:"function",forward:"function",class:"class",component:"class",dataset:"dataset","data source":"dataset",datasource:"dataset",input:"dataset",inputs:"dataset",batch:"dataset",dataloader:"dataloader","data loader":"dataloader",loader:"dataloader",batching:"dataloader",sampler:"dataloader","data pipeline":"dataloader",split:"split","data split":"split",decision:"split",conditional:"split",condition:"split",selection:"split",control:"split","control flow":"split",preprocessing:"transform",preprocess:"transform",postprocessing:"transform",transform:"transform",transformation:"transform",augmentation:"augment",augment:"augment",network:"model",module:"model",architecture:"model",layer:"layer","state update":"layer",update:"layer","state init":"layer",variable:"layer",buffer:"layer",parameters:"layer",weights:"layer",loss:"loss",criterion:"loss",optimiser:"optimizer",optimization:"optimizer",optimisation:"optimizer","optimizer step":"optimizer","optimization step":"optimizer",scheduler:"scheduler",schedule:"scheduler","learning rate":"scheduler",scaler:"scaler","training loop":"train_loop","train loop":"train_loop","optimization loop":"train_loop",epoch:"train_loop","training run":"train_loop",eval:"eval_loop",validation:"eval_loop",test:"eval_loop","evaluation loop":"eval_loop",metrics:"metric",score:"metric",scoring:"metric",log:"tracker",logging:"tracker",logger:"tracker",tracker:"tracker",history:"tracker",checkpoint:"checkpoint",checkpointing:"checkpoint",persistence:"checkpoint",save:"checkpoint",outputs:"external",result:"external",return:"external",display:"external",external:"external",configuration:"config",settings:"config",hyperparameters:"config",arguments:"config",args:"config",environment:"config","entry point":"entrypoint",entry:"entrypoint",launcher:"entrypoint","api entry":"entrypoint",main:"entrypoint",script:"entrypoint",file:"artifact",inference:"predict",predict:"predict",prediction:"predict",sampling:"predict",generation:"predict",phase:"group",stage:"group"};function vp(t){return String(t||"").trim().toLowerCase().replace(/[\s_-]+/g," ")}function bp(t){let e=vp(t||"");if(!e||e==="unknown")return"step";if(bt[e])return bt[e];let n=e.split(/[^a-z0-9]+/).filter(Boolean),o=n[n.length-1];if(o&&bt[o])return bt[o];let i=n[0];return i&&bt[i]?bt[i]:"step"}function wt(t,e){return e?"group":bp(t.kind)}function Nn(t){return Ti[t]||gp[t]||Ti.unknown}function Ri(t){return Object.prototype.hasOwnProperty.call(Ti,t)}function Oi(t,e=16){let n=H("svg",{class:"mlv-icon",viewBox:"0 0 16 16",width:e,height:e,"aria-hidden":"true",focusable:"false"}),o=document.createElementNS(_e,"path");return Se(o,{d:Nn(t),fill:"none",stroke:"currentColor","stroke-width":"1.3","stroke-linecap":"round","stroke-linejoin":"round","vector-effect":"non-scaling-stroke"}),n.appendChild(o),n}var Pa={search:"M7 2.6a4.4 4.4 0 1 0 0 8.8 4.4 4.4 0 0 0 0-8.8ZM10.2 10.2 13.6 13.6",more:"M3.4 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2ZM8 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2ZM12.6 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2Z",close:"M3.6 3.6 12.4 12.4M12.4 3.6 3.6 12.4",chevron:"M6 3.6 10.4 8 6 12.4",plus:"M8 3.2v9.6M3.2 8h9.6",minus:"M3.2 8h9.6",fit:"M2.6 6V2.6H6M10 2.6h3.4V6M13.4 10v3.4H10M6 13.4H2.6V10",rail:"M2.4 3h11.2v10H2.4ZM9.6 3v10",open:"M9 2.8h4.2V7M13.2 2.8 7.4 8.6M11.6 9.4v3.8H2.8V4.4h3.8",target:"M8 2.6v2.2M8 11.2v2.2M2.6 8h2.2M11.2 8h2.2M8 5.2A2.8 2.8 0 1 0 8 10.8 2.8 2.8 0 0 0 8 5.2Z",warning:"M8 2.2 14.4 13.4H1.6ZM8 6.4v3.4M8 11.6v.1",info:"M8 2.2a5.8 5.8 0 1 0 0 11.6A5.8 5.8 0 0 0 8 2.2ZM8 7.2v4M8 5v.1",notobserved:"M2.6 5V2.6H5M7 2.6h2M11 2.6h2.4V5M13.4 7v2M13.4 11v2.4H11M9 13.4H7M5 13.4H2.6V11M2.6 9V7",flow:"M2.4 8h8.4M8.4 5.2 11.6 8l-3.2 2.8M13.2 6.4v3.2",legend:"M2.4 3.6h2.4v2.4H2.4ZM2.4 10h2.4v2.4H2.4ZM6.8 4.8h6.8M6.8 11.2h6.8",copy:"M5.8 5.8h7.6v7.6H5.8ZM2.6 10.2V2.6h7.6v3.2",phases:"M2.6 2.2h10.8v3H2.6ZM2.6 6.5h10.8v3H2.6ZM2.6 10.8h10.8v3H2.6ZM4.6 5.2v1.3M4.6 9.5v1.3",phaseindex:"M2.4 3.4h2v2h-2ZM2.4 7h2v2h-2ZM2.4 10.6h2v2h-2ZM6.4 4.4h7.2M6.4 8h7.2M6.4 11.6h7.2",image:"M2.4 3.4h11.2v9.2H2.4ZM2.4 10.6 5.8 7.4l2.4 2.2 2.2-2 3.2 3M10.3 5.3a1.05 1.05 0 1 0 0 2.1 1.05 1.05 0 0 0 0-2.1Z",review:"M2.4 3.2 4.8 5.4 2.4 7.6M7.2 5.4h6.4M7.2 9.4h6.4M2.6 13h11",keyboard:"M1.8 4.2h12.4v7.6H1.8ZM4.2 6.6h.1M6.7 6.6h.1M9.2 6.6h.1M11.7 6.6h.1M4.8 9.4h6.4"};function B(t,e=14){let n=H("svg",{class:"mlv-uicon",viewBox:"0 0 16 16",width:e,height:e,"aria-hidden":"true",focusable:"false"}),o=document.createElementNS(_e,"path");return Se(o,{d:Pa[t]||Pa.close,fill:"none",stroke:"currentColor","stroke-width":"1.4","stroke-linecap":"round","stroke-linejoin":"round"}),n.appendChild(o),n}var wp=240;function fe(t){return t==="inferred"?"inferred":t==="unresolved"?"? unresolved":""}function yp(t,e){return t==="inferred"?"Inferred, not observed: reasoned from the cited code and stated assumptions; the quotes do not show all of it.":"Unresolved: the evidence does not settle this claim. It does not mean the "+e+" is missing."}function yt(t,e="step"){let n=fe(t);if(!n||!t)return null;let o=f("span","mlv-basis-tag",n);return o.setAttribute("data-basis",t),o.setAttribute("aria-hidden","true"),o.title=yp(t,e),o}function Na(t){if(!t.stale)return"";let e=t.staleQuotes,n=t.staleElsewhere?"a file in another folder":"a changed or missing file";return e?e.stale+" of "+e.total+(e.total===1?" quote cites ":" quotes cite ")+n:"cites "+n}function Fa(t){return"mlv-n-"+t.replace(/[^A-Za-z0-9-]/g,e=>"_"+e.charCodeAt(0).toString(16)+"_")}function Hi(t){return t.stage||"unknown"}function Bn(t){return t.sublabel?t.sublabel:t.kind&&t.kind!=="unknown"&&Ri(t.kind)?t.kind.replace(/_/g," "):""}function xp(t){return t.kind==="unknown"||!Ri(t.kind)?"step":t.kind.replace(/_/g," ")}function jt(t){let e=t.node,n=[];n.push(xp(e)+" "+e.label),n.push(e.phaseLabel?e.phaseLabel+" phase":Hi(e)+" stage"),e.loc.file&&n.push(en(e.loc));let o=$e(e.basis);o&&n.push(o.slice(2));let i=Y(t.counts),s=oe(t.counts);i>0&&n.push(i+(i===1?" finding":" findings")+", highest severity "+s),t.descendants>0&&n.push(t.descendants+(t.descendants===1?" nested step":" nested steps")),t.stale&&n.push(Na(t));let r=e.detail?Dn(e.detail):"";return n.join(", ")+"."+(r?" "+r:"")}var Fn=160,Ep=new Set(["etc","vs","cf","approx","incl","esp","resp","fig","figs","eq","eqs","al"]);function kp(t,e){if(t[e]!==".")return!1;let n=/[A-Za-z.]*$/.exec(t.slice(0,e)),o=(n?n[0]:"").toLowerCase();return o?/^(?:[a-z]\.)+[a-z]$/.test(o)?!0:Ep.has(o):!1}function Dn(t){let e=t.replace(/\s+/g," ").trim();if(!e)return"";let n=/[.!?](?=\s|$)/g;for(let s=n.exec(e);s&&s.indexFn/2?o.slice(0,i):o).replace(/[\s,;:]+$/,"")+"…"}function Pi(t){return t.detail&&t.detail.trim()&&xi(t)?2:1}function Ba(t,e){let n=t.node,o=e,i=f("div","mlv-node");i.id=Fa(n.id),i.setAttribute("role","button"),i.setAttribute("tabindex","-1"),i.setAttribute("data-node-id",n.id),i.setAttribute("data-stage",Hi(n)),i.setAttribute("data-kind",n.kind),i.setAttribute("data-level",n.level),n.basis&&i.setAttribute("data-basis",n.basis),t.phase!==void 0&&le(i,t.phase);let s=oe(t.counts);s&&i.setAttribute("data-sev",s),i.setAttribute("aria-label",jt(t)),i.style.left=t.box.x+"px",i.style.top=t.box.y+"px",i.style.width=t.box.w+"px",i.style.height=t.box.h+"px",s&&i.classList.add("has-issues"),t.stale&&i.classList.add("is-stale"),o&&i.classList.add("is-collapsed-group"),m(i,f("div","mlv-node__rail"));let r=m(i,f("div","mlv-node__main"));m(r,f("div","mlv-node__iconbox")).appendChild(Oi(wt(n,o)));let l=m(r,f("div","mlv-node__text")),d=n.label||n.qualname||n.id;m(l,f("div","mlv-node__title mlv-node__title--wrap",d)).setAttribute("data-lines",String(yi(n,t.box.w)));let u=o?1:Pi(n),h=m(l,f("div","mlv-node__sub",Bn(n).slice(0,wp)));if(u===2?h.setAttribute("data-lines","2"):n.loc.file&&m(l,Qe("mlv-node__loc",n.loc,"div")),o){let g=m(l,f("div","mlv-node__chips"));m(g,f("span","mlv-chip",et(t.descendants)))}t.stale&&i.appendChild(ja(t));let p=yt(n.basis,o?"group":"step");if(p&&i.appendChild(p),o){let g=Ce(t.counts,14);g&&(g.classList.add("mlv-node__cluster"),g.style.position="absolute",g.style.top="-8px",g.style.right="-6px",g.style.padding="1px 6px",g.style.borderRadius="var(--mlv-r-pill)",g.style.background="var(--mlv-surface)",g.style.boxShadow="var(--mlv-sh-1)",i.appendChild(g))}else{let g=Gs(t.counts,18,t.findings||[]);g&&i.appendChild(g)}return i}function et(t){return t+(t===1?" step":" steps")}function Pe(t){return t+(t===1?" finding touches this phase":" findings touch this phase")}function Gt(t){let e=Y(t),n=oe(t);return e+(e===1?" finding touches":" findings touch")+" this phase, highest severity "+(n||"none")+". A finding that cites steps or connections in several phases counts in each of them."}function Da(t){let e=t.node,n=f("div","mlv-group");n.id=Fa(e.id),n.setAttribute("data-node-id",e.id),n.setAttribute("data-group","1"),n.setAttribute("data-stage",Hi(e)),t.phase!==void 0&&le(n,t.phase),e.basis&&n.setAttribute("data-basis",e.basis),n.setAttribute("data-depth",String(Math.min(2,t.box.depth)));let o=oe(t.counts);o&&n.setAttribute("data-sev",o),n.style.left=t.box.x+"px",n.style.top=t.box.y+"px",n.style.width=t.box.w+"px",n.style.height=t.box.h+"px";let i=f("div","mlv-group__header");i.setAttribute("role","button"),i.setAttribute("tabindex","-1"),i.setAttribute("aria-expanded","true"),i.setAttribute("aria-label",jt(t)+" Group, expanded. Double-click to collapse.");let s=f("button","mlv-group__chevron-btn");s.type="button",s.tabIndex=-1,s.setAttribute("aria-label","Collapse "+(e.label||e.qualname));let r=B("chevron",12);r.setAttribute("class","mlv-uicon mlv-group__chevron"),s.appendChild(r),i.appendChild(s),i.appendChild(Oi(wt(e,!1),14)),m(i,f("span","mlv-group__name",e.label||e.qualname));let a=yt(e.basis,"group");a&&i.appendChild(a),m(i,f("span","mlv-group__count",et(t.descendants)));let l=Ce(t.counts,13);return l&&i.appendChild(l),t.stale&&(n.classList.add("is-stale"),i.appendChild(ja(t))),n.appendChild(i),n}function ja(t){let e=f("span","mlv-node__stale");return e.setAttribute("data-stale","1"),e.setAttribute("aria-hidden","true"),e.title=Na(t)+(t.staleElsewhere?". Its jumps are blocked; the notice above says which folder to add.":" since publishing. Its jumps are blocked."),e.appendChild(B("warning",12)),e}function Ga(t,e,n,o){let i=f("div","mlv-lane");i.setAttribute("data-lane-id",t.id),i.setAttribute("data-stage",t.id),o!==void 0&&le(i,o),i.style.left=t.x+"px",i.style.top=t.y+"px",i.style.width=t.w+"px",i.style.height=t.h+"px",i.style.setProperty("--mlv-lane-header-h",t.headerH+"px"),n&&i.classList.add("is-absent");let s=m(i,f("div","mlv-lane__header"));if(o!==void 0){let d=m(s,f("span","mlv-lane__num",String(o+1)));d.title="Phase "+(o+1)}let r=m(s,f("span","mlv-lane__label",t.label));r.title=o!==void 0?"Phase "+(o+1)+": "+t.label:t.label,m(s,f("span","mlv-lane__count",et(t.nodeCount))),m(s,f("span","mlv-lane__spacer"));let a=Gt(e),l=Ce(e,13,a);if(l){s.appendChild(l);let d=m(s,f("span","mlv-lane__unit",Pe(Y(e))));d.setAttribute("aria-hidden","true"),d.title=a}return i}function za(t){let e=new Map,n=[];for(let i of t){if(!_p(i))continue;let s=i.trunk.key,r=e.get(s);r?r.push(i):(e.set(s,[i]),n.push(s))}let o=[];for(let i of n){let s=e.get(i);if(s.length<2)continue;s.sort((a,l)=>a.trunk.index-l.trunk.index||Ip(a.id,l.id));let r=s[0].trunk.axis==="y"?Sp(i,s):Mp(i,s);for(let a of r)o.push(a)}return o}function _p(t){let e=t.trunk;return!e||e.joinFrom<1||e.joinTo<=e.joinFrom?!1:e.joinTo+1<=t.points.length-1}function Sp(t,e){let n=e[0].trunk,o=n.channelX,i=1/0,s=1/0;for(let d of e)i=Math.min(i,d.points[d.trunk.joinFrom-1].x),s=Math.min(s,d.points[d.trunk.joinTo+1].x);if(Math.abs(n.exitY-n.entryY){let l=r.points[r.trunk.joinFrom].x,d=r.points[r.trunk.joinTo].x;return{i:a,lo:Math.min(l,d),hi:Math.max(l,d)}}),i=[],s=0;for(let r of Lp(o)){if(r.length<2)continue;let a=1/0,l=-1/0;for(let h of r)a=Math.min(a,h.lo),l=Math.max(l,h.hi);if(!(l-a>=Uo))continue;let d=r.slice().sort((h,p)=>h.i-p.i).map(h=>e[h.i]),c=[de(a,n.entryY),de(l,n.entryY)],u=[];for(let h of d){let p=h.trunk.joinFrom,g=Wt(h.points.slice(0,p+1).concat([de(h.points[p].x,n.entryY)]));u.push({id:h.id,side:"in",points:g,d:Ie(g,ge)})}for(let h of d){let p=h.trunk.joinTo,g=Wt([de(h.points[p].x,n.entryY)].concat(h.points.slice(p)));u.push({id:h.id,side:"out",points:g,d:Ie(g,ge)})}i.push(Va(t,s++,d,n,c,u,de((a+l)/2,n.entryY)))}return i}function Lp(t){let e=t.slice().sort((s,r)=>s.lo-r.lo||s.hi-r.hi||s.i-r.i),n=[],o=[],i=-1/0;for(let s of e)o.length&&s.lo>i&&(n.push(o),o=[]),o.push(s),i=o.length===1?s.hi:Math.max(i,s.hi);return o.length&&n.push(o),n}function Va(t,e,n,o,i,s,r){let a=[],l=0;for(let d of n)l+=d.ids.length,a.indexOf(d.kind)<0&&a.push(d.kind);return{id:"bundle:"+t+(e?"#"+e:""),sourceLane:o.sourceLane,targetLane:o.targetLane,part:e,memberIds:n.map(d=>d.id),count:n.length,edgeCount:l,axis:o.axis,trunk:i,d:Ie(i,ge),spurs:s.filter(d=>d.points.length>1),badge:r,kinds:a}}function Wt(t){let e=[];for(let n of t){let o=e[e.length-1];o&&Math.abs(o.x-n.x)<.01&&Math.abs(o.y-n.y)<.01||e.push(n)}return e}function Ip(t,e){return te?1:0}function de(t,e){return{x:Wa(t),y:Wa(e)}}function Wa(t){return Math.round(t*100)/100}function jn(t){let{index:e,frame:n,routes:o,keep:i}=t,s=[];for(let h of n.lanes)s.push({lane:h,counts:e.laneCounts(h.id,i),phase:e.phaseIndexOf(h.id)});let r=[],a=Array.from(n.boxes.values()).sort((h,p)=>h.depth-p.depth||Cp(h.id,p.id));for(let h of a){let p=e.nodeById.get(h.id);if(!p)continue;let g=h.isGroup&&!h.collapsed,b=g||h.collapsed?e.subtreeIssues(h.id,i):e.issuesOf(h.id,i);r.push({expandedGroup:g,visual:{node:p,box:h,counts:e.countsFor(b),findings:b.map(x=>x.short).filter(Boolean),phase:e.phaseIndexOf(p.stage),descendants:e.descendantCount(h.id),...Ka(p.evidenceLocs,p.loc,t.staleFiles)}})}let l=[];for(let h of o){let p=e.issuesOfEdges(h.ids,i),g=e.nodeById.get(h.source),v=e.nodeById.get(h.target);l.push({route:h,severity:oe(e.countsFor(p)),findings:p.map(b=>b.short).filter(Boolean),phase:g?e.phaseIndexOf(g.stage):void 0,labelVisible:h.back,stage:g?g.stage:void 0,sourceLabel:g?g.label||g.qualname:void 0,targetLabel:v?v.label||v.qualname:void 0,mountSerial:t.mountSerial,placement:t.labels?t.labels.get(h.id):void 0,weight:Qs(h.ids),...Tp(h.ids,e,t.staleFiles),basis:Ap(h.ids,e)})}let d=new Map;for(let h of l)d.set(h.route.id,h.severity);let c=new Map;for(let h of n.lanes)c.set(h.id,h.label);let u=[];for(let h of za(o)){let p=null;for(let g of h.memberIds){let v=d.get(g)||null;v&&(!p||ne.indexOf(v)e?1:0}function Ka(t,e,n){if(!n.size)return{stale:!1};let o=t||(e.file?[e]:[]),i=[];for(let r of o){let a=r.file?n.get(r.file):void 0;a&&i.push(a)}if(!i.length)return{stale:!1};let s={stale:!0,staleQuotes:{stale:i.length,total:o.length}};return ae(i)&&(s.staleElsewhere=!0),s}function Tp(t,e,n){if(!n.size)return{};let o=!1,i=!0;for(let s of t){let r=e.edgeById.get(s),a=r?Ka(r.evidenceLocs,r.loc,n):null;!a||!a.stale||(o=!0,a.staleElsewhere||(i=!1))}return o?i?{stale:!0,staleElsewhere:!0}:{stale:!0}:{}}function Ua(t,e){let n=jn(e),o=n.frame;D(t.lanes),D(t.nodes),D(t.bundleGroup),D(t.edgeGroup),D(t.connectors),t.world.style.width=o.width+"px",t.world.style.height=o.height+"px",t.edgesSvg.setAttribute("width",String(o.width)),t.edgesSvg.setAttribute("height",String(o.height)),t.edgesSvg.setAttribute("viewBox","0 0 "+o.width+" "+o.height);for(let a of n.lanes)t.lanes.appendChild(Ga(a.lane,a.counts,!1,a.phase));let i=new Map;for(let a of n.nodes){let l=a.visual,d=a.expandedGroup?Da(l):Ba(l,l.box.collapsed);e.wireNode(d,l.node.id,a.expandedGroup),i.set(l.node.id,d),a.expandedGroup?t.lanes.appendChild(d):t.nodes.appendChild(d)}let s=new Map;for(let a of n.bundles){let l=Ha(a);t.bundleGroup.appendChild(l),s.set(a.bundle.id,l)}let r=new Map;for(let a of n.edges){let l=gr(a);e.wireEdge(l,a.route),t.edgeGroup.appendChild(l),r.set(a.route.id,l)}return{nodeEls:i,edgeEls:r,bundleEls:s,plan:n}}var Xa="(prefers-reduced-motion: reduce)",Rp="vscode-reduce-motion",Ya="vscode-using-screen-reader";function Gn(){return Hp(Xa)||Op()?"reduced":"full"}function Za(){return Ni(Ya)}function Op(){return Ni(Rp)||Ni(Ya)}function Ni(t){try{let e=typeof document<"u"?document.body:null;return!!e&&e.classList.contains(t)}catch{return!1}}function Hp(t){try{if(typeof window>"u"||!window.matchMedia)return!1;let e=window.matchMedia(t);return!!(e&&e.matches)}catch{return!1}}var Wn=class{constructor(e){this.stop=null;this.current=Gn();let n=()=>{let i=Gn();i!==this.current&&(this.current=i,e(i))},o=[];try{let i=typeof window<"u"&&window.matchMedia?window.matchMedia(Xa):null;i&&typeof i.addEventListener=="function"&&(i.addEventListener("change",n),o.push(()=>i.removeEventListener("change",n)))}catch{}try{let i=typeof document<"u"?document.body:null;if(i&&typeof MutationObserver=="function"){let s=new MutationObserver(n);s.observe(i,{attributes:!0,attributeFilter:["class"]}),o.push(()=>s.disconnect())}}catch{}o.length&&(this.stop=()=>{for(let i of o)try{i()}catch{}})}get mode(){return this.current}destroy(){this.stop&&this.stop(),this.stop=null}};var Ne=.15,tt=2.5,Pp=1.2;function el(t,e,n,o,i=24){let s=(n-i*2)/Math.max(1,t),r=(o-i*2)/Math.max(1,e);return{zoom:Fe(Math.min(s,r),Ne,Pp)}}var Be=.62,Qa=[.15,.2,.25,.32,.4,.5,.62,.8,1,1.25,1.6,2,2.5];function Np(t){if(!(t>0))return 1;let e=Qa[0],n=1/0;for(let o of Qa){let i=Math.abs(Math.log(t/o));i=Be||!a)return{zoom:r,x:(e-i*r)/2,y:Math.max(o,(n-s*r)/2),mode:"whole"};let l=a.x-Math.max(0,t.channelW||0),d=Math.max(1,a.x+a.w-l),c=Math.max(1,a.h),u=Math.min((e-o*2)/d,(n-o*2)/c),h=u>=tl,p=Fe(h?Math.min(u,De):De,Ne,tt),g=i*p<=e?(e-i*p)/2:o-l*p,v=s*p<=n?(n-s*p)/2:o-a.y*p;return{zoom:p,x:g,y:v,mode:h?"phase-fit":"phase-anchor"}}var Bp=240;function nl(t,e,n,o,i=24){let s=t.lanes[e];if(!s)return null;let r=Math.max(1,t.width),a=Math.max(1,t.height),l=s.x-Math.max(0,t.channelW||0),d=Math.max(1,s.x+s.w-l),c=Math.max(1,s.h),u=Math.min((n-i*2)/d,(o-i*2)/c),h=u>=tl,p=Fe(h?Math.min(u,De):De,Ne,tt),g=r*p<=n?(n-r*p)/2:i-l*p,v;if(a*p<=o)v=(o-a*p)/2;else{v=i-s.y*p;let b=o-i-a*p;c*p+2*i<=o&&v=0&&s.push({dx:r,dy:0});let a=e.y-i-(t.y+t.h);t.y+a>=0&&s.push({dx:0,dy:a});let l=e.x+e.w+i-t.x;t.x+t.w+l<=n&&s.push({dx:l,dy:0});let d=e.y+e.h+i-t.y;return t.y+t.h+d<=o&&s.push({dx:0,dy:d}),s.length?(s.sort((c,u)=>Math.abs(c.dx)+Math.abs(c.dy)-(Math.abs(u.dx)+Math.abs(u.dy))),s[0]):null}var $a=80,Ja=.1,zt=.45,Dp=1,zn=class{constructor(e,n,o){this.vp={x:0,y:0,zoom:1};this.contentW=1;this.contentH=1;this.lod="full";this.zBucket=0;this.fitted=!1;this.fitSize=null;this.frame=null;this.coveredFn=null;this.animToken=0;this.animLive=0;this.canvas=e,this.world=n,this.onChange=o}setContent(e,n,o=null){this.contentW=Math.max(1,e),this.contentH=Math.max(1,n),this.frame=o}apply(){let{x:e,y:n,zoom:o}=this.vp;this.world.style.transform="translate("+e+"px,"+n+"px) scale("+o+")";let i=o=$a&&o>=this.fitSize.w*Ja||i>=$a&&i>=this.fitSize.h*Ja)return this.applyFit(this.fitSize.padding,this.fitSize.whole),!0}return this.apply(),!1}visibleArea(){return this.size()}acceptResize(){if(!this.fitted||!this.fitSize)return;let{w:e,h:n}=this.size();this.fitSize={...this.fitSize,w:e,h:n}}fitsAt(e,n,o=48){let{w:i,h:s}=this.visibleArea();return Math.min((i-o)/Math.max(1,e.w),(s-o)/Math.max(1,e.h))>=n}frameRect(e,n,o,i=48){let{w:s,h:r}=this.visibleArea(),a=Math.min((s-i)/Math.max(1,e.w),(r-i)/Math.max(1,e.h)),l=this.vp.zoom;if(l>=Be&&a>=l){this.centerOn(e),this.keepClear(n);return}let d=l>=Be?Math.max(Dp,l):o,c=Math.max(zt,Math.min(d,a));this.centerOn(a>=zt?e:n,c),this.keepClear(n)}keepClear(e){let n=this.covered();if(!n)return;let{w:o,h:i}=this.visibleArea(),s=this.vp.zoom,r=Fi({x:e.x*s+this.vp.x,y:e.y*s+this.vp.y,w:e.w*s,h:e.h*s},n,o,i);r&&this.panBy(r.dx,r.dy)}centerOn(e,n){this.animToken++,this.fitted=!1;let{w:o,h:i}=this.visibleArea();typeof n=="number"&&(this.vp.zoom=Fe(n,Ne,tt));let s=this.vp.zoom;this.vp.x=o/2-(e.x+e.w/2)*s,this.vp.y=i/2-(e.y+e.h/2)*s;let r=this.covered();if(r){let a=Fi({x:e.x*s+this.vp.x,y:e.y*s+this.vp.y,w:e.w*s,h:e.h*s},r,o,i);a&&(this.vp.x+=a.dx,this.vp.y+=a.dy)}this.apply()}revealRect(e,n=16){let{w:o,h:i}=this.visibleArea(),s=this.vp.zoom,r=(c,u,h)=>{let p=Math.max(0,Math.min(n,(h-u)/2));return u>h?-c:c<0?p-c:c+u>h?h-p-(c+u):0},a=r(e.x*s+this.vp.x,e.w*s,o),l=r(e.y*s+this.vp.y,e.h*s,i),d=this.covered();if(d){let c=Fi({x:e.x*s+this.vp.x+a,y:e.y*s+this.vp.y+l,w:e.w*s,h:e.h*s},d,o,i,Math.min(n,8));c&&(a+=c.dx,l+=c.dy)}(a||l)&&this.panBy(a,l)}zoomToBox(e,n=80){let{w:o,h:i}=this.visibleArea(),s=Fe(Math.min((o-n)/Math.max(e.w,1),(i-n)/Math.max(e.h,1)),Ne,tt);this.centerOn(e,s)}isVisible(e,n=this.visibleArea()){let{w:o,h:i}=n,s=e.x*this.vp.zoom+this.vp.x,r=e.y*this.vp.zoom+this.vp.y,a=e.w*this.vp.zoom,l=e.h*this.vp.zoom;if(!(s>=0&&r>=0&&s+a<=o&&r+l<=i))return!1;let d=this.covered();return!d||!ol({x:s,y:r,w:a,h:l},d)}setCovered(e){this.coveredFn=e}covered(){if(!this.coveredFn)return null;try{let e=this.coveredFn();return e&&e.w>0&&e.h>0?e:null}catch{return null}}animateTo(e,n=Bp,o){let i=++this.animToken;this.fitted=!1;let s={x:e.x,y:e.y,zoom:Fe(e.zoom,Ne,tt)},r=typeof window<"u"&&typeof window.requestAnimationFrame=="function"?window.requestAnimationFrame.bind(window):null,a=()=>{this.animLive=0,this.vp={...s},this.apply(),o&&o()};if(!r||!(n>0)||Gn()==="reduced"){a();return}let l={...this.vp},{w:d,h:c}=this.size(),u={x:(d/2-l.x)/l.zoom,y:(c/2-l.y)/l.zoom},h={x:(d/2-s.x)/s.zoom,y:(c/2-s.y)/s.zoom},p=Date.now();this.animLive=i;let g=()=>{if(i!==this.animToken)return;let v=Math.min(1,(Date.now()-p)/n);if(v>=1){a();return}let b=1-Math.pow(1-v,3),x=l.zoom*Math.pow(s.zoom/l.zoom,b);this.vp={x:d/2-(u.x+(h.x-u.x)*b)*x,y:c/2-(u.y+(h.y-u.y)*b)*x,zoom:x},this.apply(),r(g)};r(g)}get animating(){return this.animLive!==0&&this.animLive===this.animToken}};function Fe(t,e,n){return tn?n:t}var il=360,jp=26,Gp=24,Wp=4,Vn=28,zp=360,Vp=12,sl=4;function qp(t,e,n,o){let i=[],s=null,r=0,a=Math.max(1,n*o);for(let l of t){let d=Math.max(0,l.rect.x*e.zoom+e.x),c=Math.max(0,l.rect.y*e.zoom+e.y),u=Math.min(n,(l.rect.x+l.rect.w)*e.zoom+e.x),h=Math.min(o,(l.rect.y+l.rect.h)*e.zoom+e.y),p=Math.max(0,u-d)*Math.max(0,h-c);if(p<=0)continue;i.push(l.id);let g=Math.max(1,l.rect.w*l.rect.h*e.zoom*e.zoom),v=p/a+p/g;v>r+1e-9&&(r=v,s=l.id)}return{ids:i,current:s}}function Bi(t){let e=Y(t);return e?e+(e===1?" finding touches this phase":" findings touch this phase"):""}var Di=t=>t+(t===1?" step":" steps");function rl(t){let e=t.length;for(let n of t)e=Math.max(e,n.index+1);return e}var qn=class{constructor(e){this.rowsData=[];this.rowEls=new Map;this.mode={roomy:!0,folded:!1,hidden:!1,suppressed:!1};this.popover=!1;this.inViewKey="";this.currentId=null;this.disposers=[];this.cb=e,this.root=f("nav","mlv-phaseindex"),this.root.setAttribute("aria-label","Phase index"),this.root.hidden=!0,this.panel=m(this.root,f("div","mlv-phaseindex__panel"));let n=m(this.panel,f("div","mlv-phaseindex__head"));m(n,f("span","mlv-phaseindex__title","Phases")),this.headSteps=m(n,f("span","mlv-phaseindex__total")),this.foldBtn=m(n,f("button","mlv-btn mlv-btn--icon mlv-phaseindex__fold")),this.foldBtn.type="button",this.foldBtn.tabIndex=-1,this.foldBtn.setAttribute("aria-label","Fold the phase index"),this.foldBtn.title="Fold the phase index to one line",this.foldBtn.appendChild(B("chevron",12)),_(this.foldBtn,"click",s=>{s.preventDefault(),s.stopPropagation(),this.cb.fold(!0)}),this.rowsEl=m(this.panel,f("ol","mlv-phaseindex__rows")),this.pill=m(this.root,f("button","mlv-phaseindex__pill")),this.pill.type="button",this.pill.tabIndex=-1,this.pill.setAttribute("aria-expanded","false"),this.pillNum=m(this.pill,f("span","mlv-phaseindex__pillnum")),this.pillLabel=m(this.pill,f("span","mlv-phaseindex__pilllabel"));let o=B("chevron",12);o.setAttribute("class","mlv-uicon mlv-phaseindex__pillchev"),this.pill.appendChild(o),_(this.pill,"click",s=>{s.preventDefault(),s.stopPropagation(),this.mode.roomy&&this.mode.folded?this.cb.fold(!1):this.setPopover(!this.popover)}),this.disposers.push(_(this.root,"keydown",s=>{(s.key==="Enter"||s.key===" ")&&s.stopPropagation()})),this.disposers.push(_(this.panel,"wheel",s=>s.stopPropagation()));let i=typeof document>"u"?null:document;i&&this.disposers.push(_(i,"pointerdown",s=>{if(!this.popover)return;let r=s.target;r&&this.root.contains(r)||this.setPopover(!1)}))}setRows(e){this.rowsData=e,this.rowEls.clear(),D(this.rowsEl);let n=e.reduce((i,s)=>i+s.steps,0);this.headSteps.textContent=Di(n);let o=rl(e);for(let i of e){let s=m(this.rowsEl,f("li","mlv-phaseindex__item")),r=m(s,f("button","mlv-phaseindex__row"));r.type="button",r.tabIndex=-1,r.setAttribute("data-phase-id",i.id),le(r,i.index),m(r,f("span","mlv-phaseindex__num",String(i.index+1))).setAttribute("aria-hidden","true");let a=m(r,f("span","mlv-phaseindex__label",i.label));a.title="Phase "+(i.index+1)+": "+i.label;let l=Ce(i.counts,11,Bi(i.counts));l&&(l.classList.add("mlv-phaseindex__sev"),l.setAttribute("aria-hidden","true"),l.title=Bi(i.counts)+" ("+nn(i.counts)+")",r.appendChild(l)),m(r,f("span","mlv-phaseindex__steps",Di(i.steps))).setAttribute("aria-hidden","true"),r.setAttribute("data-name","Phase "+(i.index+1)+" of "+o+": "+i.label+", "+Di(i.steps)+(Y(i.counts)?", "+Bi(i.counts)+" ("+nn(i.counts).replace(/^\d+ findings?: /,"")+")":"")),r.title="Go to this phase",_(r,"click",d=>{d.preventDefault(),d.stopPropagation(),this.setPopover(!1),this.cb.go(i.id)}),this.rowEls.set(i.id,r)}this.inViewKey="\0",this.applyMode()}get rows(){return this.rowsData}setMode(e){this.mode={...this.mode,...e},this.applyMode()}get available(){return this.rowsData.length>=2}get form(){return this.root.hidden?"none":this.root.getAttribute("data-form")==="list"?"list":"pill"}get popoverOpen(){return this.popover&&this.form==="pill"}closePopover(){return this.popoverOpen?(this.setPopover(!1),!0):!1}setPopover(e){this.popover=e,this.applyMode()}applyMode(){let e=this.mode,n=e.hidden||e.suppressed||!this.available;this.root.hidden=n;let o=e.roomy&&!e.folded;(o||n)&&(this.popover=!1),this.root.setAttribute("data-form",o?"list":"pill"),this.popover?this.root.setAttribute("data-open","true"):this.root.removeAttribute("data-open"),this.panel.hidden=!o&&!this.popover,this.pill.hidden=o,this.pill.setAttribute("aria-expanded",this.popover?"true":"false");let i=e.roomy&&e.folded;this.pill.title=i?"Unfold the phase index":this.popover?"Close the phase list":"Show every phase",this.syncPill()}setInView(e,n,o){let i=qp(this.rowsData,e,n,o),s=i.ids.join("\0")+""+(i.current||"");if(s===this.inViewKey)return;this.inViewKey=s,this.currentId=i.current;let r=new Set(i.ids);for(let[a,l]of this.rowEls){let d=r.has(a);d?l.setAttribute("data-in-view","true"):l.removeAttribute("data-in-view"),l.setAttribute("aria-label",(l.getAttribute("data-name")||"")+(d?", in view":""))}this.syncPill()}currentPhase(){return this.currentId}inView(){let e=[];for(let[n,o]of this.rowEls)o.getAttribute("data-in-view")==="true"&&e.push(n);return e}syncPill(){let e=this.rowsData,n=rl(e),o=e.find(i=>i.id===this.currentId)||null;this.pillNum.textContent=o?o.index+1+"/"+n:String(e.length),this.pillLabel.textContent=o?o.label:e.length===1?"phase":"phases",this.pill.setAttribute("aria-label",(o?"Phase "+(o.index+1)+" of "+n+" in view: "+o.label:e.length+" phases")+". "+(this.mode.roomy&&this.mode.folded?"Unfold the phase index.":this.popover?"Close the phase list.":"Show every phase."))}coveredRect(e,n,o=0,i=0){if(this.root.hidden||!(e>0)||!(n>0))return null;let s=[];for(let l of[this.panel,this.pill]){if(l.hidden)continue;let d=l.getBoundingClientRect();d.width>0&&d.height>0&&s.push({x:d.left-o,y:d.top-i,w:d.width,h:d.height})}if(!s.length){let l=Vp,d=jp+this.rowsData.length*Gp+2*Wp;if(this.root.getAttribute("data-form")==="list"){let c=Math.min(il,e-2*l),u=Math.min(d,n-2*l);s.push({x:e-l-c,y:n-l-u,w:c,h:u})}else{let c=Math.min(zp,e-2*l);if(s.push({x:e-l-c,y:n-l-Vn,w:c,h:Vn}),this.popover){let u=Math.min(il,e-2*l),h=Math.min(d,Math.max(0,n-2*l-Vn-sl));s.push({x:e-l-u,y:n-l-Vn-sl-h,w:u,h})}}}let r=Math.min(...s.map(l=>l.x)),a=Math.min(...s.map(l=>l.y));return{x:r,y:a,w:Math.max(...s.map(l=>l.x+l.w))-r,h:Math.max(...s.map(l=>l.y+l.h))-a}}destroy(){for(let e of this.disposers)try{e()}catch{}this.disposers=[]}};function Un(t,e){let n=[],o=new Map;for(let u of t.lanes)o.set(u.id,n.length),n.push({id:u.id,label:u.label||u.id,index:t.phaseIndexOf(u.id),steps:[],findings:t.laneCounts(u.id,e),inferred:0,unresolved:0});let i=new Set,s=0,r=(u,h)=>{if(i.has(u))return;let p=t.nodeById.get(u);if(!p)return;i.add(u);let g=o.get(p.stage);if(g===void 0)s++;else{let v=n[g],b=p.basis||"";b==="inferred"?v.inferred++:b==="unresolved"&&v.unresolved++,v.steps.push({id:u,title:p.label||p.qualname||u,basis:b,depth:h,tags:t.issuesOf(u,e).map(x=>({short:x.short||x.id,id:x.id,severity:ee(x.severity)}))})}for(let v of t.laneChildren(u))r(v,h+1)};for(let u of t.lanes)for(let h of t.roots(u.id))r(h,0);for(let u of t.graph.nodes||[])r(u.id,0);let a=new Map,l=0,d=0;for(let u of t.graph.edges||[]){let h=t.nodeById.get(u.source),p=t.nodeById.get(u.target),g=h?o.get(h.stage):void 0,v=p?o.get(p.stage):void 0;if(g===void 0||v===void 0)d++;else if(g===v)l++;else{let b=g+">"+v,x=a.get(b);x?x.count++:a.set(b,{from:g,to:v,count:1})}}let c=Array.from(a.values()).sort((u,h)=>u.from-h.from||u.to-h.to);return{phases:n,links:c,inside:l,unplacedConnections:d,unplacedSteps:s,totalSteps:(t.graph.nodes||[]).length,totalConnections:(t.graph.edges||[]).length}}var je=(t,e,n)=>t+" "+(t===1?e:n);function Vt(t){return t.to===t.from+1}function Kp(t){let e=0,n=0;for(let o of t.links)Vt(o)?e+=o.count:n+=o.count;return{inside:t.inside,next:e,jumps:n,unplaced:t.unplacedConnections}}function Up(t){let e=Kp(t),n=[je(e.inside,"inside a phase","inside a phase"),je(e.next,"to the next phase","to the next phase"),je(e.jumps,"skips ahead or goes back","skip ahead or go back")];return e.unplaced&&n.push(je(e.unplaced,"with an end the document lacks","with an end the document lacks")),je(t.phases.length,"phase","phases")+" · "+je(t.totalSteps,"step","steps")+" · "+je(t.totalConnections,"connection","connections")+": "+n.join(", ")+"."}var Xp="Arrows go to the next phase; brackets count connections that skip ahead (solid) or back (dashed). ◌ inferred, ? unresolved.",Yp="◌ inferred, ? unresolved. Brackets count connections that skip ahead (solid) or go back (dashed).";function zi(t){let e=[];return t.inferred&&e.push(t.inferred+" inferred"),t.unresolved&&e.push(t.unresolved+" unresolved"),je(t.steps.length,"step","steps")+(e.length?" ("+e.join(", ")+")":"")}var Zp=16,Qp=12,$p=620,Jp=600,em=980,tm=32,nm=50,al=18,om=8,ll=12,dl=16,cl=12,ji=26,Kn=12,im=12,sm=7,Gi=14,rm=3,am=24,lm=17,dm=8,ul=12,cm=6.8;function pl(t,e,n){let o=Math.max(1,Math.floor(e)),i=Math.max(1,Math.floor(n)),s=o<$p,r=s?Qp:Zp,a=t.phases.length,l=t.links.filter(O=>!Vt(O)),d=l.map((O,W)=>({link:O,i:W})).sort((O,W)=>hl(O.link)-hl(W.link)||Math.min(O.link.from,O.link.to)-Math.min(W.link.from,W.link.to)||O.i-W.i),c=new Map,u=[];for(let{link:O}of d){let W=Math.min(O.from,O.to),J=Math.max(O.from,O.to),te=0;for(;teW<=be&&ce<=J);)te++;te===u.length&&u.push([]),u[te].push([W,J]),c.set(O,te)}let h=u.length,p=Math.max(60,Math.min(160,Math.round(o*.2))),g=im;h&&Kn+h*g+Gi>p&&(g=Math.max(sm,(p-Kn-Gi)/h));let v=h?Math.ceil(Kn+(h-1)*g+Gi):0,b=Math.max(120,Math.min(em,o-2*r-v)),x=!s&&b>=Jp,y=x?2:1,k=!x,E=k?nm:tm,w=Up(t),S=s?Yp:Xp,C=Math.max(80,o-2*r),L=O=>Math.max(1,Math.ceil(O.length*cm/C)),I=L(w)+(S?L(S):0),F={x:r,y:r,w:o-2*r,h:am+I*lm+dm,lines:I,summary:w,key:S},A=t.phases.map(O=>Math.ceil(O.steps.length/y)),P=A.slice(),N=t.phases.map(O=>Math.ceil(Math.min(O.steps.length,rm)/y)),z=O=>E+(O>0?O*al:0)+om,j=()=>F.y+F.h+ul+P.reduce((O,W)=>O+z(W),0)+ji*Math.max(0,a-1)+r;for(;j()>i;){let O=-1;for(let W=0;WP[O]||P[W]===P[O]&&t.phases[W].steps.length>=t.phases[O].steps.length)&&(O=W);if(O<0)break;P[O]--}let V=[],$=F.y+F.h+ul,Xe=(b-2*ll-(y-1)*dl)/y;t.phases.forEach((O,W)=>{let J=P[W],te=J*y,ce=O.steps.length,Ee=te>=ce?ce:Math.max(0,te-1),Oo=ce-Ee,Rs=[];for(let Ye=0;Yebe.other-Ee.other||(c.get(be.link)||0)-(c.get(Ee.link)||0));let te=J.y+10,ce=Math.max(1,J.h-20);W.forEach((be,Ee)=>Ro.set(Wi(be.link,O),pe(te+(Ee+1)*ce/(W.length+1))))}let bc=l.map(O=>{let W=c.get(O)||0,J=pe(Co+Kn+W*g),te=Ro.get(Wi(O,O.from))||0,ce=Ro.get(Wi(O,O.to))||0;return{from:O.from,to:O.to,count:O.count,back:O.toi,narrow:s,header:F,blocks:V,arrows:lt,brackets:bc,trimmed:V.some(O=>O.more>0)}}function hl(t){return Math.abs(t.to-t.from)}function Wi(t,e){return t.from+">"+t.to+"@"+e}function fl(t,e,n){let o=t.get(e);o?o.push(n):t.set(e,[n])}function pe(t){return Math.round(t*10)/10}var um=0,qt=(t,e,n)=>t+" "+(t===1?e:n),Xn=class{constructor(e){this.input=null;this.layoutData=null;this.blockEls=[];this.active=0;this.covered=0;this.disposers=[];this.cb=e,this.uid="mlv-ov"+ ++um,this.root=f("section","mlv-overview"),this.root.setAttribute("aria-label","Phase overview"),this.root.hidden=!0,this.content=m(this.root,f("div","mlv-overview__content")),this.disposers.push(_(this.root,"keydown",n=>this.onKey(n))),this.disposers.push(_(this.root,"wheel",n=>n.stopPropagation()))}get open(){return!this.root.hidden}get layout(){return this.layoutData}get activeIndex(){return this.active}show(e,n,o,i){this.root.hidden=!1,this.active=Math.max(0,Math.min(e.phases.length-1,i)),this.draw(e,n,o),this.focusBlock(this.active)}hide(){this.root.hidden=!0}redraw(e,n,o){if(!this.open)return;let i=this.root.ownerDocument,s=!!i&&!!i.activeElement&&this.root.contains(i.activeElement);this.active=Math.max(0,Math.min(e.phases.length-1,this.active)),this.draw(e,n,o),s&&this.focusBlock(this.active)}focusBlock(e){let n=this.blockEls[e];if(!n){try{this.root.focus()}catch{}return}this.active=e;for(let[o,i]of this.blockEls.entries())i.tabIndex=o===e?0:-1;try{n.focus({preventScroll:!0})}catch{}mm(this.root,n,this.covered,e===0)}draw(e,n,o){this.input=e;let i=pl(e,n,o);this.layoutData=i,D(this.content),this.blockEls=[],this.root.setAttribute("data-layout",i.narrow?"narrow":"wide"),this.root.setAttribute("data-scrolls",i.scrolls?"true":"false"),this.content.style.width=i.width+"px",this.content.style.height=i.height+"px";let s=m(this.content,f("div","mlv-overview__header")),r=i.header.y+i.header.h,a=i.scrolls&&r*2<=i.viewH;this.covered=a?r:0,s.setAttribute("data-sticky",a?"true":"false"),s.style.width=i.width+"px",s.style.height=r+"px",s.style.paddingTop=i.header.y+"px",s.style.paddingLeft=i.header.x+"px",s.style.paddingRight=Math.max(0,i.width-i.header.x-i.header.w)+"px";let l=m(s,f("div","mlv-overview__top")),d=m(l,f("h3","mlv-overview__title","Phase overview"));d.id=this.uid+"-title",this.root.setAttribute("aria-labelledby",d.id);let c=m(l,f("button","mlv-btn mlv-overview__back"));c.type="button",c.tabIndex=-1,c.setAttribute("aria-label","Back to the diagram (Escape)"),c.title="Back to the diagram where you were (Escape or Shift+0)",c.appendChild(B("close",12)),m(c,f("span","mlv-overview__backlabel","Back")),m(c,f("kbd","mlv-overview__kbd","Esc")).setAttribute("aria-hidden","true"),_(c,"click",b=>{b.preventDefault(),b.stopPropagation(),this.cb.back()});let u=m(s,f("p","mlv-overview__summary",i.header.summary));if(u.id=this.uid+"-summary",this.root.setAttribute("aria-describedby",u.id),i.header.key){let b=m(s,f("p","mlv-overview__key"));i.header.key.split("◌").forEach((x,y)=>{y>0&&b.appendChild(ml()),b.appendChild(fm(s).createTextNode(x))})}let h=H("svg",{class:"mlv-overview__links",width:i.width,height:i.height,viewBox:"0 0 "+i.width+" "+i.height,"aria-hidden":"true",focusable:"false"}),p=H("defs"),g=H("marker",{id:this.uid+"-arrow",viewBox:"0 0 8 8",refX:7,refY:4,markerWidth:7,markerHeight:7,orient:"auto-start-reverse"});g.appendChild(H("path",{d:"M0 0.6 7.4 4 0 7.4Z",class:"mlv-overview__head"})),p.appendChild(g),h.appendChild(p);for(let b of i.arrows)h.appendChild(H("path",{class:"mlv-overview__arrow",d:"M"+b.x+" "+b.y1+"V"+b.y2,"marker-end":"url(#"+this.uid+"-arrow)"}));for(let b of i.brackets){let x=H("path",{class:"mlv-overview__bracket",d:"M"+b.x0+" "+b.y1+"H"+b.x+"V"+b.y2+"H"+(b.x0+2),"marker-end":"url(#"+this.uid+"-arrow)"});b.back&&x.setAttribute("data-back","true"),h.appendChild(x)}this.content.appendChild(h);for(let b of i.arrows){let x=m(this.content,f("span","mlv-overview__arrowlabel",qt(b.count,"connection","connections")));x.style.left=b.labelX+"px",x.style.top=b.labelY+"px",x.setAttribute("aria-hidden","true")}for(let b of i.brackets){let x=e.phases[b.from],y=e.phases[b.to],k=m(this.content,f("span","mlv-overview__count",String(b.count)));k.style.left=b.labelX+"px",k.style.top=b.labelY+"px",b.back&&k.setAttribute("data-back","true"),k.setAttribute("aria-hidden","true"),k.title=qt(b.count,"connection","connections")+" from phase "+(x.index+1)+(b.back?" back to":" ahead to")+" phase "+(y.index+1)}let v=m(this.content,f("ol","mlv-overview__blocks"));v.setAttribute("aria-label",qt(e.phases.length,"phase","phases")),i.blocks.forEach((b,x)=>{let y=e.phases[b.phase],k=m(v,f("li","mlv-overview__item")),E=m(k,f("div","mlv-ovblock"));E.setAttribute("role","button"),E.setAttribute("data-phase-id",y.id),E.setAttribute("data-block",String(x)),E.setAttribute("data-head",b.twoLineHead?"two-lines":"one-line"),le(E,y.index),E.tabIndex=x===this.active?0:-1,pm(E,b),E.setAttribute("aria-label",hm(e,b.phase));let w=m(E,f("div","mlv-ovblock__head"));w.style.height=b.headH+"px";let S=m(w,f("div","mlv-ovblock__name"));m(S,f("span","mlv-ovblock__num",String(y.index+1)));let C=m(S,f("span","mlv-ovblock__label",y.label));C.title="Phase "+(y.index+1)+": "+y.label;let L=m(w,f("div","mlv-ovblock__meta"));m(L,f("span","mlv-ovblock__steps",zi(y)));let I=Y(y.findings);if(I>0){let A=Ce(y.findings,12,Gt(y.findings));A&&L.appendChild(A),m(L,f("span","mlv-ovblock__unit",Pe(I)))}let F=m(E,f("ul","mlv-ovblock__list"));F.id=this.uid+"-list-"+x,E.setAttribute("aria-describedby",F.id);for(let A of b.items){let P=m(F,f("li","mlv-ovitem"));if(P.style.left=gl(A.x-b.x)+"px",P.style.top=gl(A.y-b.y)+"px",P.style.width=Math.max(0,A.w)+"px",A.step<0){P.classList.add("mlv-ovitem--more"),P.textContent="… "+qt(A.more,"more step","more steps"),m(P,f("span","mlv-sr","."));continue}let N=y.steps[A.step];P.setAttribute("data-node-ref",N.id),A.depth&&P.setAttribute("data-depth",String(A.depth)),N.basis&&N.basis!=="observed"&&P.setAttribute("data-basis",N.basis);let z=m(P,f("span","mlv-ovitem__mark",N.basis==="unresolved"?"?":""));N.basis==="inferred"&&z.appendChild(ml()),z.setAttribute("aria-hidden","true"),(N.basis==="inferred"||N.basis==="unresolved")&&m(P,f("span","mlv-sr",N.basis+": "));let j=m(P,f("span","mlv-ovitem__title",N.title));if(j.title=N.title+(N.basis&&N.basis!=="observed"?" ("+N.basis+")":""),N.tags.length){let V=m(P,f("span","mlv-ovitem__tags"));for(let $ of N.tags){let Xe=m(V,f("span","mlv-ovitem__tag"));Xe.setAttribute("data-sev",$.severity),Xe.title=$.short+" ("+$.id+"), "+ye[$.severity]+" severity";let lt=Z($.severity,10,"");lt.setAttribute("aria-hidden","true"),lt.removeAttribute("role"),Xe.appendChild(lt),m(Xe,f("span","mlv-ovitem__tagid",$.short))}V.setAttribute("aria-hidden","true"),m(P,f("span","mlv-sr",", "+(N.tags.length===1?"finding ":"findings ")+N.tags.map($=>$.short).join(", ")))}N.tags.length||!/[.!?…]$/.test(N.title.trim())?m(P,f("span","mlv-sr",". ")):m(P,f("span","mlv-sr"," "))}_(E,"click",A=>{A.preventDefault(),A.stopPropagation(),this.active=x,this.cb.go(y.id,A.detail===0)}),_(E,"focus",()=>{if(this.active!==x){this.active=x;for(let[A,P]of this.blockEls.entries())P.tabIndex=A===x?0:-1}}),this.blockEls.push(E)})}onKey(e){if(e.ctrlKey||e.metaKey||e.altKey)return;let n=e.key,o=this.blockEls.length,i=!0;if(n===")"||n==="0"&&e.shiftKey)this.cb.back();else if(n==="ArrowDown"||n==="ArrowRight")this.focusBlock(Math.min(o-1,this.active+1));else if(n==="ArrowUp"||n==="ArrowLeft")this.focusBlock(Math.max(0,this.active-1));else if(n==="Home")this.focusBlock(0);else if(n==="End")this.focusBlock(o-1);else if(n==="Enter"||n===" "){let s=this.input&&this.input.phases[this.active];s&&this.cb.go(s.id,!0)}else i=!1;i&&(e.preventDefault(),e.stopPropagation())}destroy(){for(let e of this.disposers)try{e()}catch{}this.disposers=[]}};function hm(t,e){let n=t.phases[e],o=t.phases.length?t.phases[t.phases.length-1].index+1:0,i=["Phase "+(n.index+1)+" of "+Math.max(o,t.phases.length)+": "+n.label+".",zi(n)+"."],s=Y(n.findings);s>0&&i.push(Pe(s)+".");let r=[];for(let a of t.links){if(a.from!==e)continue;let l=t.phases[a.to];r.push(qt(a.count,"connection","connections")+(Vt(a)?" to":a.to0))return;let a=t.scrollTop;i-12a+r&&(a=Math.min(i-12-n,s+12-r)),o&&s+12<=r&&(a=0),a=Math.max(0,a),a!==t.scrollTop&&(t.scrollTop=a)}function gl(t){return Math.round(t*10)/10}function gm(t,e){let n=new Set([e]),o=new Set,i=s=>{let r=[e],a=new Set([e]);for(;r.length;){let l=r.pop();for(let d of t){let c=s?d.source:d.target,u=s?d.target:d.source;c===l&&(o.add(d.id),!a.has(u)&&(a.add(u),n.add(u),r.push(u)))}}};return i(!0),i(!1),{nodes:n,edges:o}}function Vi(t,e){let n=new Set([e]),o=new Set;for(let i of t)i.source!==e&&i.target!==e||(o.add(i.id),n.add(i.source),n.add(i.target));return{nodes:n,edges:o}}function vm(t,e,n){return n==="direct"?Vi(t,e):gm(t,e)}function vl(t,e,n,o,i){for(let r of t.nodes.values())r.classList.remove("is-lit");for(let r of t.edges.values())r.classList.remove("is-lit");if(!n){t.canvas.classList.remove(o);return}let s=vm(e,n,i);for(let r of s.nodes){let a=t.nodes.get(r);a&&a.classList.add("is-lit")}for(let r of s.edges){let a=t.edges.get(r);a&&a.classList.add("is-lit")}t.canvas.classList.add(o)}var K={MAX_EDGES:120,PULSE_SPEED:320,PULSE_MIN_MS:380,PULSE_MAX_MS:2200,STREAM_SPEED:220,HEAD_PX:12,GAP_DATA:34,GAP_CALL:96,GAP_BACK:24,PERIOD_DATA:210,PERIOD_CALL:490,PERIOD_BACK:165,HOP_MS:90,HOP_MAX:6,PORT_R:3.5,CHARGE_HALO_R:9,CHARGE_GLOW_R:5.5,CHARGE_CORE_R:2.6,CHARGE_TWIN_PX:360,SETTLE_PASSES:2,SETTLE_MIN_MS:1600,SETTLE_MAX_MS:6e3},bm=["--mlv-flow-dur","--mlv-flow-len","--mlv-flow-head","--mlv-flow-gap","--mlv-flow-end","--mlv-flow-delay","--mlv-flow-iter"],wm=["is-flowing","is-flowing--pulse","is-flow-settled"];function bl(t){let e=0;for(let n=1;n0?Math.max(1,Math.round((t-e)/n)):1}function wl(t){return Math.min(t,K.HOP_MAX)*K.HOP_MS}function Sm(t){return t.kind!=="config"}function Mm(t,e){let n=new Map([[e,0]]),o=new Map,i=s=>{let r=[e],a=new Set([e]),l=0;for(;r.length;){l++;let d=[];for(let c of r)for(let u of t){let h=s?u.source:u.target,p=s?u.target:u.source;if(h!==c)continue;let g=o.get(u.id);if((g===void 0||lthis.pulse(e);let n=this.modeFor(1);if(this.host.canvas.setAttribute("data-flow",n),n==="off")return;let o=this.host.edges().get(e.id);if(!o)return;this.decorate(o,e,n,null,0),o.classList.add("is-flowing--pulse"),this.decorated.push(e.id),this.decoratedRoutes.set(e.id,e);let i=this.host.nodes(),s=i.get(e.source),r=i.get(e.target);s&&(s.classList.add("is-flow-source"),this.sourceId=e.source),r&&(r.classList.add("is-flow-target"),this.targetId=e.target)}stream(e,n){this.clear(),this.lastRun=()=>{this.stream(e,n)};let o=this.host.routes(),i=Lm(o,e,n),s=i.edges.size,r=this.modeFor(s);this.host.canvas.setAttribute("data-flow",r);let a=s>K.MAX_EDGES,l=a&&r!=="off";if(r==="off"||a)return{mode:r,capped:l,edges:s};let d=this.host.edges(),c=[],u=0,h=0;for(let g of o){let v=i.edges.get(g.id);if(v===void 0||!Sm(g))continue;let b=d.get(g.id);b&&(c.push({route:g,g:b,hop:v}),u=Math.max(u,bl(g.points||[])),h=Math.max(h,wl(v)))}let p=km(u,h);for(let{route:g,g:v,hop:b}of c)this.decorate(v,g,r,b,p),v.classList.add("is-flowing"),this.decorated.push(g.id),this.decoratedRoutes.set(g.id,g);return{mode:r,capped:l,edges:s}}settle(e){let n=this.decoratedRoutes.get(e),o=this.host.edges().get(e);if(!n||!o||o.classList.contains("is-flow-settled"))return!1;let i=o.querySelectorAll(".mlv-edge__flow, .mlv-edge__charge");for(let s=0;s=2&&!o.querySelector(".mlv-edge__dir")&&o.appendChild(kl(n)),o.classList.add("is-flow-settled"),this.settled&&this.host.canvas.setAttribute("data-flow-settled","true"),!0}get settled(){if(!this.decorated.length)return!1;let e=this.host.edges();return this.decorated.every(n=>{let o=e.get(n);return!o||o.classList.contains("is-flow-settled")})}get flowing(){return this.decorated.length>0}replay(){return!this.lastRun||!this.decorated.length||this.modeFor(1)!=="motion"?!1:(this.lastRun(),this.decorated.length>0)}modeFor(e){return this.enabledFlag?this.motionFlag==="reduced"||e>K.MAX_EDGES?"static":"motion":"off"}decorate(e,n,o,i,s){let r=n.points||[];if(r.length<2)return;let a=bl(r),l=e.style;if(e.appendChild(El("out",r[0])),e.appendChild(El("in",r[r.length-1])),o==="static"){e.appendChild(kl(n));return}let d=H("path",{class:"mlv-edge__flow",d:n.d,pathLength:100});e.appendChild(d);let c=xl(K.HEAD_PX,a);if(l.setProperty("--mlv-flow-len",Ge(a)+"px"),l.setProperty("--mlv-flow-head",String(c)),i===null){let u=ym(a);l.setProperty("--mlv-flow-dur",u+"ms"),l.setProperty("--mlv-flow-iter",String(K.SETTLE_PASSES));let h=a>0?Im(e):"";if(h){let p=Am(e),g=yl(h,u,0,p);e.appendChild(g),this.settleOnEnd(g,n.id),a>K.CHARGE_TWIN_PX&&e.appendChild(yl(h,u,-Math.round(u/2),p))}}else{let u=xm(n.kind,n.subkind,n.back),h=xl(u,a);l.setProperty("--mlv-flow-gap",String(h)),l.setProperty("--mlv-flow-end",String(Ge(-(c+h))));let p=wl(i);l.setProperty("--mlv-flow-delay",p+"ms"),l.setProperty("--mlv-flow-iter",String(_m(s,p,Em(n.kind,n.back)))),this.settleOnEnd(d,n.id)}}settleOnEnd(e,n){e.addEventListener("animationend",o=>{o.target===e&&this.settle(n)})}};function Im(t){let e=t.querySelector(".mlv-edge__path");return e&&e.getAttribute("id")||""}function Am(t){try{let e=t,n=e.ownerSVGElement||(typeof e.closest=="function"?e.closest("svg"):null);if(!n||typeof n.getCurrentTime!="function")return null;let o=n.getCurrentTime();return typeof o=="number"&&isFinite(o)?o:null}catch{return null}}function yl(t,e,n,o){let i=H("g",{class:n===0?"mlv-edge__charge":"mlv-edge__charge mlv-edge__charge--twin"});i.appendChild(H("circle",{class:"mlv-edge__charge-halo",cx:0,cy:0,r:K.CHARGE_HALO_R})),i.appendChild(H("circle",{class:"mlv-edge__charge-glow",cx:0,cy:0,r:K.CHARGE_GLOW_R})),i.appendChild(H("circle",{class:"mlv-edge__charge-core",cx:0,cy:0,r:K.CHARGE_CORE_R}));let s=H("animateMotion",{dur:e+"ms",repeatCount:String(K.SETTLE_PASSES),calcMode:"linear",rotate:"auto",fill:"freeze"});o!==null?s.setAttribute("begin",Cm(o+n/1e3)+"s"):n!==0&&s.setAttribute("begin",n+"ms");let r=H("mpath");return r.setAttribute("href","#"+t),r.setAttributeNS(Ns,"xlink:href","#"+t),s.appendChild(r),i.appendChild(s),i}function xl(t,e){return e>0?Ge(Math.min(100,t/e*100)):100}function Ge(t){return Math.round(t*100)/100}function Cm(t){return Math.round(t*1e3)/1e3}function El(t,e){return H("circle",{class:"mlv-edge__port mlv-edge__port--"+t,cx:Ge(e.x),cy:Ge(e.y),r:K.PORT_R})}function kl(t){return H("path",{class:"mlv-edge__dir",d:"M -3.4 -3.6 L 0 0 L -3.4 3.6",fill:"none",transform:"translate("+Ge(t.mid.x)+","+Ge(t.mid.y)+") rotate("+Ge(t.midAngle)+")"})}function _l(t){for(let o of wm)t.classList.remove(o);let e=t.style;if(e&&typeof e.removeProperty=="function")for(let o of bm)e.removeProperty(o);let n=t.querySelectorAll(".mlv-edge__flow, .mlv-edge__charge, .mlv-edge__port, .mlv-edge__dir");for(let o=0;oe.edges(),nodes:()=>e.nodes(),routes:()=>e.routes()}),this.motionWatch=new Wn(n=>{this.controller.setMotion(n),this.controller.syncCanvas(),this.cappedKey=null,this.stop()}),this.controller.setMotion(this.motionWatch.mode),this.controller.syncCanvas()}get enabled(){return this.controller.enabled}get motion(){return this.controller.motion}setEnabled(e){this.controller.setEnabled(e),e&&this.stop()}syncCanvas(){this.controller.syncCanvas()}clear(){this.cappedKey=null,this.controller.clear()}pulse(e){this.controller.pulse(e)}replay(){return this.controller.replay()}stream(e,n){let o=this.controller.stream(e,n);if(!o.capped)return;let i=n+":"+e;this.cappedKey!==i&&(this.cappedKey=i,this.host.toast(Tm(o.edges,this.controller.motion,n)))}stop(){if(this.latchedEdgeId){let i=this.routeById(this.latchedEdgeId);if(i){this.controller.pulse(i);return}}let e=this.host.lockedNodeId();if(e){this.stream(e,"lineage");return}let n=this.host.hoverNodeId();if(n){this.stream(n,"direct");return}let o=this.host.hoverEdgeId();if(o){let i=this.routeById(o);if(i){this.controller.pulse(i);return}}this.controller.clear()}setLatchedEdge(e){this.latchedEdgeId=e}routeById(e){for(let n of this.host.routes())if(n.id===e)return n;return null}incidentRoutes(e){return this.host.routes().filter(n=>n.source===e||n.target===e)}focusEdge(e){let n=this.host.edges().get(e);if(!n)return!1;let o=n.querySelector(".mlv-edge__hit");if(!o||typeof o.focus!="function")return!1;try{o.focus()}catch{return!1}return!0}blurFocusedEdge(){let e=this.host.canvas.ownerDocument,n=e?e.activeElement:null;return!n||!n.classList||!n.classList.contains("mlv-edge__hit")||typeof n.blur!="function"?!1:(n.blur(),!0)}edgeAnnouncement(e){let n=this.host.index(),o=n?n.nodeById.get(e.source):null,i=n?n.nodeById.get(e.target):null,s=o?o.label||o.qualname:e.source,r=i?i.label||i.qualname:e.target;return"Connection: "+s+" to "+r+(e.label?", "+e.label:"")+"."}destroy(){this.motionWatch.destroy()}};function Rm(t,e,n){let o=n.x-e.x,i=n.y-e.y,s=t.x-e.x,r=t.y-e.y,a=o*o+i*i,l=a>0?(s*o+r*i)/a:0;l<0?l=0:l>1&&(l=1);let d=s-l*o,c=r-l*i;return d*d+c*c}function Om(t,e){let n=t.points||[];if(n.length===0)return 1/0;if(n.length===1){let i=e.x-n[0].x,s=e.y-n[0].y;return Math.sqrt(i*i+s*s)}let o=1/0;for(let i=1;ithis.resolve(e))}enter(e){this.host.held&&this.host.held()||this.set(e)}drop(){let e=this.timer!==null;if(e&&(clearTimeout(this.timer),this.timer=null),!this.currentId){e&&this.host.close();return}let n=this.host.edges().get(this.currentId);n&&n.classList.remove("is-hover"),this.currentId=null,this.host.changed&&this.host.changed(null),this.host.close()}leave(e){this.currentId===e.id&&this.set(null)}reset(){this.currentId=null,this.timer!==null&&(clearTimeout(this.timer),this.timer=null),this.host.hide()}destroy(){this.reset()}resolve(e){if(this.host.held&&this.host.held())return;let n=this.host.routes();if(!n.length||this.host.canvas.classList.contains("is-panning"))return;let o=e.target;if(o&&typeof o.closest=="function"&&o.closest(Pm)){this.currentId&&this.set(null);return}let i=this.host.canvas.getBoundingClientRect(),s=this.host.viewport.vp,r=s.zoom>0?s.zoom:1,a={x:(e.clientX-i.left-s.x)/r,y:(e.clientY-i.top-s.y)/r},l=this.host.markers?this.host.markers():null,d=l&&l.size?Ml(n,l,a,ct):null;this.set(d||Sl(n,a,Hm/r))}set(e){let n=e?e.id:null;if(this.currentId===n)return;let o=this.host.edges();if(this.currentId){let i=o.get(this.currentId);i&&i.classList.remove("is-hover")}if(this.currentId=n,e){let i=o.get(e.id);if(i){i.classList.add("is-hover");let s=i.parentNode;s&&s.lastChild!==i&&s.appendChild(i)}}this.host.changed&&this.host.changed(e),this.intent(e)}intent(e){this.timer!==null&&(clearTimeout(this.timer),this.timer=null);let n=()=>{e?this.host.open(e):this.host.close()},o=e?this.host.openDelayMs:this.host.closeDelayMs;if(o<=0){n();return}this.timer=setTimeout(()=>{this.timer=null,n()},o)}};var $n=12,nt=8;function Nm(t,e,n,o,i){let s=o.w>0&&o.h>0,r=!1;if(s){let d=e-$n-o.h>=nt,c=i.h<=0||n+$n+o.h<=i.h-nt;r=!d&&c}let a=r?n+$n:e-$n;s&&(r?a=Math.min(a,Math.max(nt,i.h-nt-o.h)):a=Math.max(a,nt+o.h));let l=t;if(s&&i.w>0){let d=o.w/2,c=d+nt,u=i.w-d-nt;l=u>=c?Math.min(Math.max(t,c),u):i.w/2}return{left:Math.round(l),top:Math.round(a),below:r}}var Jn=class{constructor(e,n){this.root=f("div","mlv-tooltip"),this.root.setAttribute("role","tooltip"),this.root.hidden=!0,this.viewport=e,this.bounds=n||(()=>({w:0,h:0}))}showNode(e,n,o,i){let s=e.nodeById.get(n);if(!s)return;D(this.root),m(this.root,f("div","mlv-tooltip__title",s.label||s.qualname));let r=fe(s.basis);r&&m(this.root,f("div","mlv-tooltip__row mlv-tooltip__basis",r.replace("? ","")+", not observed")),s.sublabel&&m(this.root,f("div","mlv-tooltip__row",s.sublabel)),s.loc.file&&m(this.root,Qe("mlv-tooltip__loc",s.loc,"div"));let a=o.isGroup||o.collapsed;this.issueRows(a?e.subtreeIssues(n,i):e.issuesOf(n,i)),this.placeAt(o.x+o.w/2,o.y,o.y+o.h)}showEdge(e,n,o,i){let s=e.edgeById.get(n.id);D(this.root);let r=n.count<=1&&s&&s.label?s.label:"";m(this.root,f("div","mlv-tooltip__title",r||n.label||Ht(n.kind)));let a=s&&s.authoredKind?" · authored as "+s.authoredKind:"";m(this.root,f("div","mlv-tooltip__row",Ht(n.kind)+(n.subkind?" · "+n.subkind:"")+a));let l=s?fe(s.basis):"";l&&m(this.root,f("div","mlv-tooltip__row mlv-tooltip__basis",l.replace("? ","")+", not observed")),s&&s.loc.file&&m(this.root,Qe("mlv-tooltip__loc",s.loc,"div")),n.count>1&&m(this.root,f("div","mlv-tooltip__row",n.count+" merged connections")),this.issueRows(e.issuesOfEdges(n.ids,o)),i?this.placeAt(i.x,i.y-ct,i.y+ct):this.placeAt(n.mid.x,n.mid.y,n.mid.y)}issueRows(e){for(let n of e){let o=m(this.root,f("div","mlv-tooltip__row"));o.appendChild(Z(n.severity,12,"")),m(o,f("span",""," "+(n.short?n.short+" · ":"")+n.code+" "+n.title))}}placeAt(e,n,o){let i=this.viewport();this.root.hidden=!1,this.root.style.transform="translate(-50%, -100%)";let s=this.root.getBoundingClientRect(),r=Nm(e*i.zoom+i.x,n*i.zoom+i.y,o*i.zoom+i.y,{w:s.width,h:s.height},this.bounds());this.root.style.left=r.left+"px",this.root.style.top=r.top+"px",this.root.style.transform=r.below?"translate(-50%, 0)":"translate(-50%, -100%)"}hide(){this.root.hidden=!0}};function Ll(t){let e=f("div","mlv-state mlv-state--empty");e.setAttribute("role","status");let n=m(e,f("div","mlv-state__inner"));m(n,f("h2","mlv-state__title","No workflow steps")),m(n,f("p","mlv-state__body","This authored workflow does not contain any steps."));let o=t?t.diagnostics||[]:[];if(o.length){let i=m(n,f("ul","mlv-state__list"));for(let s of o.slice(0,8))m(i,f("li","",s.kind+" — "+s.message))}else t&&m(n,f("p","mlv-state__body","The author inspected "+t.workspace.filesAnalyzed+" files."));return e}function Il(t){let e=f("div","mlv-state mlv-state--empty");e.setAttribute("role","status");let n=m(e,f("div","mlv-state__inner"));m(n,f("h2","mlv-state__title","No steps match your filters"));let o=m(n,f("div","mlv-state__actions")),i=U("mlv-btn","Clear all filters");return _(i,"click",t),o.appendChild(i),e}var eo=class{constructor(){this.timers=[];this.root=f("div","mlv-toasts"),this.root.setAttribute("role","status")}show(e){let n=f("div","mlv-toast",e);for(this.root.appendChild(n);this.root.childElementCount>3&&this.root.firstElementChild;)this.root.removeChild(this.root.firstElementChild);let o=setTimeout(()=>{n.parentNode===this.root&&this.root.removeChild(n)},4e3);this.timers.push(o)}destroy(){for(let e of this.timers)clearTimeout(e);this.timers=[],D(this.root)}};var Fm=0,Bm=1,Dm=2,jm=100/3,qi=800,Gm=.999,Rl=40,Wm=6;function zm(t,e=qi){let n=typeof t.deltaMode=="number"?t.deltaMode:Fm,o=1;return n===Bm?o=jm:n===Dm&&(o=e>0?e:qi),{dx:Al(t.deltaX)*o,dy:Al(t.deltaY)*o,mode:n}}function Al(t){return typeof t=="number"&&isFinite(t)?t:0}function Vm(t,e){return t.ctrlKey||t.metaKey?"zoom":t.shiftKey?"pan":e.dx===0&&Math.abs(e.dy)>=Rl?"zoom":"pan"}function qm(t,e){let n=(t.ctrlKey||t.metaKey)&&Math.abs(e.dy){n.preventDefault();let o=t.getBoundingClientRect(),i=zm(n,o.height||t.clientHeight||qi);if(Vm(n,i)==="zoom"){e.zoomAt(qm(n,i),n.clientX-o.left,n.clientY-o.top);return}let s=Km(n,i);e.panBy(s.dx,s.dy)})}function Cl(t,e){let n=t.x-e.x,o=t.y-e.y;return Math.sqrt(n*n+o*o)}function Tl(t,e){return{x:(t.x+e.x)/2,y:(t.y+e.y)/2}}var to=class{constructor(e,n){this.points=new Map;this.lastDistance=0;this.lastMid=null;this.canvas=e,this.viewport=n}active(){return this.points.size>=2}down(e){this.points.set(e.pointerId,{x:e.clientX,y:e.clientY}),this.reseed()}up(e){this.points.delete(e.pointerId),this.reseed()}clear(){this.points.clear(),this.lastDistance=0,this.lastMid=null}move(e){if(!this.points.has(e.pointerId)||(this.points.set(e.pointerId,{x:e.clientX,y:e.clientY}),this.points.size<2))return!1;let n=this.pair();if(!n)return!1;let o=Cl(n[0],n[1]),i=Tl(n[0],n[1]),s=this.canvas.getBoundingClientRect();return this.lastDistance>0&&o>0&&(this.lastMid&&this.viewport.panBy(i.x-this.lastMid.x,i.y-this.lastMid.y),this.viewport.zoomAt(o/this.lastDistance,i.x-s.left,i.y-s.top)),this.lastDistance=o,this.lastMid=i,!0}pair(){let e=Array.from(this.points.keys()).sort((n,o)=>n-o);return e.length<2?null:[this.points.get(e[0]),this.points.get(e[1])]}reseed(){let e=this.pair();if(!e){this.lastDistance=0,this.lastMid=null;return}this.lastDistance=Cl(e[0],e[1]),this.lastMid=Tl(e[0],e[1])}};function Hl(t){let e=t.ownerDocument;return!e||!e.body||t.parentElement!==e.body?()=>{}:(e.documentElement.classList.add("mlv-fills-page"),e.body.classList.add("mlv-fills-page"),()=>{e.documentElement.classList.remove("mlv-fills-page"),e.body.classList.remove("mlv-fills-page")})}var Um=0;function Pl(t,e){t.classList.add("mlv-root"),t.setAttribute("data-theme",e),D(t);let n="mlv-shell"+ ++Um,o=f("div","mlv-body"),i=f("main","mlv-main");i.setAttribute("aria-labelledby",n+"-main-heading");let s=m(i,f("h2","mlv-sr","Pipeline diagram"));s.id=n+"-main-heading";let r=f("div","mlv-canvas");r.id=n+"-canvas",r.setAttribute("role","application"),r.setAttribute("aria-label","ML pipeline diagram"),r.setAttribute("data-lod","full"),r.tabIndex=0,i.appendChild(r);let a=f("a","mlv-skiplink","Skip to diagram");a.href="#"+r.id,_(a,"click",k=>{k.preventDefault();try{r.focus()}catch{}}),t.appendChild(a);let l=m(r,f("div","mlv-world")),d=m(l,f("div","mlv-layer mlv-layer--lanes")),c=H("svg",{class:"mlv-layer mlv-layer--edges",width:1,height:1});c.appendChild(pr());let u=H("g",{class:"mlv-bundles"}),h=H("g",{class:"mlv-edges"}),p=H("g",{class:"mlv-connectors"});c.appendChild(u),c.appendChild(h),c.appendChild(p),l.appendChild(c);let g=m(l,f("div","mlv-layer mlv-layer--nodes")),v=f("div","mlv-zoom"),b=m(v,f("span","mlv-zoom__level","100%"));b.setAttribute("aria-label","Zoom level");let x=m(r,f("div","mlv-statehost")),y=f("div","mlv-sr");return y.setAttribute("aria-live","polite"),y.setAttribute("role","status"),{body:o,main:i,canvas:r,world:l,lanesLayer:d,edgesSvg:c,bundleGroup:u,edgeGroup:h,connectorLayer:p,nodesLayer:g,zoomBar:v,zoomLevel:b,stateHost:x,live:y}}function Xm(t){let e=t;return!!e.classList&&e.classList.contains("mlv-root")}function Nl(t,e,n){let o=[],i=new to(t,e),s=!1,r=0,a=0;o.push(_(t,"pointerdown",c=>{let u=c.target;if(u&&u.closest&&u.closest(".mlv-overview"))return;if(i.down(c),i.active()){s=!1,t.classList.remove("is-panning");return}let h=c.target;if(!(h.closest&&h.closest(".mlv-node, .mlv-group__header, .mlv-phaseindex, .mlv-zoom, .mlv-edge__hit, .mlv-legend, .mlv-statehost, .mlv-tooltip, .mlv-toasts"))){s=!0,r=c.clientX,a=c.clientY,t.classList.add("is-panning");try{t.setPointerCapture(c.pointerId)}catch{}}})),o.push(_(t,"pointermove",c=>{i.move(c)||s&&(e.panBy(c.clientX-r,c.clientY-a),r=c.clientX,a=c.clientY)}));let l=c=>{c&&i.up(c),s=!1,t.classList.remove("is-panning")};o.push(_(t,"pointerup",l)),o.push(_(t,"pointercancel",l)),o.push(_(t,"pointerleave",l)),o.push(Ol(t,e)),o.push(_(t,"keydown",c=>n.onKeyDown(c)));let d=t.ownerDocument;return d&&o.push(_(d,"keydown",c=>{let u=c.target;u&&(u!==d.body&&u!==d.documentElement&&!Xm(u)||n.onKeyDown(c))})),o.push(_(t,"click",c=>{let u=c.target;(!u.closest||!u.closest(".mlv-node, .mlv-edge__hit, .mlv-group, .mlv-phaseindex, .mlv-overview, .mlv-legend"))&&n.onBackgroundClick()})),typeof window<"u"&&o.push(_(window,"resize",()=>n.onResize?n.onResize():e.apply())),o.push(()=>i.clear()),o}function Fl(t,e,n,o,i){D(t);let s=i.nodeIds[0];if(!s)return 0;let r=n.boxes.get(e.visibleRepresentative(s,o));if(!r)return 0;let a=ee(i.severity),l=0,d=new Set([r.id]);for(let c of i.nodeIds.slice(1)){let u=e.nodeById.get(c);if(!u)continue;let h=n.boxes.get(e.visibleRepresentative(c,o));!h||d.has(h.id)||(d.add(h.id),l++,t.appendChild(vr({x:r.x+r.w/2,y:r.y},{x:h.x+h.w/2,y:h.y},l,a,"Also affects — "+(u.label||u.id))))}return l}var no=class{constructor(e){this.hoverId=null;this.hoverTimer=null;this.pendingHover=null;this.focusLocked=!1;this.focusNodeId=null;this.focusShown=null;this.ctx=e}get hoverNodeId(){return this.hoverId}get lockedNodeId(){return this.focusLocked?this.focusShown??this.focusNodeId:null}get isFocusLocked(){return this.focusLocked}resetHover(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null),this.pendingHover=null,this.hoverId=null,this.trace(null,"is-tracing","direct"),this.ctx.tooltip.hide(),this.retraceFocus()}retraceFocus(){if(!this.focusLocked||!this.focusNodeId)return;let e=this.ctx,n=e.index(),o=n&&n.nodeById.has(this.focusNodeId)?n.visibleRepresentative(this.focusNodeId,e.collapsed()):null;if(o&&e.nodeEls().has(o)){this.focusShown=o,e.canvas.classList.remove("is-tracing"),this.trace(o,"is-focusing","lineage");return}this.focusLocked=!1,this.focusNodeId=null,this.focusShown=null,e.canvas.classList.remove("is-focusing"),this.trace(null,"is-focusing","lineage"),e.announce("Focus mode off.")}applySelection(e){let n=this.ctx;for(let o of n.nodeEls().values())o.classList.remove("is-selected");for(let o of n.edgeEls().values())o.classList.remove("is-selected");if(D(n.connectors),n.flow.setLatchedEdge(e&&e.kind==="edge"?e.id:null),!e){n.canvas.removeAttribute("aria-activedescendant"),this.retraceFocus(),n.flow.stop(),n.syncBundles();return}if(e.kind==="node"){let o=n.nodeEls().get(e.id);o&&(o.classList.add("is-selected"),n.canvas.setAttribute("aria-activedescendant",o.id))}else if(e.kind==="edge"){let o=n.edgeEls().get(e.id);o&&o.classList.add("is-selected")}else e.kind==="issue"&&this.highlightIssue(e.id);this.focusLocked&&e.kind==="node"?(this.focusNodeId=e.id,this.retraceFocus()):this.focusLocked&&this.trace(null,"is-focusing","lineage"),n.flow.stop(),n.syncBundles()}highlightIssue(e){let n=this.ctx,o=n.index(),i=n.frame();if(!o||!i)return;let s=o.issueById.get(e);if(!s)return;let r=s.nodeIds[0];if(r){let a=n.nodeEls().get(o.visibleRepresentative(r,n.collapsed()));a&&(a.classList.add("is-selected"),n.canvas.setAttribute("aria-activedescendant",a.id))}for(let a of s.edgeIds)for(let[l,d]of n.edgeEls())(d.getAttribute("data-edge-ids")||l).split(" ").indexOf(a)>=0&&d.classList.add("is-selected");Fl(n.connectors,o,i,n.collapsed(),s)}hoverIntent(e){if(e&&this.ctx.hoverHeld&&this.ctx.hoverHeld())return;if(this.hoverTimer!==null){if(this.pendingHover===e)return;clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null}if(this.hoverId===e)return;let n=e?400:120;if(n<=0){this.setHover(e);return}this.pendingHover=e,this.hoverTimer=setTimeout(()=>{this.hoverTimer=null,this.pendingHover=null,this.setHover(e)},n)}dropHover(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null),this.hoverId&&this.setHover(null)}setHover(e){if(this.hoverId===e||(this.hoverId=e,this.focusLocked))return;let n=this.ctx;if(!e){this.trace(null,"is-tracing","direct"),n.tooltip.hide(),n.flow.stop(),n.syncBundles();return}this.trace(e,"is-tracing","direct"),n.flow.stream(e,"direct"),n.syncBundles();let o=n.index(),i=n.frame();if(!o||!i)return;let s=i.boxes.get(e);s&&n.tooltip.showNode(o,e,s,r=>n.keep(r))}trace(e,n,o){let i=this.ctx;vl({nodes:i.nodeEls(),edges:i.edgeEls(),canvas:i.canvas},i.routes(),e,n,o),i.syncBundles()}toggleFocusMode(e){let n=this.ctx;if(this.focusLocked){this.focusLocked=!1,this.focusNodeId=null,this.focusShown=null,n.canvas.classList.remove("is-focusing"),this.trace(null,"is-focusing","lineage"),n.flow.clear(),n.syncBundles(),n.announce("Focus mode off.");let o=this.hoverId;o&&(this.hoverId=null,this.setHover(o));return}if(!e||e.kind!=="node"){n.toast("Select a node first, then press F to focus.");return}this.focusLocked=!0,this.focusNodeId=e.id,this.retraceFocus(),this.focusLocked&&(n.flow.stream(this.focusShown??e.id,"lineage"),n.announce("Focus mode on."))}destroy(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null)}};function Bl(t,e,n,o){let i=n?t.querySelector(".mlv-group__header"):t;if(!i)return;let s=t.querySelector(".mlv-group__chevron-btn");s&&(_(s,"click",d=>{d.preventDefault(),d.stopPropagation(),o.isGroup(e)&&o.toggleCollapse(e)}),_(s,"dblclick",d=>{d.preventDefault(),d.stopPropagation()}));let r=n||o.isGroup(e),a=null,l=()=>{a!==null&&(clearTimeout(a),a=null)};o.addDisposer(l),_(i,"click",d=>{if(d.stopPropagation(),!r){o.activateNode(e,d);return}l(),a=setTimeout(()=>{a=null,o.activateNode(e)},220)}),_(i,"dblclick",d=>{d.preventDefault(),d.stopPropagation(),l(),o.isGroup(e)?o.toggleCollapse(e):r||o.openNode(e,!1)}),_(i,"pointerenter",()=>o.hoverIntent(e)),_(i,"pointerleave",()=>o.hoverIntent(null)),_(i,"keydown",d=>{d.key==="Enter"&&!d.ctrlKey&&!d.metaKey&&!d.shiftKey?(d.preventDefault(),d.stopPropagation(),o.openNode(e,d.altKey)):d.key===" "&&o.isGroup(e)&&(d.preventDefault(),d.stopPropagation(),o.toggleCollapse(e))})}function Dl(t,e,n){let o=t.querySelector(".mlv-edge__hit");if(!o)return;let i=o;_(i,"click",s=>{s.stopPropagation(),n.activateEdge(e.id,s)}),_(i,"dblclick",s=>{s.preventDefault(),s.stopPropagation(),n.openEdge(e.id,!1)}),_(i,"pointerenter",()=>n.enterEdge(e)),_(i,"pointerleave",()=>n.leaveEdge(e)),_(i,"keydown",s=>{s.key!=="Enter"||s.ctrlKey||s.metaKey||s.shiftKey||(s.preventDefault(),s.stopPropagation(),n.openEdge(e.id,s.altKey))}),_(i,"focus",()=>{t.classList.add("is-hover"),n.syncBundles(),n.pulse(e)}),_(i,"blur",()=>{t.classList.remove("is-hover"),n.syncBundles(),n.stopFlow()})}var oo=class{constructor(e,n){this.overviewReturn=null;this.panelWidth=0;this.indexFolded=!1;this.indexHidden=!1;this.arrowLane=null;this.hoverHeld=!1;this.pointerAt=null;this.heldAt=null;this.index=null;this.frameData=null;this.routes=[];this.labelPlan=null;this.collapsedSet=new Set;this.staleFiles=new Map;this.nodeEls=new Map;this.edgeEls=new Map;this.markerPoints=new Map;this.bundles=new Pn;this.mountSerial=hr();this.disposers=[];this.lastArea=null;this.host=n,this.canvasEl=e.canvas,this.worldEl=e.world,this.lanesLayer=e.lanesLayer,this.edgesSvg=e.edgesSvg,this.bundleGroup=e.bundleGroup,this.edgeGroup=e.edgeGroup,this.connectorLayer=e.connectorLayer,this.nodesLayer=e.nodesLayer,this.zoomLevelEl=e.zoomLevel,this.stateHost=e.stateHost,this.tooltip=new Jn(()=>this.viewport.vp,()=>this.viewport.size()),this.canvasEl.appendChild(this.tooltip.root),this.phaseIndex=new qn({go:i=>this.goToPhase(i,{focusCanvas:!0}),fold:i=>{this.indexFolded=i,this.syncPhaseIndex(),this.host.onPhaseIndexChanged()}}),this.canvasEl.appendChild(this.phaseIndex.root),this.overview=new Xn({go:(i,s)=>{s&&this.holdHover(),this.goToPhase(i,{focusCanvas:!0})},back:()=>this.closeOverview(!0)}),this.canvasEl.appendChild(this.overview.root),this.toasts=new eo,this.canvasEl.appendChild(this.toasts.root),this.viewport=new zn(this.canvasEl,this.worldEl,i=>{this.zoomLevelEl.textContent=Math.round(i.zoom*100)+"%";let s=this.viewport.size();this.phaseIndex.setInView(i,s.w,s.h),this.lastArea=this.viewport.visibleArea(),this.host.onViewportChange(i)}),this.viewport.setCovered(()=>this.indexCovered());for(let i of Nl(this.canvasEl,this.viewport,{onKeyDown:s=>this.host.onKeyDown(s),onBackgroundClick:()=>this.host.onBackgroundClick(),onResize:()=>this.handleResize()}))this.disposers.push(i);let o=typeof window<"u"?window.ResizeObserver:void 0;if(o){let i=new o(()=>this.handleResize());i.observe(this.canvasEl),this.disposers.push(()=>i.disconnect())}this.flow=new Zn({canvas:this.canvasEl,edges:()=>this.edgeEls,nodes:()=>this.nodeEls,routes:()=>this.routes,index:()=>this.index,hoverNodeId:()=>this.emphasis.hoverNodeId,lockedNodeId:()=>this.emphasis.lockedNodeId,hoverEdgeId:()=>this.edgeHover.hoveredId,toast:i=>this.toasts.show(i)}),this.emphasis=new no({canvas:this.canvasEl,connectors:this.connectorLayer,flow:this.flow,tooltip:this.tooltip,nodeEls:()=>this.nodeEls,edgeEls:()=>this.edgeEls,routes:()=>this.routes,index:()=>this.index,frame:()=>this.frameData,collapsed:()=>this.collapsedSet,syncBundles:()=>this.syncBundles(),keep:i=>this.host.keep(i),announce:i=>this.host.announce(i),toast:i=>this.toasts.show(i),hoverHeld:()=>this.hoverHeld}),this.edgeHover=new Qn({canvas:this.canvasEl,viewport:this.viewport,routes:()=>this.routes,edges:()=>this.edgeEls,markers:()=>this.markerPoints,open:i=>{this.index&&(this.tooltip.showEdge(this.index,i,s=>this.host.keep(s),this.markerPoints.get(i.id)),this.flow.pulse(i))},close:()=>{this.emphasis.hoverNodeId||this.tooltip.hide(),this.flow.stop()},hide:()=>this.tooltip.hide(),changed:()=>this.syncBundles(),openDelayMs:400,closeDelayMs:120,held:()=>this.hoverHeld}),this.disposers.push(_(this.canvasEl,"pointermove",i=>this.trackPointer(i))),this.disposers.push(_(this.canvasEl,"pointerdown",()=>this.releaseHover())),this.disposers.push(this.edgeHover.wire())}holdHover(){this.hoverHeld=!0,this.heldAt=this.pointerAt,this.emphasis.dropHover(),this.edgeHover.drop(),this.tooltip.hide()}get hoverIsHeld(){return this.hoverHeld}releaseHover(){this.hoverHeld=!1,this.heldAt=null}trackPointer(e){let n={x:e.clientX,y:e.clientY};if(this.pointerAt=n,!this.hoverHeld)return;if(!this.heldAt){this.heldAt=n;return}if(n.x===this.heldAt.x&&n.y===this.heldAt.y)return;this.releaseHover();let o=e.target,i=o&&typeof o.closest=="function"?o.closest(".mlv-node[data-node-id], .mlv-group__header"):null,s=i&&i.classList.contains("mlv-group__header")?i.closest("[data-node-id]"):i,r=s?s.getAttribute("data-node-id"):null;r&&this.canvasEl.contains(s)&&this.emphasis.hoverIntent(r)}get frame(){return this.frameData}get collapsed(){return this.collapsedSet}setIndex(e){this.index=e}setCollapsed(e){this.collapsedSet=new Set(e)}get phaseIndexFolded(){return this.indexFolded}get phaseIndexHidden(){return this.indexHidden}setPhaseIndex(e){typeof e.folded=="boolean"&&(this.indexFolded=e.folded),typeof e.hidden=="boolean"&&(this.indexHidden=e.hidden),this.syncPhaseIndex()}get phaseIndexElement(){return this.phaseIndex.root}phasesInView(){return this.phaseIndex.inView()}setPanelWidth(e){e>0&&(this.panelWidth=e,this.syncPhaseIndex())}phaseIndexUnavailable(){return this.phaseIndex.available?null:"Shown when the diagram has two or more phases"}closePhasePopover(){return this.phaseIndex.closePopover()}syncPhaseIndex(){let e=this.canvasEl.getBoundingClientRect().height,n=!(this.panelWidth>0)||this.panelWidth>=1e3,o=!(e>0)||e>=350;this.phaseIndex.setMode({roomy:n&&o,folded:this.indexFolded,hidden:this.indexHidden,suppressed:this.overview.open})}indexCovered(){let e=this.canvasEl.getBoundingClientRect(),n=this.viewport.size();return this.phaseIndex.coveredRect(n.w,n.h,e.width>0?e.left:0,e.width>0?e.top:0)}renderPhaseIndex(){if(!this.index||!this.frameData)return;let e=this.frameData.lanes.map(o=>({id:o.id,label:o.label,index:this.index.phaseIndexOf(o.id),steps:o.nodeCount,counts:this.index.laneCounts(o.id,i=>this.host.keep(i)),rect:{x:o.x,y:o.y,w:o.w,h:o.h}}));this.phaseIndex.setRows(e),this.syncPhaseIndex();let n=this.viewport.size();this.phaseIndex.setInView(this.viewport.vp,n.w,n.h)}get overviewOpen(){return this.overview.open}get overviewElement(){return this.overview.root}overviewLayout(){return this.overview.layout}openOverview(){if(!this.index||!this.frameData||this.overview.open||!this.index.lanes.length)return!1;let e=this.canvasEl.ownerDocument,n=e?e.activeElement:null;this.overviewReturn=n&&n!==e.body?n:null,this.tooltip.hide();let o=Un(this.index,l=>this.host.keep(l)),i=this.viewport.size(),s=this.phaseIndex.currentPhase(),r=Math.max(0,o.phases.findIndex(l=>l.id===s));this.overview.show(o,i.w,i.h,r),this.syncPhaseIndex();let a=o.phases.length;return this.host.announce("Phase overview: "+a+(a===1?" phase":" phases")+", "+o.totalSteps+(o.totalSteps===1?" step":" steps")+". Arrow keys move between phases, Enter goes to one, Escape goes back."),!0}closeOverview(e){if(!this.overview.open)return!1;let n=this.canvasEl.ownerDocument,o=!!n&&!!n.activeElement&&this.overview.root.contains(n.activeElement);this.overview.hide(),this.syncPhaseIndex();let i=this.overviewReturn;return this.overviewReturn=null,e?(this.host.announce("Back to the diagram."),o&&this.focusSafely(i&&i.isConnected&&!this.overview.root.contains(i)?i:this.canvasEl)):o&&this.focusSafely(this.canvasEl),!0}toggleOverview(){this.overview.open?this.closeOverview(!0):this.openOverview()}goToPhase(e,n={}){if(!this.index||!this.frameData)return;let o=this.frameData.lanes.findIndex(l=>l.id===e);if(o<0)return;let i=this.closeOverview(!1),s=this.viewport.size(),r=nl(this.frameData,o,s.w,s.h);r&&this.viewport.animateTo({x:r.x,y:r.y,zoom:r.zoom}),this.arrowLane=e;let a=this.frameData.lanes[o];this.host.announce("Phase "+(this.index.phaseIndexOf(e)+1)+": "+a.label+", "+a.nodeCount+(a.nodeCount===1?" step":" steps")+"."),(n.focusCanvas||i)&&this.focusSafely(this.canvasEl)}takeArrowLane(){let e=this.arrowLane;return this.arrowLane=null,e}clearArrowLane(){this.arrowLane=null}focusSafely(e){try{e.focus()}catch{}}setStale(e){this.staleFiles=new Map(e.map(n=>[n.path,n.reason]))}nodeElement(e){return this.nodeEls.get(e)}focusTarget(e){let n=null;if(e&&e.kind==="node"&&this.index){let o=this.nodeEls.get(this.index.visibleRepresentative(e.id,this.collapsedSet));n=o&&o.classList.contains("mlv-group")?o.querySelector(".mlv-group__header"):o}else e&&e.kind==="edge"&&(n=this.edgeEls.get(e.id)?.querySelector(".mlv-edge__hit"));this.focusSafely(n||this.canvasEl)}relayout(){this.index&&(this.frameData=_a(this.index,this.collapsedSet),this.routes=Ta(this.index,this.frameData,this.collapsedSet),this.labelPlan=cr(this.frameData,this.routes),this.viewport.setContent(this.frameData.width,this.frameData.height,this.frameData),this.render())}planInputs(){return!this.index||!this.frameData?null:{index:this.index,frame:this.frameData,routes:this.routes,labels:this.labelPlan?this.labelPlan.placements:null,mountSerial:this.mountSerial,keep:e=>this.host.keep(e),staleFiles:this.staleFiles}}scenePlan(){let e=this.planInputs();return e?jn(e):null}render(){let e=this.planInputs();if(!e)return;let n=this.nodeWiring(),o=this.edgeWiring(),i=Ua({world:this.worldEl,lanes:this.lanesLayer,edgesSvg:this.edgesSvg,bundleGroup:this.bundleGroup,edgeGroup:this.edgeGroup,connectors:this.connectorLayer,nodes:this.nodesLayer},{...e,wireNode:(s,r,a)=>Bl(s,r,a,n),wireEdge:(s,r)=>Dl(s,r,o)});this.nodeEls=i.nodeEls,this.edgeEls=i.edgeEls,this.markerPoints=new Map;for(let s of i.plan.edges)s.severity&&this.markerPoints.set(s.route.id,ei(s));if(this.bundles.adopt(i.bundleEls,i.plan.bundles.map(s=>s.bundle)),this.syncBundles(),this.emphasis.resetHover(),this.edgeHover.reset(),this.flow.clear(),this.renderPhaseIndex(),this.renderEmptyState(),this.overview.open&&this.index){let s=this.viewport.size();this.overview.redraw(Un(this.index,r=>this.host.keep(r)),s.w,s.h)}}syncShortCanvas(){if(this.syncPhaseIndex(),this.overview.open&&this.index){let e=this.viewport.size();this.overview.redraw(Un(this.index,n=>this.host.keep(n)),e.w,e.h)}}handleResize(){let e=this.host.keptTarget(),n=e?this.targetRect(e):null,o=this.lastArea,i=!!(n&&o&&this.viewport.isVisible(n,o));n&&i?(this.viewport.acceptResize(),this.viewport.apply(),this.viewport.isVisible(n)||this.viewport.revealRect(n)):this.viewport.onResize(),this.syncShortCanvas()}afterSheetToggle(){this.viewport.acceptResize(),this.handleResize(),this.lastArea=this.viewport.visibleArea()}afterChromeChange(){this.viewport.refitIfFitted(),this.syncShortCanvas()}renderEmptyState(){let e=this.stateHost.querySelector(".mlv-state--empty");if(e&&this.stateHost.removeChild(e),!this.index)return;let n=this.index.graph;n.nodes.length===0?this.stateHost.appendChild(Ll(n)):this.frameData&&this.frameData.boxes.size===0&&this.stateHost.appendChild(Il(()=>this.host.clearFilters()))}nodeWiring(){return{isGroup:e=>!!this.index&&this.index.isGroup(e),toggleCollapse:e=>this.toggleCollapse(e),hoverIntent:e=>this.emphasis.hoverIntent(e),activateNode:(e,n)=>this.host.activateNode(e,n),openNode:(e,n)=>this.host.openNode(e,n),addDisposer:e=>this.disposers.push(e)}}edgeWiring(){return{activateEdge:(e,n)=>this.host.activateEdge(e,n),openEdge:(e,n)=>this.host.openEdge(e,n),enterEdge:e=>this.edgeHover.enter(e),leaveEdge:e=>this.edgeHover.leave(e),syncBundles:()=>this.syncBundles(),pulse:e=>this.flow.pulse(e),stopFlow:()=>this.flow.stop()}}incidentRoutes(e){return this.flow.incidentRoutes(e)}focusEdge(e){return this.flow.focusEdge(e)}blurFocusedEdge(){return this.flow.blurFocusedEdge()}edgeAnnouncement(e){return this.flow.edgeAnnouncement(e)}applySelection(e){this.emphasis.applySelection(e)}hoverIntent(e){this.emphasis.hoverIntent(e)}setHover(e){this.emphasis.setHover(e)}get isFocusLocked(){return this.emphasis.isFocusLocked}toggleFocusMode(e){this.emphasis.toggleFocusMode(e)}syncBundles(){this.bundles.sync(this.edgeEls)}toggleCollapse(e){if(!this.frameData)return;let n=this.frameData.boxes.get(e),o=this.viewport.vp.zoom,i=n?{x:n.x*o+this.viewport.vp.x,y:n.y*o+this.viewport.vp.y}:null;this.collapsedSet.has(e)?this.collapsedSet.delete(e):this.collapsedSet.add(e),this.relayout();let s=this.frameData.boxes.get(e);s&&i&&this.viewport.set({x:i.x-s.x*this.viewport.vp.zoom,y:i.y-s.y*this.viewport.vp.zoom});let r=this.index?this.index.nodeById.get(e):null,a=this.index?this.index.descendantCount(e):0;this.host.afterCollapse(),this.host.announce("Group "+(r?r.label:e)+(this.collapsedSet.has(e)?" collapsed, "+a+(a===1?" step":" steps")+" hidden.":" expanded."))}expandAncestors(e){if(!this.index)return!1;let n=!1;for(let o of this.index.ancestors(e))this.collapsedSet.delete(o)&&(n=!0);return n&&this.relayout(),n}centerOnNode(e,n){if(!this.index||!this.frameData)return;let o=this.index.visibleRepresentative(e,this.collapsedSet),i=this.frameData.boxes.get(o);if(!i||(this.viewport.centerOn(i),!n))return;let s=this.nodeEls.get(o);s&&(s.classList.add("is-pulse"),setTimeout(()=>s.classList.remove("is-pulse"),700))}revealNode(e,n){if(!this.index||!this.frameData)return;let o=this.frameData.boxes.get(this.index.visibleRepresentative(e,this.collapsedSet));if(o){if(this.viewport.vp.zoom>=Be){this.centerOnNode(e,n);return}this.viewport.centerOn(o,Math.max(this.viewport.vp.zoom,this.readableZoomFor(o))),n&&this.pulseNode(this.index.visibleRepresentative(e,this.collapsedSet))}}keepInView(e){let n=this.targetRect(e);n&&!this.viewport.isVisible(n)&&this.viewport.centerOn(n)}refresh(e){let n=this.canvasEl.ownerDocument,o=n?n.activeElement:null,i=!!o&&o!==this.canvasEl&&this.canvasEl.contains(o)&&typeof o.closest=="function",s=i&&o.closest("[data-node-id]")?.getAttribute("data-node-id")||null,r=i&&!s&&o.classList.contains("mlv-edge__hit")&&o.closest("[data-edge-id]")?.getAttribute("data-edge-id")||null;this.render(),this.applySelection(e);let a=null;if(s){let l=this.nodeEls.get(s);a=l&&l.classList.contains("mlv-group")?l.querySelector(".mlv-group__header"):l}else r&&(a=this.edgeEls.get(r)?.querySelector(".mlv-edge__hit"));if(a)try{a.focus()}catch{}}frameIssue(e){if(!this.index||!this.frameData)return;let n=this.index.issueById.get(e);if(!n)return;let o=[],i=[];for(let d of n.nodeIds){let c=this.targetRect({kind:"node",id:d});c&&o.push(c)}for(let d of n.edgeIds){let c=this.targetRect({kind:"edge",id:d});c&&i.push(c)}let s=o.concat(i);if(!s.length)return;let r=d=>{let c=Math.min(...d.map(h=>h.x)),u=Math.min(...d.map(h=>h.y));return{x:c,y:u,w:Math.max(...d.map(h=>h.x+h.w))-c,h:Math.max(...d.map(h=>h.y+h.h))-u}},a=r(s),l=!o.length||!i.length||this.viewport.fitsAt(a,zt)?a:r(o);this.viewport.frameRect(l,s[0],De),n.nodeIds[0]&&this.pulseNode(this.index.visibleRepresentative(n.nodeIds[0],this.collapsedSet))}frameEdge(e){if(!this.index||!this.frameData)return;let n=this.index.edgeById.get(e),o=this.targetRect({kind:"edge",id:e});if(!n||!o)return;let i=g=>this.frameData.boxes.get(this.index.visibleRepresentative(g,this.collapsedSet)),s=i(n.source),r=i(n.target);this.viewport.frameRect(o,s||o,De);let a=this.viewport.covered();if(!r||!a||this.viewport.isVisible(r))return;let l=48,{w:d,h:c}=this.viewport.visibleArea(),u=Math.min((d-l)/Math.max(1,o.w),(c-a.h-l)/Math.max(1,o.h)),h=Math.min((d-a.w-l)/Math.max(1,o.w),(c-l)/Math.max(1,o.h)),p=Math.min(this.viewport.vp.zoom,Math.max(u,h));p>=zt&&this.viewport.centerOn(o,p)}revealEdge(e){let n=this.targetRect({kind:"edge",id:e});n&&(this.viewport.vp.zoomn.classList.remove("is-pulse"),700))}zoomStep(e){let n=this.viewport.size();this.viewport.zoomAt(e>0?1.2:1/1.2,n.w/2,n.h/2)}fit(){this.viewport.fit()}fitWhole(){this.viewport.fitWhole()}zoomToNode(e){if(!this.index||!this.frameData)return;let n=this.frameData.boxes.get(this.index.visibleRepresentative(e,this.collapsedSet));n&&this.viewport.zoomToBox(n)}nextSelection(e,n){let o=this.index,i=this.frameData;if(!o||!i)return null;let s=d=>o.nodeById.has(d)?o.visibleRepresentative(d,this.collapsedSet):null,r=d=>{let c=s(d);return c&&i.boxes.has(c)?Ra(o,i,c,n):void 0},a=d=>{let c=o.edgeById.get(d);return c&&Oa(i,s(c.source),s(c.target),n)||void 0},l;if(e&&e.kind==="node")l=r(e.id);else if(e&&e.kind==="edge")l=a(e.id);else if(e&&e.kind==="issue"){let d=o.issueById.get(e.id);d&&d.nodeIds.length&&(l=r(d.nodeIds[0])),l===void 0&&d&&d.edgeIds.length&&(l=a(d.edgeIds[0]))}return l===void 0&&(l=Ci(i)),l?{id:l.id,visible:this.viewport.isVisible(l)}:null}labelOf(e){let n=this.nodeEls.get(e);return n?n.getAttribute("aria-label"):null}toast(e){this.toasts.show(e)}destroy(){this.phaseIndex.destroy(),this.overview.destroy(),this.emphasis.destroy(),this.edgeHover.destroy();for(let e of this.disposers)try{e()}catch{}this.disposers=[],this.flow.destroy(),this.toasts.destroy()}};var jl='button, [role="button"], a[href], select';function Gl(t){return typeof t.getClientRects=="function"&&t.getClientRects().length>0}var xt=class{constructor(e,n="input, textarea"){this.activeIndex=0;this.activeEl=null;this.disposers=[];this.container=e,this.ownStopSelector=n,this.disposers.push(_(e,"keydown",o=>this.onKey(o))),this.disposers.push(_(e,"focusin",o=>{let i=o.target;if(!i||typeof i.closest!="function")return;let r=this.items().indexOf(i);r>=0&&this.setActive(r,!1)})),this.sync()}items(){let e=this.container.querySelectorAll(jl),n=Gl(this.container),o=[];for(let i=0;i=0?s:r>=0?r:this.activeEl?Math.min(this.activeIndex,e.length-1):0;this.activeIndex=a,(s>=0||this.activeEl)&&(this.activeEl=e[a]);for(let l=0;l"u")return!1;let t=navigator,e=t.userAgentData&&t.userAgentData.platform||t.platform||t.userAgent||"";return/mac/i.test(e)}catch{return!1}}function ot(t,e=Xi()){return e?t.replace(/^Mod\+/,"⌘").replace(/^Ctrl\+/,"⌃").replace(/^Alt\+/,"⌥").replace(/^Shift\+/,"⇧"):t.replace(/^Mod\+/,"Ctrl+")}var Wl=[{id:"search",label:"Search steps and findings",icon:"search",keys:"Mod+F",folded:!0},{id:"about",label:"About this revision",icon:"info",folded:!0},{id:"exceptions",label:"Not observed",icon:"notobserved",check:!0,folded:!0},{id:"review",label:"Review the claims",icon:"review",keys:"R",group:!0},{id:"overview",label:"Phase overview",icon:"phases",keys:"Shift+0"},{id:"legend",label:"Legend",icon:"legend",keys:"L",check:!0,group:!0},{id:"flow",label:"Connection flow animation",icon:"flow",keys:"A",check:!0},{id:"phaseindex",label:"Phase index",icon:"phaseindex",check:!0},{id:"rail",label:"Side panel",icon:"rail",keys:"B",check:!0},{id:"fit",label:"Fit the whole diagram",icon:"fit",group:!0},{id:"zoomsel",label:"Zoom to the selection",icon:"target",keys:"Z"},{id:"svg",label:"Export SVG…",icon:"image",group:!0},{id:"png",label:"Export PNG…",icon:"image"},{id:"copy-svg",label:"Copy SVG",icon:"copy"},{id:"shortcuts",label:"Keyboard shortcuts",icon:"keyboard",keys:"?",group:!0}],zl=["svg","png","copy-svg"],$m=0,io=class{constructor(e,n=()=>{}){this.items=new Map;this.openState=!1;this.disposers=[];this.onPick=e,this.beforeOpen=n;let o="mlv-more"+ ++$m;this.button=he("mlv-btn mlv-btn--icon mlv-btn--more","More actions"),this.button.title="More: review, phase overview, legend, fit, export, shortcuts",this.button.appendChild(B("more",16)),this.button.setAttribute("aria-haspopup","menu"),this.button.setAttribute("aria-expanded","false"),this.button.setAttribute("aria-controls",o),_(this.button,"click",s=>{s.preventDefault(),s.stopPropagation(),this.setOpen(!this.openState)}),_(this.button,"keydown",s=>{if(s.key==="Escape"){if(!this.openState)return;s.preventDefault(),s.stopPropagation(),this.setOpen(!1);return}s.key!=="ArrowDown"&&s.key!=="ArrowUp"||(s.preventDefault(),s.stopPropagation(),this.setOpen(!0),this.focusItem(s.key==="ArrowDown"?0:this.shown().length-1))}),this.panel=f("div","mlv-moremenu"),this.panel.id=o,this.panel.setAttribute("role","menu"),this.panel.setAttribute("aria-label","More actions"),this.panel.hidden=!0;for(let s of Wl){if(s.group){let a=m(this.panel,f("div","mlv-moremenu__sep"));a.setAttribute("role","separator"),a.setAttribute("data-sep-before",s.id)}let r=f("button","mlv-moremenu__item");r.type="button",r.tabIndex=-1,r.setAttribute("role",s.check?"menuitemcheckbox":"menuitem"),r.setAttribute("data-more-item",s.id),zl.indexOf(s.id)>=0&&r.setAttribute("data-export-action",s.id),s.folded&&r.setAttribute("data-folded","1"),r.appendChild(B(s.icon,14)),m(r,f("span","mlv-moremenu__label",s.label)),s.id==="phaseindex"&&(m(r,f("span","mlv-moremenu__note")).hidden=!0),s.keys&&m(r,f("kbd","mlv-moremenu__keys",ot(s.keys))).setAttribute("aria-hidden","true"),_(r,"click",a=>{if(a.preventDefault(),r.disabled)return;let l=a.detail===0;this.setOpen(!1),this.onPick(s.id,l)}),this.panel.appendChild(r),this.items.set(s.id,r)}_(this.panel,"keydown",s=>this.onKey(s));let i=typeof document>"u"?null:document;i&&(this.disposers.push(_(i,"pointerdown",s=>{if(!this.openState)return;let r=s.target;r&&(this.panel.contains(r)||this.button.contains(r))||this.setOpen(!1)})),this.disposers.push(_(i,"keydown",s=>{if(!this.openState||s.key!=="Escape")return;s.preventDefault(),s.stopPropagation();let r=i.activeElement,a=!!r&&(this.panel.contains(r)||this.button.contains(r));this.setOpen(!1),a&&this.focusButton()},!0)))}get open(){return this.openState}update(e){for(let[o,i]of this.items){let s=Wl.find(a=>a.id===o),r=!s.folded||(o==="search"?e.folded.search:o==="about"?e.folded.about:e.folded.exceptions);if(i.hidden=!r,o==="about"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.aboutLabel),i.title="Show About: the request, coverage, limitations and provenance"}if(o==="exceptions"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.exceptionsLabel),i.title=e.exceptionsOn?"The observed claims are faded; choose to show them again":"Fade the observed claims so these stand out"}if(o==="review"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.walking?"End the review":"Review the claims"),i.title=e.walking?"End the review walk (Escape); it remembers its place for this revision":"Walk the claims one by one, each opened and highlighted in the editor beside; focus stays here",i.disabled=!e.canExport}if(o==="rail"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.railMode==="sheet"?"Bottom panel":"Side panel"),i.title=e.railMode==="sheet"?"Open or collapse the bottom panel":"Show or hide the side panel"}if(o==="overview"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.overviewOpen?"Close the phase overview":"Phase overview"),i.title=e.overviewOpen?"Back to the diagram where you were (Escape)":"Every phase as a block of its step titles, with the connections between phases; choose one to go there",i.disabled=!e.canExport}if(s.check){let a=o==="legend"?e.legendOpen:o==="flow"?e.flowOn:o==="phaseindex"?e.phaseIndexShown&&!e.phaseIndexUnavailable:o==="exceptions"?e.exceptionsOn:e.railOpen;i.setAttribute("aria-checked",a?"true":"false")}if(o==="phaseindex"){let a=i.querySelector(".mlv-moremenu__note");a&&(a.textContent=e.phaseIndexUnavailable||"",a.hidden=!e.phaseIndexUnavailable),i.disabled=!!e.phaseIndexUnavailable,i.title=e.phaseIndexUnavailable?"Phase index: "+e.phaseIndexUnavailable.charAt(0).toLowerCase()+e.phaseIndexUnavailable.slice(1):"Show or hide the phase index: each phase with its findings and steps, the phases in view marked"}o==="zoomsel"&&(i.disabled=!e.hasSelection),zl.indexOf(o)>=0&&(i.disabled=!e.canExport),i.disabled?i.setAttribute("aria-disabled","true"):i.removeAttribute("aria-disabled")}let n=this.shown()[0];for(let o of Array.from(this.panel.querySelectorAll(".mlv-moremenu__sep")))o.hidden=!!n&&o.getAttribute("data-sep-before")===n.getAttribute("data-more-item")}setOpen(e){if(this.openState!==e)if(this.openState=e,this.panel.hidden=!e,this.button.setAttribute("aria-expanded",e?"true":"false"),e){try{this.beforeOpen()}catch{}this.position(),this.focusItem(0)}else{let n=this.panel.ownerDocument?this.panel.ownerDocument.activeElement:null;n&&this.panel.contains(n)&&this.focusButton()}}shown(){return Array.from(this.items.values()).filter(e=>!e.hidden&&!e.disabled)}position(){let e=null;try{e=this.button.getBoundingClientRect()}catch{e=null}e&&(this.panel.style.position="fixed",this.panel.style.top=Math.round(e.bottom+4)+"px",this.panel.style.left="auto",this.panel.style.right=Math.max(8,Math.round(Jm()-e.right))+"px")}focusItem(e){let n=this.shown();if(!n.length)return;let o=(e%n.length+n.length)%n.length;try{n[o].focus()}catch{}}focusButton(){try{this.button.focus()}catch{}}onKey(e){if(e.key==="Escape"){e.preventDefault(),e.stopPropagation(),this.setOpen(!1);return}if(e.key==="Tab"){this.setOpen(!1);return}let n=this.shown(),o=n.indexOf(e.target);if(!(o<0)){if(e.key==="ArrowDown")this.focusItem(o+1);else if(e.key==="ArrowUp")this.focusItem(o-1);else if(e.key==="Home")this.focusItem(0);else if(e.key==="End")this.focusItem(n.length-1);else return;e.preventDefault(),e.stopPropagation()}}destroy(){for(let e of this.disposers)try{e()}catch{}this.disposers=[]}};function Jm(){try{if(typeof window<"u"&&window.innerWidth)return window.innerWidth}catch{}return 1280}var Yi={mid:620,wide:1e3,full:1200};function eg(t){return t>0?tt+" "+(t===1?e:n);function Vl(t){let e=t.steps+t.connections+t.findings,n=[];return t.steps&&n.push(We(t.steps,"step","steps")),t.connections&&n.push(We(t.connections,"connection","connections")),t.findings&&n.push(We(t.findings,"finding","findings")),e+" not observed ("+n.join(", ")+")"}function tg(t){let e=t.coverage.limitations.length;return"Coverage: "+t.coverage.status+(e?" · "+We(e,"limitation","limitations"):"")}var ql=4;function ng(t){let e=t.steps+t.connections+t.findings;return e+(e===1?" claim":" claims")+" not observed"}var og=0,so=class{constructor(e,n){this.sevButtons=new Map;this.roving=null;this.layout="full";this.width=0;this.fitLevel=0;this.hasDocument=!1;this.hasExceptions=!1;this.sevShown=new Set;this.searchOpen=!1;this.lastAboutLabel="About this revision";this.state=null;this.cb=e;let o="mlv"+ ++og;this.header=f("header","mlv-header"),this.header.setAttribute("data-layout",this.layout),this.title=m(this.header,f("h1","mlv-header__title","")),this.toolbar=m(this.header,f("div","mlv-header__bar")),this.toolbar.setAttribute("role","toolbar"),this.toolbar.setAttribute("aria-label","Diagram controls"),this.toolbar.setAttribute("aria-orientation","horizontal"),this.provenance=f("button","mlv-chip mlv-header__prov"),this.provenance.type="button",m(this.provenance,f("span","mlv-header__dot")).setAttribute("aria-hidden","true"),this.provHost=m(this.provenance,f("span","mlv-header__host","")),this.provRev=m(this.provenance,f("span","mlv-header__rev","")),_(this.provenance,"click",l=>e.onAbout(l.detail===0)),this.toolbar.appendChild(this.provenance),m(this.toolbar,f("span","mlv-header__spacer")),this.search=m(this.toolbar,f("div","mlv-search")),m(this.search,B("search",14)).setAttribute("class","mlv-uicon mlv-search__glyph");let s=m(this.search,f("label","mlv-sr","Search steps, findings, IDs, or cited text"));s.htmlFor=o+"-search-input",this.searchInput=m(this.search,f("input","mlv-input")),this.searchInput.id=o+"-search-input",this.searchInput.type="search",this.searchInput.placeholder="Search steps and findings",this.searchInput.setAttribute("role","combobox"),this.searchInput.setAttribute("aria-expanded","false"),this.searchInput.setAttribute("aria-controls",o+"-search-results"),this.searchInput.autocomplete="off",this.results=m(this.search,f("ul","mlv-search__results")),this.results.id=o+"-search-results",this.results.setAttribute("role","listbox"),this.results.setAttribute("aria-label","Search results"),this.results.hidden=!0,_(this.searchInput,"input",()=>e.onQuery(this.searchInput.value)),_(this.searchInput,"keydown",l=>e.onSearchKey(l)),_(this.searchInput,"blur",()=>{this.searchInput.value||this.setSearchOpen(!1)}),this.searchBtn=he("mlv-btn mlv-btn--icon mlv-header__searchbtn","Search ("+ot("Mod+F")+")"),this.searchBtn.appendChild(B("search",16)),this.searchBtn.setAttribute("aria-controls",this.searchInput.id),_(this.searchBtn,"click",()=>e.onSearchOpen()),this.toolbar.appendChild(this.searchBtn);for(let l of ne){let d=f("button","mlv-chip mlv-chip--btn mlv-chip--sev");d.type="button",d.setAttribute("aria-pressed","true"),d.setAttribute("data-severity",l),d.appendChild(Z(l,14,"")),m(d,f("span","mlv-chip__count","0")),_(d,"click",()=>e.onSeverity(l)),this.sevButtons.set(l,d),this.toolbar.appendChild(d)}this.sevUnit=m(this.toolbar,f("span","mlv-header__unit","findings")),this.sevUnit.setAttribute("aria-hidden","true"),this.sevUnit.hidden=!0,this.exceptionsBtn=f("button","mlv-chip mlv-chip--btn mlv-chip--exceptions"),this.exceptionsBtn.type="button",this.exceptionsBtn.setAttribute("aria-pressed","false"),this.exceptionsBtn.hidden=!0,_(this.exceptionsBtn,"click",()=>e.onToggleExceptions(this.exceptionsBtn.getAttribute("aria-pressed")!=="true")),this.toolbar.appendChild(this.exceptionsBtn),this.reviewBtn=f("button","mlv-btn mlv-header__review"),this.reviewBtn.type="button",this.reviewBtn.setAttribute("aria-pressed","false"),this.reviewBtn.appendChild(B("review",14)),m(this.reviewBtn,f("span","mlv-header__reviewlabel","Review")),this.reviewBtn.hidden=!0,_(this.reviewBtn,"click",()=>e.onReview()),this.toolbar.appendChild(this.reviewBtn),this.more=new io((l,d)=>this.pick(l,d),()=>e.onMenuOpen()),this.toolbar.appendChild(this.more.button),this.refineSlot=m(this.toolbar,f("span","mlv-header__refine")),this.status=f("footer","mlv-status"),this.status.setAttribute("data-layout",this.layout),this.counts=m(this.status,f("span","mlv-status__counts","")),this.coverage=f("button","mlv-status__coverage"),this.coverage.type="button",this.coverage.hidden=!0,_(this.coverage,"click",l=>e.onAbout(l.detail===0)),this.status.appendChild(this.coverage),m(this.status,f("span","mlv-status__spacer")),this.fresh=m(this.status,f("span","mlv-status__freshness"));let r=he("mlv-btn mlv-btn--icon mlv-zoom__btn","Zoom out");r.appendChild(B("minus",12)),_(r,"click",()=>e.onZoom(-1));let a=he("mlv-btn mlv-btn--icon mlv-zoom__btn","Zoom in");a.appendChild(B("plus",12)),_(a,"click",()=>e.onZoom(1)),n.insertBefore(r,n.firstChild),n.appendChild(a),this.status.appendChild(n),this.roving=new xt(this.toolbar)}destroy(){this.roving&&this.roving.destroy(),this.roving=null,this.more.destroy()}syncRoving(){this.fitRow()}setWidth(e){let n=eg(e),o=n!==this.layout;return o&&(this.layout=n,this.header.setAttribute("data-layout",n),this.status.setAttribute("data-layout",n),this.syncSearch()),(o||e!==this.width)&&(this.width=e,this.fitRow()),n}get headerFit(){return this.fitLevel}get headerLayout(){return this.layout}setSearchOpen(e){this.searchOpen=e,this.syncSearch(),this.fitRow()}syncSearch(){let n=!(this.layout==="mid"||this.layout==="narrow")||this.searchOpen||!!this.searchInput.value;this.header.setAttribute("data-search",n?"open":"closed"),this.searchBtn.setAttribute("aria-expanded",n?"true":"false"),this.syncControls()}syncControls(){this.provenance.hidden=!this.hasDocument||this.layout==="narrow"||this.fitLevel>=1;let e=(this.layout==="mid"||this.layout==="narrow")&&this.header.getAttribute("data-search")!=="open";this.searchBtn.hidden=!(this.layout==="mid"&&e),this.exceptionsBtn.hidden=!this.hasExceptions||this.fitLevel>=2,this.reviewBtn.hidden=!this.hasDocument||this.layout==="narrow"||this.fitLevel>=1;for(let[n,o]of this.sevButtons)o.hidden=!this.sevShown.has(n)||this.fitLevel>=4;this.sevUnit.hidden=this.sevShown.size===0||this.fitLevel>=4,this.header.setAttribute("data-fit",String(this.fitLevel))}fitRow(){this.fitLevel=0,this.syncControls();let e=this.header.clientWidth,n=this.header.getAttribute("data-search")==="open"&&(this.layout==="mid"||this.layout==="narrow")?ql:ql-1;if(e>0)for(;this.fitLevelthis.header.clientWidth;)this.fitLevel++,this.syncControls();this.updateMenu(),this.roving&&this.roving.sync()}updateMenu(){let e=this.state;if(!e)return;let n=e.notObserved,o=this.hasExceptions&&this.fitLevel>=2;this.more.update({folded:{search:this.layout==="narrow",about:this.layout==="narrow"||this.fitLevel>=1,exceptions:o},aboutLabel:this.lastAboutLabel,exceptionsLabel:this.hasExceptions?Vl(n):"",exceptionsOn:e.exceptionsOn,legendOpen:e.legendOpen,flowOn:e.flowOn,phaseIndexShown:e.phaseIndexShown,phaseIndexUnavailable:e.phaseIndexUnavailable,overviewOpen:e.overviewOpen,railOpen:e.railOpen,walking:e.walking,railMode:e.railMode,hasSelection:e.hasSelection,canExport:!!e.graph})}pick(e,n){let o=this.cb;e==="search"?o.onSearchOpen():e==="about"?o.onAbout(n):e==="exceptions"?o.onToggleExceptions(!(this.state&&this.state.exceptionsOn)):e==="legend"?o.onToggleLegend(!(this.state&&this.state.legendOpen)):e==="flow"?o.onToggleFlow(!(this.state&&this.state.flowOn)):e==="phaseindex"?o.onTogglePhaseIndex(!(this.state&&this.state.phaseIndexShown)):e==="overview"?o.onOverview():e==="rail"?o.onToggleRail():e==="fit"?o.onFitWhole():e==="zoomsel"?o.onZoomToSelection():e==="shortcuts"?o.onShortcuts():e==="review"?o.onReview():o.onExport(e)}update(e){this.state=e;let n=e.document,o=e.graph;this.renderHeader(e,n);let i=e.notObserved,s=i.steps+i.connections+i.findings;if(this.hasExceptions=!!o&&s>0,s>0){let a=Vl(i),l=a.indexOf(" (");D(this.exceptionsBtn),this.exceptionsBtn.appendChild(B("notobserved",14)),this.layout==="full"&&l>0?(m(this.exceptionsBtn,f("span","mlv-chip__lead",a.slice(0,l))),m(this.exceptionsBtn,f("span","mlv-chip__detail",a.slice(l)))):m(this.exceptionsBtn,f("span","mlv-chip__lead",ng(i))),this.exceptionsBtn.setAttribute("aria-label",a),this.exceptionsBtn.setAttribute("aria-pressed",e.exceptionsOn?"true":"false"),this.exceptionsBtn.title=a+(e.exceptionsOn?". The observed claims are faded; press to show them again.":". Press to fade the observed claims so these stand out.")}for(let a of ne){let l=this.sevButtons.get(a),d=e.filters.severities.indexOf(a)>=0,c=e.visibleCounts[a];l.setAttribute("aria-pressed",d?"true":"false");let u=l.querySelector(".mlv-chip__count");u&&(u.textContent=String(c)),o&&c>0?this.sevShown.add(a):this.sevShown.delete(a),l.title=We(c,a+" finding",a+" findings")+(d?". Press to hide them.":", hidden. Press to show them."),l.setAttribute("aria-label",l.title)}let r=ne.filter(a=>this.sevShown.has(a));this.sevUnit.textContent=r.length===1&&e.visibleCounts[r[0]]===1?"finding":"findings",this.reviewBtn.setAttribute("aria-pressed",e.walking?"true":"false"),this.reviewBtn.title=e.walking?"End the review walk (Escape). It remembers its place for this revision.":"Review the claims one by one (R): each is selected here and its cited lines are opened and highlighted in the editor beside. Focus stays here.",this.reviewBtn.setAttribute("aria-label","Review the claims"),this.syncSearch(),this.renderStatus(e),this.fitRow()}renderHeader(e,n){if(!n){this.title.textContent="",this.title.removeAttribute("title"),this.hasDocument=!1;return}this.title.textContent=n.title,this.title.title=n.title,this.hasDocument=!0;let o=n.producer.host,i=n.producer.model?" ("+n.producer.model+")":"";this.provHost.textContent=o+" · ",this.provRev.textContent=n.revision.id;let s=!!e.freshness&&e.freshness.state==="stale";this.provenance.setAttribute("data-stale",s?"true":"false");let r=n.verification?"Published "+n.verification.publishedAt+" with hashes of "+We(Object.keys(n.verification.files||{}).length,"file","files")+".":"Published without source hashes.",a=s&&e.freshness?" "+e.freshness.text+".":"";this.provenance.title="Revision "+n.revision.id+" by "+o+i+". "+r+a+" Show About: the request, coverage and provenance.",this.provenance.setAttribute("aria-label","Revision "+n.revision.id+", "+o+"."+a+" Show About this revision"),this.lastAboutLabel="About revision "+n.revision.id+" · "+o}renderStatus(e){let n=e.graph,o=e.document;if(!n){this.counts.textContent="Waiting for a workflow…",this.coverage.hidden=!0,D(this.fresh);return}if(this.counts.textContent=We(n.nodes.length,"step","steps")+" · "+We(n.edges.length,"connection","connections"),o){this.coverage.hidden=!1;let s=tg(o);this.coverage.textContent=this.layout==="narrow"?s.replace(/^Coverage: /,""):s,this.coverage.setAttribute("aria-label",s);let a=o.coverage.status==="scoped"?'"scoped": the assistant lists no remaining work within the stated scope.':'"partial": the assistant lists work that remains.';this.coverage.title="Coverage as the assistant recorded it. "+a+" Show About, where the limitations are listed."}else this.coverage.hidden=!0;D(this.fresh);let i=e.freshness;if(this.fresh.setAttribute("data-stale",i&&i.state==="stale"?"true":"false"),i&&i.state==="stale"){let s=m(this.fresh,f("span","mlv-status__fresh is-warn"));s.appendChild(B("warning",12)),m(s,f("span","",i.text)),s.title=i.title,s.setAttribute("data-freshness","stale")}if(e.checking)m(this.fresh,f("span","mlv-status__fresh is-checking","Checking source freshness…")).setAttribute("data-freshness","checking");else if(i&&i.state!=="stale"){let s=m(this.fresh,f("span","mlv-status__fresh is-muted",i.text));s.title=i.title,s.setAttribute("data-freshness",i.state)}}};function Kt(t){let e=yt(t.basis,"finding");return e?(e.removeAttribute("aria-hidden"),e.classList.add("mlv-basis-tag--rail"),e):null}function ro(t,e){if(!e)return[];let n=[];for(let o of[t.loc].concat(t.relatedLocs||[])){let i=o&&o.file?e(o.file):void 0;i&&n.indexOf(i)<0&&n.push(i)}return n}function Et(t){return ae(t)?"cites a file in another folder":t.length===1&&t[0]==="changed"?"cites a changed file":t.indexOf("changed")<0?t.length===1?"cites a missing file":"cites missing files":"cites changed or missing files"}function ao(t,e="div"){if(!(t.fixHint||"").trim())return null;let o=f("div","mlv-insp__fix");return o.setAttribute("data-suggestion",t.id),m(o,f(e,"mlv-insp__fix-label","What to change")),m(o,f("p","mlv-insp__fix-text",t.fixHint)),o}function Ut(t,e,n,o){if(o){t.disabled=!0,t.classList.add("is-stale"),t.setAttribute("data-stale",o);let i=we(e)+": "+me[o]+". Not opened.";t.title=i,t.setAttribute("aria-label",i);return}_(t,"click",i=>{i.stopPropagation(),n(e,i.altKey)}),_(t,"keydown",i=>{i.key!=="Enter"||!i.altKey||(i.preventDefault(),i.stopPropagation(),n(e,!0))})}function Kl(t,e,n){if(D(t),m(t,f("h3","mlv-sr","Findings")),!e.index){m(t,f("div","mlv-empty-note","No workflow loaded yet."));return}let o=e.issues.filter(e.keep);if(!o.length){e.issues.length?t.appendChild(ug(n)):(e.index.graph.nodes||[]).length===0?t.appendChild(cg(e)):t.appendChild(dg(e));return}for(let i of ne){let s=o.filter(l=>ee(l.severity)===i);if(!s.length)continue;let r=m(t,f("section","mlv-rail__section"));r.setAttribute("data-severity-section",i);let a=m(r,f("h4","mlv-rail__heading"));a.appendChild(Z(i,12,"")),m(a,f("span","",i+" · "+s.length+(s.length===1?" finding":" findings"))),r.appendChild(ig(s,i,e,n))}}function ig(t,e,n,o,i){let s=f("ul","mlv-issues");s.setAttribute("role","listbox"),s.setAttribute("aria-label",i||e+" severity findings");for(let r of t)s.appendChild(sg(r,n,o));return lg(s,o),s}function sg(t,e,n){let o=e.selectedIssueId===t.id,i=f("li","mlv-issues__item");i.setAttribute("role","presentation");let s=f("div","mlv-issue");s.setAttribute("role","option"),s.tabIndex=-1,s.setAttribute("data-issue-id",t.id),s.setAttribute("aria-selected",o?"true":"false"),s.setAttribute("aria-label",(t.short?t.short+", ":"")+t.code+" "+t.severity+" severity, "+t.title+(t.loc.file?", "+en(t.loc):"")+$e(t.basis)),o&&s.classList.add("is-selected");let r=ro(t,e.staleReason);r.length&&(s.classList.add("is-stale"),s.setAttribute("data-stale",r.join(" ")),s.setAttribute("aria-label",s.getAttribute("aria-label")+", "+Et(r)+(ae(r)?"":" since publishing"))),s.appendChild(Z(t.severity,14,"")),t.short&&m(s,f("span","mlv-issue__short",t.short));let a=m(s,f("div","mlv-issue__text"));m(a,f("div","mlv-issue__title",t.title));let l=m(a,f("div","mlv-issue__meta"));m(l,f("span","",t.code)),t.loc.file&&l.appendChild(Qe("",t.loc));let d=Kt(t);if(d&&l.appendChild(d),r.length&&l.appendChild(rg(r)),_(s,"click",c=>n.onSelectIssue(t.id,c)),i.appendChild(s),t.loc.file){let c=he("mlv-btn mlv-btn--icon mlv-issue__open","Open "+we(t.loc));c.appendChild(B("open",12)),Ut(c,t.loc,n.onOpen,e.staleReason?e.staleReason(t.loc.file):void 0),i.appendChild(c)}return o&&i.appendChild(ag(t,e,n)),i}function rg(t){let e=f("span","mlv-chip mlv-chip--stale");return e.appendChild(B("warning",11)),m(e,f("span","",Et(t))),e.setAttribute("data-stale",t.join(" ")),e.title=ae(t)?"A file this finding cites is not under the workspace root; it is unchanged in another folder, which the notice above names. Its jump is blocked.":"A file this finding cites no longer matches the published revision. Its jump is blocked.",e}function ag(t,e,n){let o=f("div","mlv-issue__detail");o.setAttribute("data-issue-detail",t.id),t.message&&m(o,f("p","mlv-insp__line",t.message));let i=ao(t);i&&o.appendChild(i);let s=m(o,f("div","mlv-issue__goto"));if(t.loc.file){let r=U("mlv-btn","Go to "+we(t.loc));Ut(r,t.loc,n.onOpen,e.staleReason?e.staleReason(t.loc.file):void 0),s.appendChild(r)}for(let r of t.relatedLocs||[]){let a="Go to "+(r.message||r.role.replace(/_/g," "))+" — "+we(r),l=U("mlv-btn",a);Ut(l,r,n.onOpen,e.staleReason?e.staleReason(r.file):void 0),s.appendChild(l)}return o}function lg(t,e){let n=()=>Array.prototype.slice.call(t.querySelectorAll('[role="option"]')),o=n(),i=o.filter(s=>s.getAttribute("aria-selected")==="true")[0]||o[0]||null;for(let s of o)s.tabIndex=s===i?0:-1;_(t,"keydown",s=>{let r=s.target;if(!r||typeof r.closest!="function")return;let a=r.closest('[role="option"]');if(!a||!t.contains(a))return;let l=n(),d=l.indexOf(a),c=null;if(s.key==="ArrowDown")c=l[Math.min(l.length-1,d+1)];else if(s.key==="ArrowUp")c=l[Math.max(0,d-1)];else if(s.key==="Home")c=l[0];else if(s.key==="End")c=l[l.length-1];else if(s.key==="Enter"){if(s.ctrlKey||s.metaKey||s.shiftKey)return;s.preventDefault();let u=a.getAttribute("data-issue-id");u&&e.onOpenIssue(u,s.altKey);return}else if(s.key===" "){s.preventDefault();let u=a.getAttribute("data-issue-id");u&&e.onSelectIssue(u);return}else return;if(s.preventDefault(),!!c){for(let u of l)u.tabIndex=u===c?0:-1;c.focus()}})}function dg(t){let e=f("div","mlv-empty-note mlv-nofindings");e.setAttribute("role","status"),m(e,f("div","mlv-clean__title","No findings recorded in this revision"));let n=t.index?t.index.graph.authoredCoverage:void 0,o=n?n.status:"unknown",i=n?n.limitations:0;return m(e,f("div","mlv-clean__detail","The assistant recorded no findings. Coverage: "+o+(i?"; "+i+(i===1?" limitation":" limitations")+" listed in About":"")+". This is not a check result.")),e}function cg(t){let e=f("div","mlv-empty-note");e.setAttribute("role","status"),m(e,f("div","mlv-clean__title","Nothing analyzed"));let n=t.index?t.index.graph:null,o=n?n.diagnostics||[]:[],i=n?n.workspace.filesAnalyzed:0;if(m(e,f("div","mlv-clean__detail","No ML pipeline was found, so there is nothing to flag. "+i+(i===1?" file":" files")+" analyzed.")),o.length){let s=m(e,f("ul","mlv-state__list"));for(let r of o.slice(0,5))m(s,f("li","",r.kind+" — "+r.message))}return e}function ug(t){let e=f("div","mlv-empty-note");m(e,f("div","","No findings match these filters."));let n=U("mlv-btn","Clear filters");return _(n,"click",()=>t.onClearFilters()),e.appendChild(n),e}var Zi='[role="treeitem"]';function Xl(t,e,n){let o=m(t,f("ul","mlv-outline"));o.setAttribute("role","tree"),o.setAttribute("aria-label","Pipeline outline");for(let i of e.index.lanes){let s=m(o,f("li","mlv-outline__item"));s.setAttribute("role","treeitem"),s.setAttribute("data-outline-lane",i.id),s.tabIndex=-1;let r=m(s,f("div","mlv-outline__row mlv-outline__row--lane"));m(r,f("span","mlv-outline__label",e.index.phaseIndexOf(i.id)+1+" · "+(i.label||i.id)));let a=e.index.laneCounts(i.id,e.keep);if(Y(a)>0){r.appendChild(Z(oe(a),12,""));let d=Y(a),c=m(r,f("span","mlv-outline__stage mlv-outline__phasecount",Pe(d)));c.title=Gt(a)}_(r,"click",()=>n.onSelectLane(i.id));let l=e.index.roots(i.id);s.setAttribute("aria-expanded",l.length?"true":"false"),l.length&&s.appendChild(Yl(l,e,n))}return hg(t,e,n),bg(o,n),Zl(o,pg(o,e.selectedNodeId)),o}function hg(t,e,n){let o=m(t,f("section","mlv-relations"));m(o,f("h4","mlv-rail__heading","Text relationships"));let i=e.selectedNodeId&&e.index.nodeById.has(e.selectedNodeId)?e.selectedNodeId:null;i&&m(o,f("p","mlv-relations__context","For "+(e.index.nodeById.get(i)?.label||i)));let s=m(o,f("div","mlv-relations__views"));s.setAttribute("role","group"),s.setAttribute("aria-label","Relationship direction");let r=m(o,f("div")),a=(c,u=!1)=>{for(let g of Array.from(s.querySelectorAll("button")))g.setAttribute("aria-pressed",g.getAttribute("data-relation-view")===c?"true":"false");for(;r.firstChild;)r.removeChild(r.firstChild);let h=c==="unresolved"?e.index.graph.edges.filter(g=>g.basis==="unresolved"||e.index.nodeById.get(g.source)?.basis==="unresolved"||e.index.nodeById.get(g.target)?.basis==="unresolved"):!i||c==="all"?e.index.graph.edges:c==="incoming"?e.index.inEdges.get(i)||[]:e.index.outEdges.get(i)||[],p=m(r,f("ul","mlv-relations__list"));if(p.setAttribute("aria-label",c[0].toUpperCase()+c.slice(1)+" relationships"),!h.length){m(p,f("li","mlv-empty-note","No "+c+" relationships in this view."));return}for(let g of h){let v=e.index.nodeById.get(g.source),b=e.index.nodeById.get(g.target),x=m(p,f("li","mlv-relations__item")),y=m(x,f("button","mlv-relations__row"));y.type="button",y.setAttribute("data-relation-id",g.id);let k=i?g.target===i?"Incoming":g.source===i?"Outgoing":"Related":"Directed",E=$e(g.basis);y.setAttribute("aria-label",`${k}: ${v?.label||g.source} to ${b?.label||g.target}; ${g.label||g.kind}${E?";"+E.slice(1):""}`),m(y,f("span","mlv-relations__path",(v?.label||g.source)+" → "+(b?.label||g.target))),m(y,f("span","mlv-relations__detail",(g.label||g.kind||"connection")+(fe(g.basis)?" · "+fe(g.basis):""))),_(y,"click",()=>n.onSelectEdge(g.id))}u&&p.querySelector("button")?.focus()},l=[{id:"all",label:"All"},{id:"incoming",label:"Incoming"},{id:"outgoing",label:"Outgoing"},{id:"unresolved",label:"Unresolved"}];for(let c of l){let u=m(s,f("button","mlv-btn",c.label));u.type="button",u.setAttribute("data-relation-view",c.id),u.disabled=!i&&c.id!=="all"&&c.id!=="unresolved",_(u,"click",()=>{n.onRelationMode(c.id),a(c.id,!0)})}let d=!i&&(e.relationMode==="incoming"||e.relationMode==="outgoing")?"all":e.relationMode;a(d)}function Yl(t,e,n){let o=f("ul");o.setAttribute("role","group");for(let i of t){let s=e.index.nodeById.get(i);if(!s)continue;let r=m(o,f("li","mlv-outline__item"));r.setAttribute("role","treeitem"),r.setAttribute("data-outline-id",i),r.tabIndex=-1;let a=e.index.laneChildren(i),l=a.length>0&&e.collapsed.has(i);a.length&&r.setAttribute("aria-expanded",l?"false":"true");let d=m(r,f("div","mlv-outline__row")),c=e.selectedNodeId===i;if(r.setAttribute("aria-selected",c?"true":"false"),c&&d.classList.add("is-selected"),a.length){let p=B("chevron",12);p.setAttribute("class","mlv-uicon mlv-outline__chevron"),d.appendChild(p)}else m(d,f("span","mlv-outline__chevron mlv-outline__chevron--none"));m(d,f("span","mlv-outline__label",s.label||s.qualname));let u=fe(s.basis);u&&m(d,f("span","mlv-outline__stage",u)).setAttribute("data-basis",s.basis||"");let h=fg(e,i);if(h&&d.appendChild(h),_(d,"click",p=>n.onSelectNode(i,p)),a.length){let p=Yl(a,e,n);p.hidden=l,r.appendChild(p)}}return o}function fg(t,e){let n=oe(t.index.subtreeCounts(e,t.keep));return n?Z(n,12,""):null}function Qi(t){return Array.prototype.slice.call(t.querySelectorAll(Zi)).filter(n=>{let o=n.parentElement;for(;o&&o!==t;){if(o.hidden)return!1;o=o.parentElement}return!0})}function Zl(t,e){let n=Qi(t),o=e&&n.indexOf(e)>=0?e:n[0]||null;for(let i of n)i.tabIndex=i===o?0:-1}function pg(t,e){if(!e)return null;let n=Qi(t);for(let o of n)if(o.getAttribute("data-outline-id")===e)return o;return null}function it(t,e){e&&(Zl(t,e),e.focus())}function mg(t,e){let n=e.parentElement;for(;n&&n!==t;){if(n.getAttribute("role")==="treeitem")return n;n=n.parentElement}return null}function gg(t){let e=t.querySelector('[role="group"]');return!e||e.hidden?null:e.querySelector(Zi)}function Ul(t,e){let n=t.getAttribute("data-outline-id");if(n){e.onSelectNode(n);return}let o=t.getAttribute("data-outline-lane");o&&e.onSelectLane(o)}function vg(t){let e=t.querySelector(".mlv-outline__label");return(e&&e.textContent||"").toLowerCase()}function bg(t,e){_(t,"keydown",n=>{let o=n.target;if(!o||typeof o.closest!="function")return;let i=o.closest(Zi);if(!i||!t.contains(i))return;let s=Qi(t),r=s.indexOf(i);if(r<0)return;let a=i.getAttribute("aria-expanded"),l=i.getAttribute("data-outline-id");switch(n.key){case"ArrowDown":n.preventDefault(),it(t,s[Math.min(s.length-1,r+1)]);return;case"ArrowUp":n.preventDefault(),it(t,s[Math.max(0,r-1)]);return;case"Home":n.preventDefault(),it(t,s[0]);return;case"End":n.preventDefault(),it(t,s[s.length-1]);return;case"ArrowRight":n.preventDefault(),a==="false"&&l?e.onToggleCollapse(l):a==="true"&&it(t,gg(i));return;case"ArrowLeft":n.preventDefault(),a==="true"&&l?e.onToggleCollapse(l):it(t,mg(t,i));return;case"Enter":if(n.ctrlKey||n.metaKey||n.shiftKey)return;n.preventDefault(),l?e.onOpenNode(l,n.altKey):Ul(i,e);return;case" ":n.preventDefault(),Ul(i,e);return;default:break}if(n.key.length!==1||n.ctrlKey||n.metaKey||n.altKey)return;let d=n.key.toLowerCase();for(let c=1;c<=s.length;c++){let u=s[(r+c)%s.length];if(vg(u).indexOf(d)===0){n.preventDefault(),it(t,u);return}}})}var kt=["notObserved","findings","changed","all"],lo={notObserved:"Not observed",findings:"Findings",changed:"Changed files",all:"All claims"};function co(t,e){let n=e===1?" claim":" claims";return t==="all"?"All "+e+n:lo[t]+", "+e+n}var wg={notObserved:"not observed",findings:"findings",changed:"changed files",all:""};function ze(t){return!!t&&t!=="observed"}function ue(t,e){return!!t&&!!e&&t.kind===e.kind&&t.id===e.id}function Ql(t){let e=[],n=new Map,o=[],i=c=>{if(!(n.has(c)||!t.nodeById.has(c))){n.set(c,o.length),o.push(c);for(let u of t.laneChildren(c))i(u)}};for(let c of t.lanes)for(let u of t.roots(c.id))i(u);for(let c of t.graph.nodes||[])i(c.id);let s=new Map,r=[];for(let c of t.graph.issues||[]){let u=null;for(let h of c.nodeIds||[]){let p=n.get(h);p!==void 0&&(u===null||p{l.has(c)||(l.add(c),e.push({kind:"issue",id:c}))};for(let c of o){e.push({kind:"node",id:c});for(let u of t.outEdges.get(c)||[])a.has(u.id)||(a.add(u.id),e.push({kind:"edge",id:u.id}));for(let u of s.get(c)||[])d(u)}for(let c of t.graph.edges||[])a.has(c.id)||(a.add(c.id),e.push({kind:"edge",id:c.id}));for(let c of r)d(c);return e}function uo(t,e){return e.kind==="node"?t.nodeById.get(e.id)?.basis:e.kind==="edge"?t.edgeById.get(e.id)?.basis:t.issueById.get(e.id)?.basis}function $i(t,e){return e.kind==="node"?t.nodeById.get(e.id)?.evidenceLocs||[]:e.kind==="edge"?t.edgeById.get(e.id)?.evidenceLocs||[]:t.issueById.get(e.id)?.relatedLocs||[]}function $l(t){return t.length>0&&!ae(t)}function Jl(t,e,n,o){return t==="all"?!0:t==="findings"?n.kind==="issue":t==="notObserved"?ze(uo(e,n)):$i(e,n).some(i=>!!i.file&&o(i.file))}function ed(t,e,n,o){return t.filter(i=>Jl(e,n,i,o))}function td(t,e,n){let o={notObserved:0,findings:0,changed:0,all:0};for(let i of t)for(let s of kt)Jl(s,e,i,n)&&o[s]++;return o}function nd(t,e){return kt.filter(n=>n==="all"||t[n]>0&&(n!=="changed"||e))}function _t(t,e,n){if(!t.length)return-1;if(!e)return 0;let o=t.findIndex(a=>ue(a,e));if(o>=0)return o;let i=new Map;n.forEach((a,l)=>i.set(a.kind+":"+a.id,l));let s=i.get(e.kind+":"+e.id);if(s===void 0)return 0;let r=t.findIndex(a=>(i.get(a.kind+":"+a.id)??-1)>s);return r>=0?r:t.length-1}function yg(t,e){return e.kind==="node"?t.isGroup(e.id)?"Group":"Step":e.kind==="edge"?"Connection":"Finding"}function od(t,e){let n=t.edgeById.get(e.id);if(!n)return null;let o=t.nodeById.get(n.source),i=t.nodeById.get(n.target);return{from:o?o.label||o.id:n.source,to:i?i.label||i.id:n.target}}function xg(t,e){if(e.kind==="node"){let o=t.nodeById.get(e.id);return o?o.label||o.id:e.id}if(e.kind==="edge"){let o=t.edgeById.get(e.id),i=od(t,e);return!o||!i?e.id:(o.label?o.label+" · ":"")+i.from+" → "+i.to}let n=t.issueById.get(e.id);return n?(n.short?n.short+" ":"")+n.title:e.id}function Eg(t,e){if(e.kind!=="edge")return xg(t,e);let n=t.edgeById.get(e.id),o=od(t,e);return!n||!o?e.id:(n.label?n.label+", ":"")+"from "+o.from+" to "+o.to}function id(t,e,n,o,i){let s=uo(o,i),r=wg[n];return"Claim "+(t+1)+" of "+e+(r?", "+r:"")+": "+yg(o,i)+" "+Eg(o,i)+(ze(s)?", "+s:"")+"."}function Ji(t){let e=t.endLine&&t.endLine>t.line?t.endLine:t.line,n=e>t.line?"lines "+t.line+"–"+e:"line "+t.line,o=ke(t);return t.file+(o?" · cell "+o.cell:"")+" · "+n}function kg(t){return t.quotes>1?" (quote "+(t.quote+1)+" of "+t.quotes+")":""}function sd(t){let e=t.loc?Ji(t.loc)+kg(t):"";switch(t.state){case"none":return"This claim cites no lines, so nothing is opened for it.";case"unavailable":return"This view cannot open source files.";case"idle":return"Enter shows "+e+" in the editor beside.";case"opening":return"Opening in the editor beside: "+e+"…";case"done":return"In the editor beside: "+e+", highlighted. "+(t.focusEditor?"Focus moved to the editor.":"Focus stays here.");default:return(t.quotes>1?"Quote "+(t.quote+1)+" of "+t.quotes+": ":"")+(t.message||"Not opened.")}}function rd(t){switch(t.state){case"idle":return"Enter shows these lines in the editor beside.";case"opening":return"Opening in the editor beside…";case"done":return t.focusEditor?"In the editor beside, highlighted; the focus moved there.":"In the editor beside, highlighted.";case"blocked":case"failed":return t.message||"Not opened.";default:return""}}function ad(t,e){return/\bnot opened\b/i.test(e)?e:(t==="blocked"?"Not opened: ":"Failed: ")+e}var _g="A matching quote shows these lines exist unchanged since publishing. Whether they support the claim is for you to judge.",Ve=(t,e,n)=>t+" "+(t===1?e:n);function Sg(t,e){return t==="inferred"?"Reasoned from the cited code and stated assumptions; the quotes do not show all of it directly.":t==="unresolved"?"The evidence does not settle this claim. It does not mean the "+e+" is missing.":""}function dd(t,e,n){let o=m(t,f("div","mlv-sel"));o.setAttribute("data-columns",String(e.columns)),e.issue?(o.setAttribute("data-kind","finding"),Tg(o,e.issue,e,n)):e.edge?(o.setAttribute("data-kind","connection"),Cg(o,e.edge,e,n)):e.node?(o.setAttribute("data-kind",e.index.isGroup(e.node.id)?"group":"step"),Ag(o,e.node,e,n)):m(o,f("p","mlv-empty-note","Select a step, a connection or a finding to read its claim beside its evidence.")),os(t,e.walk||null)}function os(t,e,n=!1){if(!t)return;for(let r of Array.from(t.querySelectorAll(".mlv-quote.is-walk"))){r.classList.remove("is-walk"),r.removeAttribute("data-walk-status");let a=r.querySelector(".mlv-quote__walk");a&&a.remove()}if(!e)return;let i=t.querySelectorAll(".mlv-sel .mlv-insp__source-evidence > .mlv-quote")[e.quote];if(!i)return;i.classList.add("is-walk"),i.setAttribute("data-walk-status",e.status.state);let s=rd(e.status);if(s){let r=f("p","mlv-quote__walk");(e.status.state==="blocked"||e.status.state==="failed")&&r.appendChild(B("warning",12)),m(r,f("span","",s));let a=i.querySelector(".mlv-quote__head");a&&a.nextSibling?i.insertBefore(r,a.nextSibling):i.appendChild(r)}n&&Ig(i,n==="claim")}var St=8;function Mg(t,e,n,o,i,s=!1){if(!(e>0))return t;let a=Math.min(o,n+e-2*St)+St-e,l=n-St;if(i!==null&&i-St>=a)l=Math.min(l,i-St);else if(i!==null&&s)return Math.max(0,Math.round(Math.min(t,i-St)));return l{n.clientHeight!==i&&(a.disconnect(),t.isConnected&&t.classList.contains("is-walk")&&ld(n,t,o,e))});a.observe(n),s.setTimeout(()=>a.disconnect(),Lg)}function ld(t,e,n,o){let i=t.clientHeight;if(!(i>0))return i;let s=t.getBoundingClientRect(),r=h=>h.top-s.top+t.scrollTop,a=e.getBoundingClientRect(),l=e.querySelector(".mlv-quote__walk")||e.querySelector(".mlv-quote__head"),d=l?r(l.getBoundingClientRect())+l.getBoundingClientRect().height:r(a)+a.height,c=t.querySelector(".mlv-sel .mlv-insp__title"),u=Mg(n,i,r(a),d,c?r(c.getBoundingClientRect()):null,o);return u!==t.scrollTop&&(t.scrollTop=u),i}function is(t,e){if(e.columns===1)return{claim:t,evidence:t};let n=m(t,f("div","mlv-sel__col mlv-sel__col--claim")),o=m(t,f("div","mlv-sel__col mlv-sel__col--evidence"));return{claim:n,evidence:o}}function Ag(t,e,n,o){let i=n.index,s=i.isGroup(e.id),r=s?"group":"step",{claim:a,evidence:l}=is(t,n),d=m(a,f("p","mlv-insp__eyebrow")),c=i.phaseIndexOf(e.stage),u=e.phaseLabel||ud(i,e.stage),h=m(d,f("span","mlv-insp__phase"));h.setAttribute("data-stage",e.stage),le(h,c),m(h,f("span","mlv-insp__phasedot")).setAttribute("aria-hidden","true"),m(h,f("span","",c+1+" · "+u)),h.title="Phase "+(c+1)+": "+u,e.kind&&e.kind!=="unknown"&&es(d,"mlv-insp__kind",e.kind);let p=i.parentOf.get(e.id),g=p?i.nodeById.get(p):void 0;g&&es(d,"mlv-insp__parent","in "+(g.label||g.id)),m(a,f("h4","mlv-insp__title",e.label||e.qualname)),ss(a,e.basis,r),e.detail&&m(a,f("p","mlv-insp__detail",e.detail)),s?(ts(a,i.subtreeIssues(e.id,n.keep),"Findings in this group",n,o),Ng(a,e,n,o)):ts(a,i.issuesOf(e.id,n.keep),"Findings on this step",n,o);let v=()=>s?Fg(a,e,n,o):Pg(a,e,n,o);n.columns===1?(ns(l,e.evidenceLocs||[],n,o,r),v()):(v(),ns(l,e.evidenceLocs||[],n,o,r)),rs(l,o),as(l,n,o)}function Cg(t,e,n,o){let i=n.index,{claim:s,evidence:r}=is(t,n),a=m(s,f("p","mlv-insp__eyebrow"));m(a,f("span","","Connection"));let l=es(a,"mlv-insp__edgekind",Ht(e.kind)+(e.authoredKind?" · authored as "+e.authoredKind:""));l.setAttribute("data-edge-kind",e.kind),l.title="Kind of connection, as the author named it",m(s,f("h4","mlv-insp__title",e.label||"Connection"));let d=i.nodeById.get(e.source),c=i.nodeById.get(e.target),u=m(s,f("p","mlv-insp__ends"));u.appendChild(st(e.source,d,o)),u.appendChild(document.createTextNode(" → ")),u.appendChild(st(e.target,c,o)),ss(s,e.basis,"connection"),ts(s,i.issuesOfEdge(e.id,n.keep),"Findings on this connection",n,o),ns(r,e.evidenceLocs||[],n,o,"connection"),rs(r,o),as(r,n,o)}function Tg(t,e,n,o){let i=n.index,{claim:s,evidence:r}=is(t,n),a=m(s,f("div","mlv-insp__eyebrow mlv-insp__issue-head"));if(a.setAttribute("data-issue-id",e.id),a.appendChild(Z(e.severity,14,"")),m(a,f("span","mlv-insp__severity",Bg(e.severity)+" finding")),e.short){let g=m(a,f("span","mlv-insp__short",e.short));g.title=e.short+" is this finding's number in this revision; its id is "+e.code+"."}m(a,f("span","mlv-mono",e.code));let l=Kt(e);l&&a.appendChild(l),m(s,f("h4","mlv-insp__title",e.title)),ss(s,e.basis,"finding"),e.message&&m(s,f("p","mlv-insp__detail mlv-insp__message",e.message));let d=ao(e,"h5");d&&s.appendChild(d);let c=m(s,f("section","mlv-insp__section mlv-insp__cited"));c.setAttribute("data-section","cited");let u=e.nodeIds.filter(g=>i.nodeById.has(g)),h=e.edgeIds.filter(g=>i.edgeById.has(g));if(!u.length&&!h.length&&m(c,f("p","mlv-insp__note","This finding cites no step or connection: it is about the workflow as a whole.")),u.length){c.appendChild(qe("Cited steps",Ve(u.length,"step","steps")));let g=m(c,f("ul","mlv-insp__links"));for(let v of u){let b=i.nodeById.get(v),x=m(g,f("li"));x.appendChild(st(v,b,o));let y=b.phaseLabel||ud(i,b.stage);m(x,f("span","mlv-insp__aside"," · "+(i.phaseIndexOf(b.stage)+1)+" · "+y))}}if(h.length){c.appendChild(qe("Cited connections",Ve(h.length,"connection","connections")));let g=m(c,f("ul","mlv-insp__links"));for(let v of h){let b=i.edgeById.get(v),x=m(g,f("li")),y=U("mlv-link mlv-insp__flow-edge",b.label||"connection","Select the connection "+ds(i,b));y.setAttribute("data-edge-id",b.id),_(y,"click",()=>o.onShowEdge(b.id)),x.appendChild(y),m(x,f("span","mlv-insp__aside"," · "+ls(i,b)))}}let p=e.relatedLocs||[];cd(r,p,n,o,{noun:"finding",role:g=>g.role||"Supporting evidence",staleNote:()=>{let g=ro(e,n.staleReason);return g.length?ae(g)?"This finding "+Et(g)+"; those jumps are blocked. The notice above says which folder to add.":"This finding "+Et(g)+" since publishing; those jumps are blocked.":""}}),rs(r,o),as(r,n,o)}function es(t,e,n){return m(t,f("span","mlv-insp__sep"," · ")).setAttribute("aria-hidden","true"),m(t,f("span",e,n))}function ss(t,e,n){let o=Sg(e,n);if(!o)return;let i=m(t,f("p","mlv-insp__basis"));i.setAttribute("data-basis",e||"");let s=n==="finding"?null:yt(e,n);s&&(s.removeAttribute("aria-hidden"),s.removeAttribute("title"),i.appendChild(s),i.appendChild(document.createTextNode(" "))),m(i,f("span","",o))}function qe(t,e){let n=f("h5","mlv-rail__heading");return m(n,f("span","mlv-rail__headtext",t)),e&&m(n,f("span","mlv-rail__count",e)),n}function ts(t,e,n,o,i){if(!e.length)return;let s=m(t,f("section","mlv-insp__section"));s.setAttribute("data-section","findings"),s.appendChild(qe(n,Ve(e.length,"finding","findings")));for(let r of e){let a=m(s,f("div","mlv-insp__issue"));a.setAttribute("data-issue-id",r.id);let l=m(a,f("div","mlv-insp__issue-head"));if(l.appendChild(Z(r.severity,14,"")),r.short){let p=m(l,f("span","mlv-insp__short",r.short));p.title=r.short+" is this finding's number in this revision; its id is "+r.code+"."}let d=U("mlv-link mlv-insp__issue-title",r.title,"Select finding "+(r.short?r.short+", ":"")+r.title);_(d,"click",()=>i.onShowIssue(r.id)),l.appendChild(d);let c=Kt(r);c&&l.appendChild(c),m(a,f("p","mlv-insp__issue-id mlv-mono",r.code)),r.message&&m(a,f("p","mlv-insp__line",r.message));let u=ao(r,"h6");u&&a.appendChild(u);let h=ro(r,o.staleReason);if(h.length){a.classList.add("is-stale");let p=m(a,f("p","mlv-insp__stale-note"));p.appendChild(B("warning",12)),m(p,f("span","","This finding "+Et(h)+(ae(h)?".":" since publishing.")))}}}function ns(t,e,n,o,i){cd(t,e,n,o,{noun:i,nav:!0,staleNote:()=>{let s=d=>n.staleReason&&d.file?n.staleReason(d.file):void 0,r=Vs(e,d=>!!(n.staleReason&&n.staleReason(d)));if(!r.stale)return"";let a=e.map(s).filter(d=>!!d),l=r.stale+" of "+r.total+(r.total===1?" quote cites ":" quotes cite ");return ae(a)?l+"a file that is not under the workspace root but is unchanged in another folder; those jumps are blocked. The notice above says which folder to add.":l+"a file that changed or went missing since publishing; those jumps are blocked. To compare the claim with the code as it is now, ask the assistant for a fresh revision."}})}function cd(t,e,n,o,i){let s=m(t,f("section","mlv-insp__section mlv-insp__quotes"));if(s.setAttribute("data-section","quotes"),!e.length){s.appendChild(qe("Source evidence","no quotes")),m(s,f("p","mlv-empty-note mlv-insp__no-evidence","No source evidence was authored for this "+i.noun+". Its basis and the coverage limitations describe what remains uncertain."));return}s.appendChild(qe("Source evidence",Ve(e.length,"quote","quotes"))),m(s,f("p","mlv-insp__caption",_g));let r=i.staleNote();if(r){let c=m(s,f("p","mlv-insp__stale-note"));c.appendChild(B("warning",12)),m(c,f("span","",r))}let a=0,l=()=>{};if(i.nav&&e.length>1){let c=m(s,f("div","mlv-insp__evidence-nav")),u=U("mlv-btn","Previous evidence"),h=U("mlv-btn","Next evidence");l=()=>{u.disabled=a===0,h.disabled=a===e.length-1,u.title=u.disabled?"This is the first evidence item":"Open the previous evidence item",h.title=h.disabled?"This is the last evidence item":"Open the next evidence item"},_(u,"click",()=>{a>0&&o.onOpen(e[--a]),l()}),_(h,"click",()=>{a{let h=m(d,f("li","mlv-quote")),p=n.staleReason&&c.file?n.staleReason(c.file):void 0;p&&(h.classList.add("is-stale"),h.setAttribute("data-stale",p));let g=m(h,f("div","mlv-quote__head"));m(g,f("span","mlv-quote__num",String(u+1))).setAttribute("aria-hidden","true");let v=m(g,f("span","mlv-quote__loc"));m(v,f("span","mlv-quote__file",c.file)),m(v,f("span","mlv-quote__at",Rg(c))),Ze(c)&&(v.title=Ze(c)),i.role&&m(g,f("span","mlv-quote__role",i.role(c))),g.appendChild(Og(c,p,n));let b=U("mlv-link mlv-quote__open","Open","Open "+we(c));b.setAttribute("data-evidence-id",c.evidenceId||""),p||(b.title="Open "+we(c)+" beside the diagram and highlight the cited lines; focus stays here (Alt+click moves it)."),Ze(c)&&!p&&(b.title=Ze(c)),Ut(b,c,(x,y)=>{a=u,l(),o.onOpen(x,y)},p),g.appendChild(b),c.snippet&&h.appendChild(Hg(c))})}function Rg(t){let e=ke(t),n=e?" › cell "+e.cell:"",o=t.endLine&&t.endLine>t.line?t.endLine:t.line;return n+(o>t.line?", lines "+t.line+"–"+o:", line "+t.line)}function Og(t,e,n){if(e){let s=f("span","mlv-quote__fresh mlv-insp__stale");return s.setAttribute("data-fresh","stale"),s.setAttribute("data-stale",e),s.appendChild(B("warning",12)),m(s,f("span","",me[e])),s}let o=!!(n.document&&n.document.verification&&t.file&&Object.prototype.hasOwnProperty.call(n.document.verification.files||{},t.file)),i=f("span","mlv-quote__fresh is-muted",o?"unchanged":"not checked");return i.setAttribute("data-fresh",o?"unchanged":"unchecked"),i.title=o?"Unchanged means these exact lines still exist as quoted. It does not mean they support the claim.":"This revision has no published hash for this file, so MLView cannot tell whether it changed.",i}function Hg(t){let e=f("pre","mlv-quote__text"),n=String(t.snippet||"").split(` +`);return n.forEach((o,i)=>{let s=m(e,f("span","mlv-quote__line"));m(s,f("span","mlv-quote__ln",String(t.line+i))).setAttribute("aria-hidden","true"),m(s,f("span","mlv-quote__code",o)),i{let h=m(l,f("li"));h.setAttribute("data-direction",u);let p=u==="in"?c.source:c.target;p===e.id?m(h,f("span","","Loops back to this step")):(m(h,f("span","",u==="in"?"Comes from ":"Feeds ")),h.appendChild(st(p,i.nodeById.get(p),o))),m(h,f("span","",": "));let g=U("mlv-link mlv-insp__flow-edge",c.label||"an unlabelled connection","Select the connection "+ds(i,c));g.setAttribute("data-edge-id",c.id),_(g,"click",()=>o.onShowEdge(c.id)),h.appendChild(g);let v=c.basis==="inferred"?" (inferred, not observed)":c.basis==="unresolved"?" (unresolved)":"";m(h,f("span",v?"mlv-insp__flow-basis":"",v+"."))};for(let c of s)d(c,"in");for(let c of r)c.source!==c.target&&d(c,"out")}function Ng(t,e,n,o){let i=n.index,s=i.laneChildren(e.id).filter(l=>i.nodeById.has(l));if(!s.length)return;let r=m(t,f("section","mlv-insp__section"));r.setAttribute("data-section","members"),r.appendChild(qe("Steps in this group",Ve(s.length,"step","steps")));let a=m(r,f("ul","mlv-insp__links"));for(let l of s){let d=m(a,f("li"));d.appendChild(st(l,i.nodeById.get(l),o));let c=i.descendantCount(l);c&&m(d,f("span","mlv-insp__aside"," · group of "+Ve(c,"step","steps")))}}function Fg(t,e,n,o){let i=n.index,s=new Set,r=h=>{s.add(h);for(let p of i.laneChildren(h))r(p)};r(e.id);let a=[],l=[];for(let h of i.graph.edges){let p=s.has(h.source),g=s.has(h.target);if(p&&g){h.source===e.id&&h.target===e.id&&a.push(h);continue}g?a.push(h):p&&l.push(h)}if(!a.length&&!l.length)return;let d=m(t,f("section","mlv-insp__section mlv-insp__flow"));d.setAttribute("data-section","connections"),d.appendChild(qe("Connections across its edge",Ve(a.length+l.length,"connection","connections")));let c=m(d,f("ul","mlv-insp__sentences")),u=(h,p)=>{let g=m(c,f("li"));g.setAttribute("data-direction",p);let v=p==="in"?h.source:h.target,b=p==="in"?h.target:h.source;v===e.id&&b===e.id?m(g,f("span","","Loops back to this group")):(m(g,f("span","",p==="in"?"Comes from ":"Feeds ")),g.appendChild(st(v,i.nodeById.get(v),o)),b!==e.id&&(m(g,f("span","",p==="in"?" into ":" from ")),g.appendChild(st(b,i.nodeById.get(b),o)))),m(g,f("span","",": "));let x=U("mlv-link mlv-insp__flow-edge",h.label||"an unlabelled connection","Select the connection "+ds(i,h));x.setAttribute("data-edge-id",h.id),_(x,"click",()=>o.onShowEdge(h.id)),g.appendChild(x);let y=h.basis==="inferred"?" (inferred, not observed)":h.basis==="unresolved"?" (unresolved)":"";m(g,f("span",y?"mlv-insp__flow-basis":"",y+"."))};for(let h of a)u(h,"in");for(let h of l)u(h,"out")}function rs(t,e){let n=m(t,f("div","mlv-insp__actions")),o=U("mlv-btn mlv-insp__challenge","Challenge this claim","Copy a prompt that asks your assistant to check this claim against the code. MLView calls no model.");_(o,"click",()=>e.onChallenge());let i=U("mlv-btn mlv-insp__refine","Refine…","Copy a refinement prompt about this selection. MLView calls no model.");_(i,"click",()=>e.onRefine()),n.append(o,i)}function as(t,e,n){let o=(e.index.graph.diagnostics||[]).filter(r=>r.kind==="workflow_limitation").length;if(!o)return;let i=m(t,f("p","mlv-insp__limits"));i.setAttribute("data-limitations",String(o)),m(i,f("span","",(o===1?"1 document-wide limitation applies":o+" document-wide limitations apply")+" to every claim. "));let s=U("mlv-link mlv-link--inline mlv-insp__limits-show",o===1?"Read it in About":"Read them in About");s.setAttribute("aria-label",o===1?"Read the document-wide limitation in About":"Read the "+o+" document-wide limitations in About"),_(s,"click",()=>n.onShowLimitations()),i.appendChild(s)}function st(t,e,n){let o=e?e.label||e.id:t,i=U("mlv-link mlv-insp__flow-step",o,"Select the step "+o);return i.setAttribute("data-node-id",t),_(i,"click",()=>n.onShowNode(t)),i}function ls(t,e){let n=t.nodeById.get(e.source),o=t.nodeById.get(e.target);return(n?n.label:e.source)+" → "+(o?o.label:e.target)}function ds(t,e){return(e.label?e.label+", ":"")+"from "+ls(t,e).replace(" → "," to ")}function ud(t,e){let n=(t.graph.stages||[]).find(o=>o.id===e);return n&&n.label||e}function Bg(t){return t&&t[0].toUpperCase()+t.slice(1)}function hd(t,e){let n=s=>s==="inferred"?" Inferred, not observed.":s==="unresolved"?" Unresolved.":"";if(e.kind==="node"){let s=t.nodeById.get(e.id);if(!s)return"";let r=s.detail?" "+Dn(s.detail):"";return"Step: "+(s.label||s.id)+"."+n(s.basis)+r}if(e.kind==="edge"){let s=t.edgeById.get(e.id);return s?"Connection: "+(s.label?s.label+", ":"")+"from "+ls(t,s).replace(" → "," to ")+"."+n(s.basis):""}let o=t.issueById.get(e.id);if(!o)return"";let i=o.message?" "+Dn(o.message):"";return"Finding "+(o.short?o.short+", ":"")+o.severity+" severity: "+o.title+"."+n(o.basis)+i}var Dg=280,jg=4,Gg=3,cs=/(^|[.!?]["')\]]?\s+|\n\s*)([A-Z][A-Za-z0-9&/(),' -]{0,47}?):\s+/g;function Wg(t){let e=String(t||""),n=[];cs.lastIndex=0;for(let s=cs.exec(e);s;s=cs.exec(e)){let r=s[2].trim();if(r.split(/\s+/).filter(Boolean).length>6)continue;let l=s.index+s[1].length;n.push({at:l,textAt:s.index+s[0].length,head:r})}if(n.length{let a=r+1n&&e.push({text:t.slice(n,o.index),kv:!1}),e.push({text:i,kv:!0}),n=o.index+i.length,ho.lastIndex=n}return nDg||n.request.question.split(` +`).length>jg){r.setAttribute("data-clamped","true");let w=U("mlv-link mlv-about__more","Show all","Show the whole question");w.setAttribute("aria-expanded","false"),w.setAttribute("aria-controls",r.id),_(w,"click",()=>{let S=r.getAttribute("data-clamped")==="true";r.setAttribute("data-clamped",S?"false":"true"),w.textContent=S?"Show less":"Show all",w.setAttribute("aria-expanded",S?"true":"false"),w.setAttribute("aria-label",S?"Show less of the question":"Show the whole question")}),s.appendChild(w)}let l=rt(i,"traced","What the model traced"),d=Wg(n.coverage.summary);l.setAttribute("data-paragraphs",String(d.length));for(let w of d){let S=m(l,f("p","mlv-about__para"));w.head&&(m(S,f("strong","mlv-about__lead",w.head+":")),S.appendChild(document.createTextNode(" "))),S.appendChild(document.createTextNode(w.text))}let c=rt(i,"coverage","Coverage");m(c,f("p","mlv-about__status mlv-workflow__coverage",Vg(n.coverage.status))).setAttribute("data-status",n.coverage.status);let h=n.coverage.limitations;if(h.length){let w=m(c,f("details","mlv-about__limitations mlv-workflow__limitations"));w.open=!0;let S=h.length;m(w,f("summary","",S+(S===1?" coverage limitation":" coverage limitations")+" (apply to every claim)"));let C=m(w,f("ul"));for(let L of h)m(C,f("li","",L))}else m(c,f("p","mlv-about__note","The assistant listed no coverage limitations."));let p=rt(i,"scope","Scope");m(p,f("p","mlv-about__text",n.request.scope));let g=n.request.entrypoints&&n.request.entrypoints.length?n.request.entrypoints.join(", "):"not specified";m(p,f("p","mlv-about__meta","Entrypoints: "+g));let v=rt(i,"config","Run configuration"),b=n.request.configuration;if(b){let w=m(v,f("p","mlv-about__text mlv-about__config"));for(let S of zg(b))S.kv?m(w,f("code","mlv-about__kv",S.text)):w.appendChild(document.createTextNode(S.text))}else m(v,f("p","mlv-about__meta","Configuration: not specified."));let x=rt(i,"files","Cited files"),y=[];for(let w of n.evidence||[])y.indexOf(w.file)<0&&y.push(w.file);if(!y.length)m(x,f("p","mlv-about__note","No file is cited."));else{let w=x.querySelector(".mlv-rail__heading");w&&m(w,f("span","mlv-rail__count",y.length+(y.length===1?" file":" files")));let S=m(x,f("ul","mlv-about__files")),C=n.verification?n.verification.files||{}:null;for(let L of y){let I=m(S,f("li","mlv-about__file"));I.setAttribute("data-file",L),m(I,f("span","mlv-about__path",L)).title=L;let F=e.staleReason?e.staleReason(L):void 0;if(F){let A=m(I,f("span","mlv-quote__fresh mlv-insp__stale"));A.setAttribute("data-fresh","stale"),A.setAttribute("data-stale",F),A.appendChild(B("warning",12)),m(A,f("span","",me[F]))}else{let A=!!C&&Object.prototype.hasOwnProperty.call(C,L);m(I,f("span","mlv-quote__fresh is-muted",A?"unchanged":"not checked")).setAttribute("data-fresh",A?"unchanged":"unchecked")}}m(x,f("p","mlv-about__note","Unchanged means the quoted lines still exist as published. It does not mean they support the claims."))}let k=rt(i,"provenance","Provenance"),E=[n.producer.host,n.producer.model||"model not named","revision "+n.revision.id+(n.revision.parent?" (after "+n.revision.parent+")":"")];E.push(n.verification?"published "+n.verification.publishedAt:"published without source hashes"),m(k,f("p","mlv-about__meta",E.join(" · "))),m(k,f("p","mlv-about__trust mlv-workflow__provenance","Model-authored; MLView checks citations, not the interpretation."))}function rt(t,e,n){let o=m(t,f("section","mlv-about__section"));o.setAttribute("data-about",e);let i=m(o,f("h4","mlv-rail__heading"));return m(i,f("span","mlv-rail__headtext",n)),o}var Ke=[{id:"about",label:"About",heading:"About this revision"},{id:"issues",label:"Findings",heading:"Findings"},{id:"inspector",label:"Selection",heading:"Selection"},{id:"outline",label:"Outline",heading:"Outline"}],pd=.05,Kg=.15,Ug=0,fo=class{constructor(e){this.tabs=new Map;this.counts=new Map;this.panels=new Map;this.relationMode="outgoing";this.relationNodeId=null;this.current="about";this.mode="docked";this.open=!0;this.selectionKey="";this.cb=e;let n="mlv"+ ++Ug;this.root=f("aside","mlv-rail"),this.root.id=n+"-rail",this.root.setAttribute("data-mode","docked"),this.root.setAttribute("aria-labelledby",n+"-rail-heading"),this.heading=m(this.root,f("h2","mlv-sr","Side panel")),this.heading.id=n+"-rail-heading",this.grip=m(this.root,f("div","mlv-rail__grip")),this.grip.setAttribute("role","separator"),this.grip.setAttribute("aria-orientation","vertical"),this.grip.setAttribute("aria-label","Resize side panel"),this.grip.tabIndex=0,this.wireGrip(this.grip);let o=m(this.root,f("div","mlv-rail__bar")),i=m(o,f("div","mlv-rail__tabs"));i.setAttribute("role","tablist"),i.setAttribute("aria-label","Panel");for(let s=0;se.onTab(r.id)),_(a,"keydown",d=>{let c;if(d.key==="ArrowRight")c=(s+1)%Ke.length;else if(d.key==="ArrowLeft")c=(s+Ke.length-1)%Ke.length;else if(d.key==="Home")c=0;else if(d.key==="End")c=Ke.length-1;else return;d.preventDefault();let u=Ke[c].id;e.onTab(u),this.tabs.get(u)?.focus()}),i.appendChild(a),this.tabs.set(r.id,a);let l=f("div","mlv-rail__panel");l.id=n+"-panel-"+r.id,l.setAttribute("role","tabpanel"),l.setAttribute("aria-labelledby",a.id),l.setAttribute("data-tab",r.id),l.tabIndex=0,l.hidden=!0,this.root.appendChild(l),this.panels.set(r.id,l)}this.chevron=m(o,f("button","mlv-btn mlv-btn--icon mlv-rail__chevron")),this.chevron.type="button",this.chevron.setAttribute("aria-controls",this.root.id),this.chevron.appendChild(B("chevron",14)),this.chevron.hidden=!0,_(this.chevron,"click",()=>e.onSheetToggle()),_(this.root,"keydown",s=>{s.key!=="Escape"||s.defaultPrevented||this.mode!=="sheet"||!this.open||(s.preventDefault(),s.stopPropagation(),e.onSheetCollapse())}),this.applyShape()}setMode(e,n){this.mode=e,this.root.setAttribute("data-mode",e);let o=this.root.parentElement;o&&o.setAttribute("data-rail",e),e==="sheet"&&(this.root.style.width=""),this.setSheetFraction(n),this.applyShape()}setOpen(e){this.open=e,this.applyShape()}setSheetFraction(e){if(this.root.style.setProperty("--mlv-sheet-fraction",String(e)),this.mode==="sheet"){let n=this.cb.sheetFractionMax(),o=Math.round(Math.min(e,n)*100);this.grip.setAttribute("aria-valuemax",String(Math.round(n*100))),this.grip.setAttribute("aria-valuenow",String(o)),this.grip.setAttribute("aria-valuetext",this.open?o+" percent of the height":"collapsed")}}focusTab(e){let n=this.tabs.get(e);if(n)try{n.focus()}catch{}}activePanel(){return this.panels.get(this.current)}revealLimitations(){let e=this.panels.get("about"),n=e.querySelector(".mlv-about__limitations");if(!n)return e;n.open=!0;let o=n.querySelector("summary")||n;if(typeof n.scrollIntoView=="function")try{n.scrollIntoView({block:"nearest"})}catch{}return o}applyShape(){let e=this.mode==="sheet";if(this.root.hidden=!e&&!this.open,e?this.root.setAttribute("data-expanded",this.open?"true":"false"):this.root.removeAttribute("data-expanded"),this.heading.textContent=e?"Bottom panel":"Side panel",this.grip.setAttribute("aria-orientation",e?"horizontal":"vertical"),this.grip.setAttribute("aria-label",e?"Resize the bottom panel":"Resize side panel"),e)this.grip.setAttribute("aria-valuemin","25"),this.setSheetFraction(this.cb.sheetFraction());else for(let o of["aria-valuemin","aria-valuemax","aria-valuenow","aria-valuetext"])this.grip.removeAttribute(o);this.chevron.hidden=!e,this.chevron.setAttribute("aria-expanded",this.open?"true":"false");let n=this.open?"Collapse the bottom panel (Escape)":"Expand the bottom panel";this.chevron.setAttribute("aria-label",n),this.chevron.title=n;for(let[o,i]of this.panels)i.hidden=o!==this.current||e&&!this.open}wireGrip(e){let n=0,o=0,i=0,s=0,r=0,a=!1,l=!1,d=!0,c=0,u=0,h=g=>{if(!l){this.cb.onResize(i+(n-g.clientX));return}let v=o-g.clientY;!a&&Math.abs(v)<4||(a||(a=!0,!d&&v>0&&this.cb.onSheetToggle()),c=s+v/r,this.open&&this.cb.onSheetResize(c))},p=()=>{document.removeEventListener("pointermove",h),document.removeEventListener("pointerup",p),l&&(a?this.open&&c{l=this.mode==="sheet",n=g.clientX,o=g.clientY,i=this.root.getBoundingClientRect().width||360,r=Math.max(1,this.cb.bodyHeight()),d=this.open,u=this.cb.sheetFraction(),s=d?Math.min(u,this.cb.sheetFractionMax()):32/r,c=s,a=!1,document.addEventListener("pointermove",h),document.addEventListener("pointerup",p),g.preventDefault()}),_(e,"keydown",g=>{if(this.mode==="sheet"){if(g.key==="Enter"||g.key===" ")this.cb.onSheetToggle();else if(g.key==="ArrowUp"||g.key==="ArrowDown"){let b=g.key==="ArrowUp";this.open?this.cb.onSheetResize(this.cb.sheetFraction()+(b?pd:-pd)):b&&this.cb.onSheetToggle()}else return;g.preventDefault();return}let v=this.root.getBoundingClientRect().width||360;if(g.key==="ArrowLeft")this.cb.onResize(v+16);else if(g.key==="ArrowRight")this.cb.onResize(v-16);else return;g.preventDefault()})}update(e){let n=this.captureFocus();this.current=e.tab;for(let[i,s]of this.tabs){let r=i===e.tab;s.setAttribute("aria-selected",r?"true":"false"),s.tabIndex=r?0:-1}let o=this.counts.get("issues");if(o){let i=e.issues.length;o.textContent=e.index?" ("+i+")":"";let s=this.tabs.get("issues");s.title=e.index?i+(i===1?" finding":" findings")+" in this revision":"",e.index?s.setAttribute("aria-label","Findings, "+i+(i===1?" finding":" findings")):s.removeAttribute("aria-label")}this.applyShape();for(let[i,s]of this.panels)i!==e.tab&&D(s);e.tab==="about"?this.renderAbout(e):e.tab==="issues"?this.renderIssues(e):e.tab==="outline"?this.renderOutline(e):this.renderSelection(e),n()}captureFocus(){let e=typeof document<"u"?document.activeElement:null;if(!e||typeof e.closest!="function"||!this.root.contains(e))return()=>{};let n=e.closest('[role="tabpanel"]'),o=()=>{if(!n||e.isConnected)return;let a=this.root.ownerDocument;if(a.activeElement&&a.activeElement!==a.body)return;let l=this.panels.get(this.current);if(!(!l||l.hidden))try{l.focus()}catch{}},i=e.closest('[data-issue-id][role="option"], [data-outline-id], [data-outline-lane], [data-relation-id]');if(!i)return o;let s=i.hasAttribute("data-issue-id")?"data-issue-id":i.hasAttribute("data-outline-id")?"data-outline-id":i.hasAttribute("data-outline-lane")?"data-outline-lane":"data-relation-id",r=i.getAttribute(s)||"";return()=>{let a=this.root.querySelector("["+s+'="'+r+'"]');if(!a){o();return}if(a.closest('[role="tabpanel"]')?.hidden)return;let d=a.closest('[role="tree"], [role="listbox"]');if(d){let c=d.querySelectorAll('[role="treeitem"], [role="option"]');for(let u=0;uthis.cb.onSelectIssue(n,o),onOpenIssue:(n,o)=>this.cb.onOpenIssue(n,o),onOpen:(n,o)=>this.cb.onOpen(n,o),onClearFilters:()=>this.cb.onClearFilters()})}renderSelection(e){let n=this.panels.get("inspector");if(D(n),m(n,f("h3","mlv-sr","Selection")),!e.index){m(n,f("div","mlv-empty-note","No workflow loaded yet."));return}dd(n,{index:e.index,keep:e.keep,node:e.selectedNode,edge:e.selectedEdge,issue:e.selectedIssue,columns:e.columns,document:e.document,staleReason:e.staleReason,walk:e.walk||null},{onOpen:(s,r)=>this.cb.onOpen(s,r),onShowNode:s=>this.cb.onShowNode(s),onShowEdge:s=>this.cb.onShowEdge(s),onShowIssue:s=>this.cb.onShowIssue(s),onChallenge:()=>this.cb.onChallenge(),onRefine:()=>this.cb.onRefine(),onShowLimitations:()=>this.cb.onShowLimitations()});let i=(e.selectedIssue?"issue:"+e.selectedIssue.id:e.selectedEdge?"edge:"+e.selectedEdge.id:e.selectedNode?"node:"+e.selectedNode.id:"")+"@"+(e.document&&e.document.revision?e.document.revision.id:"");i!==this.selectionKey&&(this.selectionKey=i,n.scrollTop=0)}renderOutline(e){let n=this.panels.get("outline");D(n),m(n,f("h3","mlv-sr","Outline"));let o=e.index;if(!o){m(n,f("div","mlv-empty-note","No workflow loaded yet."));return}e.selectedNode&&(this.relationNodeId=e.selectedNode.id);let i=this.relationNodeId&&o.nodeById.has(this.relationNodeId)?this.relationNodeId:null;Xl(n,{index:o,keep:e.keep,selectedNodeId:e.selectedNode?e.selectedNode.id:i,collapsed:e.collapsed,relationMode:this.relationMode},{onSelectNode:(s,r)=>this.cb.onSelectNode(s,r),onOpenNode:(s,r)=>this.cb.onOpenNode(s,r),onSelectEdge:s=>this.cb.onSelectEdge(s),onRelationMode:s=>{this.relationMode=s},onSelectLane:s=>this.cb.onSelectLane(s),onToggleCollapse:s=>this.cb.onToggleCollapse(s)})}};var Xg=[{group:"basis",key:"observed",label:"Observed",detail:"Directly supported by cited workspace source. Most claims are observed, so they carry no mark."},{group:"basis",key:"inferred",label:"Inferred",detail:'Reasoned from cited source and stated assumptions; the quotes do not show all of it. Dashed border or line, and an "inferred" tag.'},{group:"basis",key:"unresolved",label:"Unresolved",detail:'The available evidence does not settle this claim. It does not mean the step is absent. Dotted border or line, faint hatching, and a "? unresolved" tag.'}],Yg=[{group:"edge",key:"arrow",label:"connection",detail:"Points from the step that produces something to the step that uses it. The line style shows the basis above, not the kind."},{group:"edge",key:"back",label:"loop back",detail:"The return leg of a loop, marked with a chevron."},{group:"edge",key:"kinds",label:"kind",detail:"What a connection carries (data, call, state…) is written in its hover card and the Selection tab."}],Zg=[{group:"phase",key:"order",label:"phase colour",detail:"Each phase gets a colour by its place in the document, shown on the lane's left rule, the swatch before its number and name, the card's left edge, and its number in the phase index and the phase overview. It means nothing else. Problems are the only other colour on the diagram."},{group:"phase",key:"index",label:"phase index",detail:`The list in the lower right corner of the diagram: each phase's number, name, the findings that touch it and its step count, with the phases in view marked. A row goes to that phase. Beside the code it is one line, such as "4/6 Data loading", that opens the list. Shift+0 shows every phase as a block of its step titles (the phase overview).`}],Qg=[{group:"freshness",key:"verified",label:"Unchanged",detail:"The status bar counts the cited files that still match the hashes published with this revision. Unchanged means the quoted lines still exist; it does not prove the interpretation."},{group:"freshness",key:"draft",label:"Not checked",detail:"This revision has no published source hashes, so the status bar says freshness is not checked."},{group:"freshness",key:"stale",label:"Changed or missing",detail:"A cited file no longer matches the published revision. Cards, connections, findings and quotes that cite it carry this mark and a border in the warning colour, and their jumps are blocked. It does not say whether the claim is still right. When the notice above says the workspace root is the wrong folder, the mark means the file is unchanged in another folder."}];function $g(){return[{id:"basis",title:"Claim basis",rows:Xg},{id:"severity",title:"Findings",rows:ne.map(t=>({group:"severity",key:t,label:ye[t],detail:t==="high"?"High potential impact if the finding is correct.":t==="medium"?"Medium potential impact if the finding is correct.":"Low potential impact if the finding is correct. Severity does not express certainty."})).concat([{group:"severity",key:"short",label:"numbers",detail:"Findings numbered in document order. A new revision can renumber them; the hover card, the Selection tab and Refine prompts use the real id."}])},{id:"edges",title:"Connections",rows:Yg},{id:"phases",title:"Phases",rows:Zg},{id:"freshness",title:"Source freshness",rows:Qg}]}function md(t,e=!1){let n=H("svg",{class:"mlv-legend__swatch",viewBox:"0 0 44 14",width:44,height:14,"aria-hidden":"true"}),o=H("g",{class:"mlv-edge mlv-edge--data"+(e?" mlv-edge--back":"")});return o.setAttribute("data-basis",t),o.appendChild(H("path",{class:"mlv-legend__edge",d:"M2 7 H 33"})),o.appendChild(H("path",{class:"mlv-arrow",d:Pt.d,transform:"translate(31,2)"})),e&&o.appendChild(H("path",{class:"mlv-edge__loopmark",d:"M22 3 L18 7 L22 11",fill:"none",stroke:"var(--mlv-edge)","stroke-width":1.3,"stroke-linecap":"round"})),n.appendChild(o),n}function Jg(t){let e=f("span","mlv-legend__basis"),n=m(e,f("span","mlv-legend__card"));n.setAttribute("data-basis",t);let o=fe(t);return o&&m(n,f("span","mlv-legend__tag",o)).setAttribute("data-basis",t),e.appendChild(md(t)),e}function ev(){let t=f("span","mlv-legend__phases");t.setAttribute("aria-hidden","true");for(let e=0;e{this.setOpen(!1),e(!1)}),n.appendChild(this.toggleBtn),this.body=m(this.root,f("div","mlv-legend__body"));for(let o of $g()){let i=m(this.body,f("section","mlv-legend__section"));i.setAttribute("data-legend-section",o.id),m(i,f("h3","mlv-legend__heading",o.title));let s=m(i,f("dl","mlv-legend__list"));for(let r of o.rows){let a=m(s,f("dt","mlv-legend__term"));a.setAttribute("data-legend-row",r.group+":"+r.key);let l=tv(r);a.appendChild(l);let d=r.group==="basis";!d&&(l.textContent||"").trim()!==r.label&&m(a,f("span","mlv-legend__label",r.label));let c=m(s,f("dd","mlv-legend__desc"));d&&m(c,f("strong","mlv-legend__label",r.label+". ")),c.appendChild(c.ownerDocument.createTextNode(r.detail))}}this.setOpen(!1)}get open(){return this.openState}setKinds(e,n){let o=this.root.querySelector('[data-legend-row="edge:kinds"]'),i=o&&o.nextElementSibling;if(!i)return;let s=i.querySelector(".mlv-legend__authored");s&&s.remove();let r=[];if(e.length){let a=e.slice(0,8).map(l=>l.kind+" "+l.count).join(", ");r.push("Connections in this diagram, by kind: "+a+(e.length>8?", and "+(e.length-8)+" more kinds":"")+".")}n>0&&r.push(n+(n===1?" connection has":" connections have")+" no kind."),r.length&&m(i,f("span","mlv-legend__authored"," "+r.join(" ")))}setOpen(e){this.openState=e,this.root.hidden=!e}toggle(){return this.setOpen(!this.openState),this.openState}};var gd=[{keys:["Mod+F","/"],action:"focusSearch",description:"Search steps, findings, IDs, or cited text"},{keys:["n","p"],action:"cycleIssue",description:"Next / previous finding (document order); in the review walk, in the walk's order"},{keys:["Enter"],action:"open",description:"Open the cited source beside the diagram; focus stays here (in the review walk: open the current quote again)"},{keys:["Alt+Enter"],action:"openFocus",description:"Open the cited source and move focus to the editor"},{keys:["Space"],action:"collapse",description:"Collapse or expand the selected group"},{keys:["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"],action:"move",description:"Move the selection (from a connection, to its end that lies that way; in the review walk, ↓ and ↑ step through the claims)"},{keys:["r"],action:"review",description:"Review walk: go claim by claim, each opened and highlighted in the editor beside (starts on the claims not observed; r again or Escape ends it)"},{keys:["j","k"],action:"walkStep",description:"In the review walk: next / previous claim"},{keys:["[","]"],action:"walkQuote",description:"In the review walk: previous / next quote of the claim"},{keys:["u","Shift+U"],action:"walkNotObserved",description:"Review walk on the claims not observed: start it, then next / previous one"},{keys:["0"],action:"fit",description:"Readable view: the whole diagram if it fits at reading size, otherwise phase 1"},{keys:["Shift+0"],action:"overview",description:"Phase overview: every phase as a block of its step titles; arrows move between phases, Enter goes to one, Escape comes back"},{keys:["+","="],action:"zoomIn",description:"Zoom in"},{keys:["-","_"],action:"zoomOut",description:"Zoom out"},{keys:["z"],action:"zoomToSelection",description:"Zoom to the selection"},{keys:["f"],action:"focusMode",description:"Focus mode: light the full lineage of the selection"},{keys:["l"],action:"legend",description:"Show or hide the legend"},{keys:["a"],action:"flow",description:"Turn the connection flow animation on or off"},{keys:["Shift+A"],action:"replayFlow",description:"Play the connection flow again (it stops after two passes)"},{keys:["e","Shift+E"],action:"cycleConnections",description:"Next / previous connection of the selected step"},{keys:["1","2","3"],action:"toggleSeverity",description:"Toggle the high / medium / low filters"},{keys:["b"],action:"toggleRail",description:"Show or hide the side panel; open or collapse the bottom panel"},{keys:["t"],action:"railTab",description:"Go to the panel tabs, About / Findings / Selection / Outline (opens the panel); ← and → pick a tab"},{keys:["?"],action:"shortcuts",description:"Show this shortcut sheet"},{keys:["Escape"],action:"escape",description:"Close the menu, this sheet, the Refine popover, the phase list or the legend, leave the phase overview, end the review walk, collapse the bottom panel, exit focus mode, clear the selection, leave the canvas"},{keys:["Tab","Shift+Tab"],action:"browser",description:"Move focus out of the diagram (never intercepted)"}];function vd(t,e){let n=t.key,o=t.ctrlKey||t.metaKey,i=()=>(t.preventDefault(),!0);if(n==="Tab")return!1;if(o)return n==="f"||n==="F"?(e.focusSearch(),i()):!1;if(t.altKey&&n==="Enter"&&!t.shiftKey)return e.openSelection(!0)?i():!1;if(t.altKey)return!1;if(n==="/")return e.focusSearch(),i();if(n==="?")return e.toggleShortcuts(),i();if(n==="Escape")return e.escape()?i():!1;if(n==="r"||n==="R")return e.review(),i();if(n==="u"||n==="U")return e.walkNotObserved(t.shiftKey)?i():!1;if(e.walking()){let s=n==="j"||n==="J"||n==="ArrowDown"?1:n==="k"||n==="K"||n==="ArrowUp"?-1:0;if(s)return e.walkStep(s)?i():!1;if(n==="["||n==="]")return e.walkQuote(n==="]"?1:-1)?i():!1}return n==="n"||n==="N"||n==="p"||n==="P"?e.cycleIssue(n==="p"||n==="P")?i():!1:n==="+"||n==="="?(e.zoom(1),i()):n==="-"||n==="_"?(e.zoom(-1),i()):n===")"||n==="0"&&t.shiftKey?(e.overview(),i()):n==="0"?(e.fit(),i()):n==="1"||n==="2"||n==="3"?(e.toggleSeverity(Number(n)-1),i()):n==="f"||n==="F"?(e.toggleFocusMode(),i()):n==="l"||n==="L"?(e.toggleLegend(),i()):n==="a"||n==="A"?t.shiftKey?e.replayFlow()?i():!1:(e.toggleFlow(),i()):n==="e"||n==="E"?e.cycleConnections(t.shiftKey)?i():!1:n==="z"||n==="Z"?(e.zoomToSelection(),i()):n==="b"||n==="B"?(e.toggleRail(),i()):n==="t"||n==="T"?(e.focusRailTabs(),i()):n===" "?e.toggleCollapse()?i():!1:n==="Enter"?e.openSelection(!1)?i():!1:n.indexOf("Arrow")===0?(e.move(n),i()):!1}var mo=class{constructor(e){this.opener=null;this.root=f("div","mlv-sheet"),this.root.hidden=!0;let n=m(this.root,f("div","mlv-sheet__scrim"));_(n,"click",()=>e());let o=m(this.root,f("div","mlv-sheet__panel"));this.panel=o,o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.setAttribute("aria-label","Keyboard shortcuts");let i=m(o,f("div","mlv-sheet__head"));m(i,f("h2","mlv-sheet__title","Keyboard shortcuts")),this.closeBtn=he("mlv-btn mlv-btn--icon","Close shortcuts"),this.closeBtn.appendChild(B("close")),_(this.closeBtn,"click",()=>e()),i.appendChild(this.closeBtn),_(this.root,"keydown",l=>{if(an(this.panel,l)){l.stopPropagation();return}l.key==="Escape"&&(l.preventDefault(),l.stopPropagation(),e())});let s=Xi();m(o,f("p","mlv-sheet__note","Click a card, connection, finding or Outline row to select it and read its claim. Double-click a step, connection, finding or Outline step, or press Enter on it, to open the cited source beside the diagram; focus stays here. Double-click a group to collapse it. "+(s?"Option":"Alt")+"+click an Open link, or press "+ot("Alt+Enter",s)+", to move focus to the editor. From the code: right-click a cited line in the editor and choose Reveal in Diagram to show the claim that cites it here."));let a=m(o,f("dl","mlv-sheet__list"));for(let l of gd){let d=m(a,f("dt","mlv-sheet__keys"));for(let c of l.keys)m(d,f("kbd","mlv-kbd",ot(c,s)));m(a,f("dd","mlv-sheet__desc",l.description))}}get open(){return!this.root.hidden}show(){if(!this.open){let e=this.root.ownerDocument,n=e?e.activeElement:null;this.opener=n&&!this.root.contains(n)?n:null}this.root.hidden=!1;try{this.closeBtn.focus()}catch{}}hide(e=null){let n=this.open;if(this.root.hidden=!0,!n)return;let o=this.opener;this.opener=null,At(o,e)}toggle(){return this.open?this.hide():this.show(),this.open}};function bd(t,e){return(t.id||"mlv-search-results")+"-opt-"+e}var nv=52;function ov(t,e=nv){return t.length<=e?t:"…"+t.slice(t.length-(e-1))}var us=(t,e,n)=>t+" "+(t===1?e:n);function iv(t){let e=[];return t.totalNodes&&e.push(us(t.totalNodes,"step","steps")),t.totalIssues&&e.push(us(t.totalIssues,"finding","findings")),us(t.total,"match","matches")+(e.length?": "+e.join(", "):"")}function wd(t,e,n){let{query:o,result:i,cursor:s,onPick:r}=n,a=i.hits;if(D(t),!o){t.hidden=!0,e.setAttribute("aria-expanded","false"),e.removeAttribute("aria-activedescendant");return}if(t.hidden=!1,e.setAttribute("aria-expanded","true"),!a.length){m(t,f("li","mlv-result__empty","No matches")),e.removeAttribute("aria-activedescendant");return}let l=m(t,f("li","mlv-result__count",iv(i)));if(l.setAttribute("role","presentation"),l.setAttribute("data-search-count",String(i.total)),a.forEach((d,c)=>{let u=m(t,f("li"));u.setAttribute("role","presentation");let h=f("button","mlv-result");if(h.type="button",h.id=bd(t,c),h.setAttribute("role","option"),h.tabIndex=-1,h.setAttribute("aria-selected",c===s?"true":"false"),d.severity&&h.appendChild(Z(d.severity,12,"")),m(h,f("span","mlv-result__label",d.label)),d.meta){let p=m(h,f("span","mlv-result__meta",ov(d.meta)));p.textContent!==d.meta&&(p.title=d.meta)}if(d.location){let p=d.location.line===null?"first in file":"line "+d.location.line;m(h,f("span","mlv-result__jump",d.location.exact?"jump to "+p:"nearest to "+p)).setAttribute("data-search-jump",d.location.exact?"exact":"nearest"),u.setAttribute("data-search-pinned","1")}_(h,"click",()=>r(d)),u.appendChild(h)}),i.truncated){let d=m(t,f("li","mlv-result__foot"));d.setAttribute("role","presentation"),d.setAttribute("data-search-truncated",String(i.total)),m(d,f("span","","showing "+a.length+" of "+i.total));let c=U("mlv-link mlv-link--inline","Show more","Show more search results");c.setAttribute("data-search-more","1"),_(c,"click",u=>{u.preventDefault(),n.onShowMore()}),d.appendChild(c)}s>=0&&s|]+\.[A-Za-z][A-Za-z0-9_]{0,7}$/;function Ed(t){let e=(t||"").trim();if(!e)return null;let n=/^(.+?):(\d+)(?::\d+)?$/.exec(e);if(n){let o=n[1].trim(),i=Number(n[2]);return!xd.test(o)||!isFinite(i)||i<1?null:{path:hs(o),line:i}}return xd.test(e)?{path:hs(e),line:null}:null}function hs(t){let e=t.split("\\").join("/");for(;e.indexOf("./")===0;)e=e.slice(2);return e}function sv(t,e){let n=hs(t);return n===e?!0:n.length>e.length&&n.slice(n.length-e.length-1)==="/"+e}function kd(t,e){let n=t.graph.nodes||[],o=null;for(let i of n){if(!i.loc||!sv(i.loc.file,e.path))continue;let s=i.loc.line,r=Math.max(s,i.loc.endLine||s),a=r-s,l,d;e.line===null?(l=!1,d=s):(l=e.line>=s&&e.line<=r,d=l?0:e.lineg.hit),p=av(t,e);if(p){let g=h.findIndex(v=>v.kind==="node"&&v.id===p.id);g>=0&&h.splice(g,1),h.unshift(p),h.length>n&&(h.length=n)}return{hits:h,total:i.length+s.length,totalNodes:i.length,totalIssues:s.length,limit:n,truncated:i.length+s.length>h.length}}function av(t,e){let n=Ed(e);if(!n||n.line===null)return null;let o=kd(t,n);if(!o)return null;let i=o.node;return{kind:"node",id:i.id,label:i.label||i.qualname,meta:dt(i.loc),stage:i.stage,location:{path:n.path,line:n.line,exact:o.contains}}}function fs(t){t.sort((e,n)=>e.score-n.score||e.at-n.at)}var Xt=40,ps={hits:[],total:0,totalNodes:0,totalIssues:0,limit:Xt,truncated:!1},go=class{constructor(e,n,o){this.result=ps;this.limit=Xt;this.cursor=-1;this.input=e,this.results=n,this.host=o}run(e){this.limit=Xt,this.execute(e),this.host.onQueryChanged(e)}execute(e){let n=this.host.index();this.result=n?Sd(n,e,this.limit):ps,this.cursor=this.result.hits.length?0:-1,this.render()}showMore(){this.limit+=Xt,this.execute(this.input.value)}get hits(){return this.result.hits}clear(){this.input.value="",this.result=ps,this.limit=Xt,this.cursor=-1,this.render()}focus(){this.input.focus(),this.input.select()}hideResults(){this.results.hidden=!0}handleKey(e){if(e.key==="Escape"){e.preventDefault(),this.clear(),this.host.onQueryChanged(""),this.host.blurToCanvas();return}let n=this.result.hits;if(n.length){if(e.key==="ArrowDown"||e.key==="ArrowUp")e.preventDefault(),this.cursor=yd(this.cursor,n.length,e.key==="ArrowDown"?1:-1),this.render();else if(e.key==="Enter"){e.preventDefault();let o=n[Math.max(0,this.cursor)];o&&(this.host.activate(o),this.hideResults())}}}render(){wd(this.results,this.input,{query:this.input.value,result:this.result,cursor:this.cursor,onPick:e=>{this.host.activate(e),this.hideResults()},onShowMore:()=>this.showMore()})}};var lv=new Set(["invalid","parse","missing","unreadable","obsolete","same-id-changed"]),dv=new Set(["stale","root-hint"]),vo=class{constructor(e){this.reviewCount=0;this.walking=!1;this.message="";this.codes=[];this.cb=e,this.root=f("div","mlv-hostnotice"),this.root.setAttribute("role","status"),this.root.hidden=!0,this.icon=m(this.root,f("span","mlv-hostnotice__icon")),this.icon.setAttribute("aria-hidden","true"),this.text=m(this.root,f("p","mlv-hostnotice__text")),this.actions=m(this.root,f("div","mlv-hostnotice__actions")),this.review=U("mlv-btn mlv-hostnotice__review","Review affected claims"),this.review.setAttribute("data-notice-action","review"),_(this.review,"click",()=>this.cb.onReviewAffected())}setReview(e,n){e===this.reviewCount&&n===this.walking||(this.reviewCount=e,this.walking=n,this.syncActions())}update(e,n){let o=n||[],i=o.some(s=>lv.has(s))?"error":o.some(s=>dv.has(s))?"warn":"info";this.root.hidden=!e,this.root.className="mlv-hostnotice mlv-hostnotice--"+i,this.root.setAttribute("data-codes",o.join(" ")),this.text.textContent=e,D(this.icon),i!=="info"&&this.icon.appendChild(B("warning",14)),this.message=e,this.codes=o,this.syncActions()}syncActions(){let e=this.message,n=this.codes;if(D(this.actions),this.actions.hidden=!0,e&&n.indexOf("stale")>=0&&this.reviewCount>0&&!this.walking&&(this.review.title="Walk the "+this.reviewCount+(this.reviewCount===1?" claim":" claims")+" whose quotes cite a file that changed or went missing; nothing stale is opened",this.actions.appendChild(this.review),this.actions.hidden=!1),e&&n.indexOf("root-hint")>=0){let o=U("mlv-btn mlv-btn--primary","Add folder to workspace");o.setAttribute("data-workspace-hint","add"),_(o,"click",()=>this.cb.onWorkspaceHint("add"));let i=U("mlv-btn","Open folder");i.setAttribute("data-workspace-hint","open"),i.title="Open the folder the citations resolve against in a new window",_(i,"click",()=>this.cb.onWorkspaceHint("open")),this.actions.append(o,i),this.actions.hidden=!1}}};var cv=new Set(["j","J","k","K","[","]","u","U","n","N","p","P"]),bo=class{constructor(e){this.filters=new Map;this.root=f("section","mlv-walkbar"),this.root.setAttribute("aria-label","Review walk"),this.root.hidden=!0;let n=m(this.root,f("div","mlv-walkbar__row"));this.pos=m(n,f("p","mlv-walkbar__pos")),this.posMark=m(this.pos,f("span","mlv-walkbar__mark")),this.posMark.setAttribute("aria-hidden","true"),this.posMark.appendChild(B("warning",12)),this.posMark.hidden=!0,this.posText=m(this.pos,f("span","mlv-walkbar__postext")),this.posSpoken=m(this.pos,f("span","mlv-sr mlv-walkbar__posspoken")),this.tools=m(n,f("div","mlv-walkbar__tools")),this.tools.setAttribute("role","toolbar"),this.tools.setAttribute("aria-label","Review walk controls");let o=m(this.tools,f("div","mlv-walkbar__steps"));o.setAttribute("role","group"),o.setAttribute("aria-label","Step through the claims");for(let[s,r,a]of[[-1,"Previous claim (k)","prev"],[1,"Next claim (j)","next"]]){let l=U("mlv-btn mlv-btn--icon mlv-walkbar__step mlv-walkbar__step--"+a,"",r);l.setAttribute("data-walk-step",a),l.title=r+(s<0?", or ↑":", or ↓"),l.appendChild(B("chevron",12)),_(l,"click",()=>e.onStep(s)),o.appendChild(l)}let i=m(this.tools,f("div","mlv-walkbar__filters"));i.setAttribute("role","group"),i.setAttribute("aria-label","Which claims to walk");for(let s of kt){let r=U("mlv-walkbar__filter","");r.setAttribute("data-walk-filter",s),r.setAttribute("aria-pressed","false"),m(r,f("span","mlv-walkbar__flabel",lo[s])),m(r,f("span","mlv-walkbar__fcount","0")),_(r,"click",()=>e.onFilter(s)),this.filters.set(s,r),i.appendChild(r)}this.keys=m(this.tools,f("span","mlv-walkbar__keys")),this.keys.setAttribute("aria-hidden","true");for(let[s,r]of[["j k","step"],["[ ]","quotes"],["Enter","opens again"],["Esc","exits"]]){let a=m(this.keys,f("span","mlv-walkbar__key"));for(let l of s.split(" "))m(a,f("kbd","mlv-kbd",l));m(a,f("span",""," "+r))}this.exit=U("mlv-btn mlv-walkbar__exit","","Exit the review (Escape)"),this.exit.appendChild(B("close",12)),m(this.exit,f("span","mlv-walkbar__exitlabel","Exit")),this.exit.title="Exit the review (Escape). The walk remembers its place for this revision.",_(this.exit,"click",()=>e.onExit()),this.tools.appendChild(this.exit),this.editor=m(this.root,f("p","mlv-walkbar__editor")),this.editorIcon=m(this.editor,f("span","mlv-walkbar__editoricon")),this.editorIcon.setAttribute("aria-hidden","true"),this.editorIcon.appendChild(B("warning",12)),this.editorText=m(this.editor,f("span","mlv-walkbar__editortext")),_(this.root,"keydown",s=>{if(!(s.defaultPrevented||s.ctrlKey||s.metaKey||s.altKey)){if(s.key==="Escape"){s.preventDefault(),s.stopPropagation(),e.onExit();return}cv.has(s.key)&&e.onKey(s)&&s.preventDefault()}}),this.roving=new xt(this.tools)}get shown(){return!this.root.hidden}update(e){if(!e){this.root.hidden=!0;return}this.root.hidden=!1,this.root.setAttribute("data-layout",e.narrow?"narrow":"wide");let n=e.status.state==="blocked"||e.status.state==="failed";this.root.setAttribute("data-walk-status",e.status.state);let o=lo[e.filter],i=e.total?"Claim "+(e.position+1)+" of "+e.total:"No claims";this.posText.textContent=e.narrow?e.total?e.position+1+"/"+e.total:"0/0":i+" · "+o,this.posMark.hidden=!(n&&e.narrow);let s=sd(e.status);this.pos.title=i+" · "+o+(e.narrow?". "+s:""),e.narrow?this.posText.setAttribute("aria-hidden","true"):this.posText.removeAttribute("aria-hidden"),this.posSpoken.textContent=e.narrow?i+", "+o+(n?". "+s:""):"",this.posSpoken.hidden=!e.narrow;for(let[r,a]of this.filters){let l=e.offered.indexOf(r)>=0;a.hidden=!l,a.setAttribute("aria-pressed",r===e.filter?"true":"false");let d=a.querySelector(".mlv-walkbar__fcount");d&&(d.textContent=String(e.counts[r]));let c=e.counts[r];a.setAttribute("aria-label",co(r,c)),a.title=uv(r,c)}this.keys.hidden=e.narrow,this.editor.hidden=e.narrow,this.editor.setAttribute("data-walk-status",e.status.state),this.editorIcon.hidden=!n,this.editorText.textContent=s,this.roving.sync()}focus(){let e=Array.from(this.tools.querySelectorAll("button")).find(n=>n.tabIndex===0&&!n.hidden);try{(e||this.exit).focus()}catch{}}destroy(){this.roving.destroy(),D(this.root)}};function uv(t,e){let n=e+(e===1?" claim":" claims");return t==="notObserved"?"Walk the "+n+` the author marked inferred or unresolved (the header's "not observed" count)`:t==="findings"?"Walk the "+n+" that are findings":t==="changed"?"Walk the "+n+" whose quotes cite a file that changed or went missing since publishing":"Walk all "+n+": every step, connection and finding"}var Md={bg:"--mlv-bg",surface:"--mlv-surface",surface2:"--mlv-surface-2",border:"--mlv-border",borderStrong:"--mlv-border-strong",text:"--mlv-text",text2:"--mlv-text-2",text3:"--mlv-text-3",link:"--mlv-link",accent:"--mlv-accent",edge:"--mlv-edge",sevHigh:"--mlv-sev-high",sevMedium:"--mlv-sev-medium",sevLow:"--mlv-sev-low",sevHighInk:"--mlv-sev-high-ink",sevMediumInk:"--mlv-sev-medium-ink",sevLowInk:"--mlv-sev-low-ink",stageUnknown:"--mlv-stage-unknown",nodeEdge:"--mlv-node-edge"},hv=["--mlv-phase-0","--mlv-phase-1","--mlv-phase-2","--mlv-phase-3","--mlv-phase-4","--mlv-phase-5","--mlv-phase-6","--mlv-phase-7"],fv={light:.64,dark:.54,hc:1},pv={light:.58,dark:.46,hc:1},Ld={laneTint:"--mlv-lane-tint",groupTint:"--mlv-group-tint"},ms={bg:"#FBFBFD",surface:"#FFFFFF",surface2:"#F3F4F8",border:"#E3E5EB",borderStrong:"#C9CDD6",text:"#16181D",text2:"#5A6070",text3:"#676E81",link:"#2B57C4",accent:"#3B6CF6",edge:"#8C93A3",sevHigh:"#D0342C",sevMedium:"#E8A317",sevLow:"#2F5FD0",sevHighInk:"#FFFFFF",sevMediumInk:"#3A2500",sevLowInk:"#FFFFFF",stageUnknown:"#7A8090",nodeEdge:"",phases:["#00796B","#6A4FB3","#0A693C","#A0388F","#558B2F","#0B7F99","#546E7A","#1F6FB2"],laneTint:.025,groupTint:0},mv={bg:"#131417",surface:"#1A1C21",surface2:"#22252C",border:"#2C3038",borderStrong:"#3B414C",text:"#E6E8EE",text2:"#9AA1B1",text3:"#969DAD",link:"#8FB0FF",accent:"#6E96FF",edge:"#79808F",sevHigh:"#FF6169",sevMedium:"#F2B03C",sevLow:"#6E96FF",sevHighInk:"#1A0405",sevMediumInk:"#2A1A00",sevLowInk:"#0B1020",stageUnknown:"#8B93A7",phases:["#4DB6AC","#A48BE0","#4CA871","#D684C5","#9CCC65","#4FC3D9","#90A4AE","#6FA8DC"],laneTint:.035,groupTint:0},gv={phases:["#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF"],bg:"#000000",surface:"#000000",surface2:"#000000",border:"#FFFFFF",borderStrong:"#FFFFFF",text:"#FFFFFF",text2:"#FFFFFF",text3:"#FFFFFF",link:"#6BB7FF",edge:"#FFFFFF",laneTint:0,groupTint:0},Id={light:wo({...ms},"light"),dark:wo({...ms,...mv},"dark"),hc:wo({...ms,...gv},"hc")};function vv(t){let e=Id[t]||Id.light;return{...e,phases:e.phases.slice()}}function vs(t,e){if(e===void 0||!t.phases.length)return t.stageUnknown;let n=t.phases.length;return t.phases[(e%n+n)%n]||t.stageUnknown}function wo(t,e){return e==="hc"?(t.edge=t.border,t.nodeEdge=t.border,t):(t.edge=gs(t.text,t.bg,fv[e])||t.edge,t.nodeEdge=gs(t.text,t.bg,pv[e])||t.border,t)}function Ad(t){let e=t.trim().toLowerCase(),n=/^#([0-9a-f]{3,8})$/.exec(e);if(n){let i=n[1];return i.length===3||i.length===4?[0,1,2].map(s=>parseInt(i[s]+i[s],16)):i.length===6||i.length===8?[0,2,4].map(s=>parseInt(i.slice(s,s+2),16)):null}let o=/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/.exec(e);return o?[Number(o[1]),Number(o[2]),Number(o[3])].map(i=>Math.max(0,Math.min(255,i))):null}function bv(t){return"#"+t.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("").toUpperCase()}function gs(t,e,n){let o=Ad(t),i=Ad(e);return!o||!i?"":bv(o.map((s,r)=>s*n+i[r]*(1-n)))}function wv(t){let e=/^color-mix\(\s*in srgb\s*,\s*(.+?)\s+([\d.]+)%\s*,\s*(.+?)\s*\)$/i.exec(t.trim());return e?gs(e[1],e[3],Number(e[2])/100):""}function at(t,e){return e==="high"?t.sevHigh:e==="medium"?t.sevMedium:e==="low"?t.sevLow:t.edge}function Td(t,e){return e==="high"?t.sevHighInk:e==="medium"?t.sevMediumInk:t.sevLowInk}function Rd(t,e){let o={...vv(e)},i=yv(t);if(!t||!i||typeof i.getComputedStyle!="function")return o;let s;try{s=i.getComputedStyle(t)}catch{return o}if(!s||typeof s.getPropertyValue!="function")return o;let r=new Set;for(let l of Object.keys(Md)){let d=Cd(s.getPropertyValue(Md[l]));d&&(o[l]=d,r.add(l))}hv.forEach((l,d)=>{let c=Cd(s.getPropertyValue(l));c&&(o.phases[d]=c)});let a=wo({...o},e);r.has("edge")||(o.edge=a.edge),r.has("nodeEdge")||(o.nodeEdge=a.nodeEdge);for(let l of Object.keys(Ld)){let d=Od(s.getPropertyValue(Ld[l])),c=d?Number(d):NaN;isFinite(c)&&c>=0&&c<=1&&(o[l]=c)}return o}function yv(t){if(!t)return null;let e=t.ownerDocument;return e?e.defaultView:null}function Cd(t){let e=(t||"").trim();return e.toLowerCase().startsWith("color-mix(")?wv(e):Od(e)}function Od(t){let e=(t||"").trim();return!e||e.indexOf("var(")>=0||e.indexOf("color-mix(")>=0?"":e}var bs="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",Zt="ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",xv=.51,Ev=.55,kv=.6;function re(t,e,n,o){return t?'"+R(t)+"":""}function ws(t,e,n,o,i,s){return''}function ie(t,e,n,o){let i=n?kv:o?Ev:xv;return t.length*e*i}function Ue(t,e,n,o,i){if(ie(t,n,o,i)<=e)return t;let s=t.length;for(;s>1&&ie(t.slice(0,s)+"…",n,o,i)>e;)s--;return t.slice(0,Math.max(1,s))+"…"}function yo(t,e,n,o,i,s){let r=ee(t),a=o/18,l=at(i,r),d=s?'':'',c='';return''+d+c+""}function Hd(t,e,n,o,i,s=[]){let r=oe(t);if(!r)return"";let a=Y(t),l=s.length?Bo(s):a>1?String(a):"",d=26+(l?ie(l,10.5,!1,!0)+4:0),c=e-d,u=['',yo(r,c+4,n+1,18,o,i)];return l&&u.push(re(l,c+24,n+14,{size:10.5,fill:o.text2,weight:650})),u.join("")}function xo(t,e,n,o,i,s){let r=[];for(let c of ne)t[c]>0&&r.push({sev:c,n:t[c]});if(!r.length)return"";let a=0;for(let c of r)a+=o+2+ie(String(c.n),10.5,!1,!1)+6;let l=e-a,d=[];for(let c of r)d.push(yo(c.sev,l,n-o/2,o,i,s)),l+=o+2,d.push(re(String(c.n),l,n+3.5,{size:10.5,fill:i.text2})),l+=ie(String(c.n),10.5,!1,!1)+6;return d.join("")}function Pd(t){if(!t.length)return{x:0,y:0,w:0,h:0};let e=t[0].x,n=t[0].y,o=t[0].x,i=t[0].y;for(let s of t)s.xo&&(o=s.x),s.y>i&&(i=s.y);return{x:e,y:n,w:o-e,h:i-n}}function Nd(t,e){return{x:t.x-e,y:t.y-e,w:t.w+e*2,h:t.h+e*2}}function Qt(t,e){return t.xe.x&&t.ye.y}function Fd(t){return{x:Yt(t.x),y:Yt(t.y),w:Yt(Math.max(1,t.w)),h:Yt(Math.max(1,t.h))}}function Yt(t){return Math.round((isFinite(t)?t:0)*100)/100}function M(t){return String(Yt(t))}function R(t){let e="",n=String(t??"");for(let o=0;o=55296&&s<=56319){let r=o+1=56320&&r<=57343?(e+=i+n.charAt(o+1),o++):e+="�";continue}if(s>=56320&&s<=57343||s===65534||s===65535){e+="�";continue}i==="&"?e+="&":i==="<"?e+="<":i===">"?e+=">":i==='"'?e+=""":i==="'"?e+="'":i<" "&&i!==` +`&&i!==" "?e+=" ":e+=i}return e}var _v=10,_s=4,Kd=8,Sv=12,Mv=10,Ss=26,Lv=8,Bd=_s+Kd+Ss+Lv,Dd=13,Lt=11,ys=10.5,jd=10.5,$t=13,Iv=.05,Mt=10.5,xs=12,Av=10,Cv=22.5,Gd=38.5,Tv=52.5,Rv=22,Ov=15,Hv=98,Es=14,Wd=3,zd=6,Ms=14,Pv=10,Vd=10,Nv="6 4",Fv="1.5 3.5",Bv=12,ks=34;function Ud(t){let{plan:e,palette:n,theme:o}=t,i=o==="hc",s=Fd(t.region),r=[],a=[],l=[],d=[],c=[],u=[],h=0,p=k=>{u.indexOf(k)<0&&u.push(k)};r.push('');let g=[];for(let k of e.lanes){let E=k.lane,w={x:E.x,y:E.y,w:E.w,h:E.h};if(!Qt(w,s))continue;c.push(E.id);let S=vs(n,k.phase);p(S),g.push(Dv(E,k.counts,S,k.phase,n,i))}let v=[];for(let k of e.nodes){let E=k.visual.box,w={x:E.x-8,y:E.y-8,w:E.w+16,h:E.h+16};if(!Qt(w,s))continue;a.push(k.visual.node.id);let S=vs(n,k.visual.phase);p(S);let C=k.expandedGroup?zv(k.visual,n,i):jv(k.visual,S,n,i);k.expandedGroup?g.push(C):v.push(C)}let b=[];for(let k of e.edges){let E=k.route;if(!Qt(Nd(Pd(E.points),16),s))continue;l.push(E.id);for(let S of E.ids)d.push(S);let w=Vv(k,n,i);b.push(w.markup),w.labelled&&h++}r.push(''+g.join("")+""),r.push(''+b.join("")+""),r.push(''+v.join("")+"");let x='',y=""+R(t.title)+""+(t.desc?""+R(t.desc)+"":"");return{svg:` +`+x+y+r.join("")+` +`,width:s.w,height:s.h,region:s,nodeIds:a,edgeIds:l,edgeDocIds:d,laneIds:c,labels:h,stageColors:u}}function Dv(t,e,n,o,i,s){let r=[''];r.push(''),r.push(''),r.push(''),r.push('');let a=t.y+(t.headerH-8)/2;r.push('');let l=t.y+t.headerH/2+4.5,d=String(o+1),c=t.x+26;r.push(re(d,c,l,{size:$t,fill:i.text2,weight:650}));let u=c+ie(d,$t,!1,!0)+6,h=Y(e),p=h>0?"· "+Pe(h):"",g=p?ie(p,Mt,!1,!1):0,v=et(t.nodeCount),b=ie(v,Mt,!1,!1),x=Math.max(40,t.x+t.w-12-u-b-10-(p?g+96:0)),y=Ue(t.label||t.id,x,$t,!1,!0);r.push(re(y,u,l,{size:$t,fill:i.text,weight:650}));let k=u+ie(y,$t,!1,!0)+10;if(r.push(re(v,k,l,{size:Mt,fill:i.text3})),p){let E=t.x+t.w-12;r.push(re(p,E-g,l,{size:Mt,fill:i.text3})),r.push(xo(e,E-g-6,t.y+t.headerH/2,13,i,s))}return r.push(""),r.join("")}function jv(t,e,n,o){let i=t.node,s=t.box,r=s.collapsed,a=oe(t.counts),l=Ls(i.basis),c=[""];c.push(""+R(jt(t))+"");let u=t.stale?n.sevMedium:a?at(n,a):l?n.text2:n.nodeEdge;c.push('"),c.push('');let h=s.x+_s+Kd,p=s.y+Mv;c.push(''),c.push(ws(Nn(wt(i,r)),h+5,p+5,16/16,n.text2,1.3));let g=s.x+Bd,v=Math.max(24,s.w-Bd-Sv),b=i.label||i.qualname||i.id,x=wi(b,s.w);x.forEach((E,w)=>{c.push(re(Ue(E,v,Dd,!1,!0),g,s.y+Cv+w*18,{size:Dd,fill:n.text,weight:650}))});let y=(x.length-1)*18;if(!r&&Pi(i)===2)Wv(Bn(i),v,2).forEach((E,w)=>{c.push(re(E,g,s.y+Gd+y+w*Gv,{size:Lt,fill:n.text2}))});else{let E=Ue(Bn(i),v,Lt,!1,!1);c.push(re(E,g,s.y+Gd+y,{size:Lt,fill:n.text2}));let w=It(i.loc),S=ie(w.tail,ys,!0,!1),C=Ue(w.head,Math.max(12,v-S),ys,!0,!1)+w.tail;c.push(re(C,g,s.y+Tv+y,{size:ys,fill:n.text3,mono:!0}))}let k=r?[et(t.descendants)]:[];if(k.length&&s.h-y>=Hv){let E=s.y+s.h-Rv,w=g;for(let S of k){let C=ie(S,jd,!1,!1)+12;if(w+C>g+v)break;c.push(''),c.push(re(S,w+6,E+11,{size:jd,fill:n.text2})),w+=C+4}}return a&&!r?c.push(Hd(t.counts,s.x+s.w+6,s.y-6,n,o,t.findings||[])):r&&Y(t.counts)>0&&c.push(xo(t.counts,s.x+s.w+6,s.y+4,14,n,o)),c.push(Xd(i.basis,s.x+Pv,s.y+s.h-Ms/2,n)),c.push(""),c.join("")}var Gv=14.85;function Wv(t,e,n){let o=t.replace(/\s+/g," ").trim().split(" ").filter(Boolean),i=[],s="";for(let r=0;r'+re(i,e+4,n+3.5,{size:Vd,fill:o.text})+""}function zv(t,e,n){let o=t.node,i=t.box,s=oe(t.counts),r=Ls(o.basis),a=['"];a.push(""+R(jt(t))+""),a.push('");let l=i.y+(ks-14)/2;a.push(ws(Nn(wt(o,!1)),i.x+8,l,14/16,e.text2,1.3));let d=i.x+28,c=i.y+ks/2+4,u=Math.max(40,i.w-(s?170:96)),h=Ue(o.label||o.qualname,u,xs,!1,!0);a.push(re(h,d,c,{size:xs,fill:e.text,weight:650}));let p=et(t.descendants),g=ie(p,Mt,!1,!1)+12,v=d+ie(h,xs,!1,!0)+8;return a.push(''),a.push(re(p,v+6,c,{size:Mt,fill:e.text2})),a.push(Xd(o.basis,v+g+6,c-3.5,e)),s&&a.push(xo(t.counts,i.x+i.w-10,i.y+ks/2,13,e,n)),a.push(""),a.join("")}function Vv(t,e,n){let o=t.route,i=t.severity?at(e,t.severity):e.edge,s=Ls(t.basis),r=t.weight&&t.weight>1?Math.floor(t.weight):1,a=r>=2,l=a?Math.max(qd,dn(r)):qd,d=['"];d.push('"),d.push(Kv(o.points,i));let c=!1,u=t.placement,h=!u||!u.hidden&&u.always;if(o.label&&h){let g=u?u.x:o.mid.x,v=u?u.y:o.mid.y-8,b=u?u.text:o.label;d.push(''+R(b)+""),c=!0}let p=u?u.marker:o.mid;if(t.severity){let g=ee(t.severity);d.push(''),d.push(yo(g,p.x-7,p.y-7,14,e,n))}else o.back&&d.push('');return a&&d.push(qv(r,p,o.midAngle,!!t.severity||o.back,e)),d.push(""),{markup:d.join(""),labelled:c}}function qv(t,e,n,o,i){let s=Ot(t),r=un(t),a=cn(e,n,o);return''+R(s)+""}var qd=1.5;function Kv(t,e){let n=Pt;if(t.length<2)return"";let o=t[t.length-1],i=t[t.length-2];for(let l=t.length-2;l>=0;l--)if(t[l].x!==o.x||t[l].y!==o.y){i=t[l];break}let s=Math.atan2(o.y-i.y,o.x-i.x)*180/Math.PI,r="translate("+M(o.x)+","+M(o.y)+") rotate("+M(s)+") translate(-7.65,-4.5) scale(0.9)",a='fill="'+R(e)+'"';return'"}var Uv="image/svg+xml",Xv="image/png";function Yv(t,e,n){let o=n,i=Math.max(t,e);i>0&&i*o>16384&&(o=16384/i);let s=t*e;return s>0&&s*o*o>268435456&&(o=Math.sqrt(268435456/s)),o>=1?o:null}function Zv(t){return"data:"+Uv+";charset=utf-8,"+encodeURIComponent(t)}function Yd(t,e,n,o=2){return new Promise(i=>{if(typeof document>"u"||typeof Image>"u"){i(null);return}let s=Yv(e,n,o);if(s===null){i(null);return}let r=s,a,l;try{a=document.createElement("canvas"),a.width=Math.max(1,Math.round(e*r)),a.height=Math.max(1,Math.round(n*r)),l=typeof a.getContext=="function"?a.getContext("2d"):null}catch{i(null);return}if(!l||typeof a.toDataURL!="function"){i(null);return}let d=new Image,c=!1,u=h=>{c||(c=!0,i(h))};d.onload=()=>{try{l.setTransform(r,0,0,r,0,0),l.drawImage(d,0,0,e,n);let h=a.toDataURL(Xv),p=h.indexOf(","),g=p<0?"":h.slice(p+1);u(h==="data:,"||!g?null:{base64:g,width:a.width,height:a.height,scale:r})}catch{u(null)}},d.onerror=()=>u(null);try{d.src=Zv(t)}catch{u(null)}})}var Eo="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function Qv(t){let e=[];for(let n=0;n=55296&&o<=56319){let i=n+1=56320&&i<=57343?(o=65536+(o-55296<<10)+(i-56320),n++):o=65533}else o>=56320&&o<=57343&&(o=65533);o<128?e.push(o):o<2048?e.push(192|o>>6,128|o&63):o<65536?e.push(224|o>>12,128|o>>6&63,128|o&63):e.push(240|o>>18,128|o>>12&63,128|o>>6&63,128|o&63)}return new Uint8Array(e)}function $v(t){let e=typeof globalThis>"u"?{}:globalThis;if(typeof e.btoa=="function"){let o="";for(let i=0;i>2)+Eo.charAt((i&3)<<4|s>>4),n+=o+1>6):"=",n+=o+2!!o).join("-")+"."+e}function $d(t){return String(t.workspace.root||"")||"workflow"}function tb(t){return String(t).toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)}function Jd(t,e,n){return{v:1,type:"exportFile",kind:t,name:e,base64:n,data:n,suggestedName:e,scope:"all"}}function ec(t,e,n){return o=>{if(o.outcome==="done"){t.announce(e(o.name||n));return}if(o.outcome==="cancelled"){t.announce("Export cancelled.");return}let i="Export failed: "+(o.message||"the host did not say why");t.toast(i),t.announce(i)}}function tc(t,e,n){let o=Zd(e.svg);if(!o){t.toast("This browser could not encode the SVG.");return}let i=e.nodeIds.length,s=e.edgeIds.length;t.announce("Saving SVG…"),t.request(Jd("svg",n,o),ec(t,r=>"Exported "+i+" cards and "+s+" connections as "+r+".",n))}async function nc(t,e,n){let o=await nb(e);if(!o)return t.toast("Could not draw the PNG here — save the SVG instead."),t.announce("PNG export is not available in this host."),!1;let i=o.width+"×"+o.height;return t.announce("Saving PNG…"),t.request(Jd("png",n,o.base64),ec(t,s=>"Exported "+i+" PNG as "+s+".",n)),!0}async function oc(t,e){if(await ob(e.svg)){let s="SVG copied — "+e.nodeIds.length+" cards, "+e.edgeIds.length+" connections.";return t.toast(s),t.announce(s),!0}let o=e.nodeIds.length,i=e.edgeIds.length;return t.request({v:1,type:"copy",text:e.svg},s=>{if(s.outcome==="done"){let r="SVG copied — "+o+" cards, "+i+" connections.";t.toast(r),t.announce(r)}else t.announce("The SVG could not be copied.")}),!1}async function nb(t){try{return await Yd(t.svg,t.width,t.height,Jv)}catch{return null}}async function ob(t){let e=typeof navigator<"u"?navigator:null;if(!e||!e.clipboard||typeof e.clipboard.writeText!="function")return!1;try{return await e.clipboard.writeText(t),!0}catch{return!1}}function ib(t){let e=t.view.scenePlan();return!e||!t.graph?null:{plan:e,palette:Rd(t.root,t.themes.kind),theme:t.themes.kind,graph:t.graph,generatedAt:new Date().toISOString().slice(0,10)}}function ic(t,e){let n={post:s=>t.bridge.post(s),request:(s,r)=>{t.postRequest(s,r)},toast:s=>t.view.toast(s),announce:s=>t.announce(s)},o=ib(t);if(!o){t.view.toast("Nothing is drawn yet — there is nothing to export.");return}let i=Qd(o);e==="svg"?tc(n,i,Is(o,"svg")):e==="png"?nc(n,i,Is(o,"png")):e==="copy-svg"&&oc(n,i)}function sb(t){let e={steps:0,connections:0,findings:0};if(!t.graph)return e;for(let n of t.graph.nodes)ze(n.basis)&&e.steps++;for(let n of t.graph.edges)ze(n.basis)&&e.connections++;for(let n of t.graph.issues)ze(n.basis)&&e.findings++;return e}function rb(t){let e=Me();if(!t.graph)return e;for(let n of t.graph.issues)e[ee(n.severity)]++;return e}function X(t){let e=sb(t);t.exceptionsOn&&e.steps+e.connections+e.findings===0&&(t.exceptionsOn=!1,t.view.canvasEl.removeAttribute("data-exceptions")),t.chrome.update({graph:t.graph,document:t.graph?t.workflowDocument:null,flowOn:t.flowOn,legendOpen:t.legendOpen,hasSelection:!!t.selection,filters:t.filters.value,visibleCounts:rb(t),phaseIndexShown:!t.view.phaseIndexHidden,phaseIndexUnavailable:t.view.phaseIndexUnavailable(),overviewOpen:t.view.overviewOpen,railOpen:t.railOpen,freshness:qs(t.workflowDocument,t.freshness),checking:t.freshness.checking,notObserved:e,exceptionsOn:t.exceptionsOn,railMode:t.railMode,walking:!!t.walk&&t.walk.active})}function Q(t){let e=t.selection,n=t.selectedNodeId(),o=n&&t.index&&t.index.nodeById.get(n)||null,i=e&&e.kind==="edge"&&t.index&&t.index.edgeById.get(e.id)||null,s=e&&e.kind==="issue"&&t.index&&t.index.issueById.get(e.id)||null;t.paneColumns=t.selectionColumns(),t.rail.update({index:t.index,tab:t.railTab,issues:t.graph?t.graph.issues:[],selectedNode:o,selectedEdge:i,selectedIssueId:e&&e.kind==="issue"?e.id:null,selectedIssue:s,collapsed:t.view.collapsed,keep:t.filters.keep,staleReason:r=>t.freshness.reasonOf(r),document:t.graph?t.workflowDocument:null,columns:t.paneColumns,walk:t.walk?t.walk.paneMark():null})}function sc(t,e,n,o=!1){t.graph=e;let i=new ln(e);t.index=i,n&&n.collapsed?t.collapsedState=n.collapsed.slice():!t.collapsedState.length&&t.view.collapsed.size===0&&(t.collapsedState=i.defaultCollapsed());let s=new Set(e.nodes.map(a=>a.id));t.view.setIndex(i),t.view.flow.clear(),t.view.setCollapsed(t.collapsedState.filter(a=>s.has(a)&&i.isGroup(a))),n&&n.selection!==void 0&&(t.selection=n.selection),t.selection&&t.selection.kind==="node"&&!s.has(t.selection.id)&&(t.selection=null),t.selection&&t.selection.kind==="edge"&&!i.edgeById.has(t.selection.id)&&(t.selection=null),t.selection&&t.selection.kind==="issue"&&!i.issueById.has(t.selection.id)&&(t.selection=null),t.view.relayout();let r=n&&n.viewport?n.viewport:null;if(r?t.view.viewport.set(r):t.view.fit(),X(t),Q(t),t.view.applySelection(t.selection),o){let a=(d,c,u)=>d+" "+(d===1?c:u),l=e.stats.issues.high;t.announce("Workflow loaded: "+a(e.nodes.length,"step","steps")+", "+a(e.edges.length,"connection","connections")+", "+a(e.issues.length,"finding","findings")+(l?" ("+l+" high severity)":"")+".")}t.saveSoon()}function ko(t){if(!t.graph)return;let e=new Set(t.graph.nodes.map(o=>o.id)),n=t.collapsedState.filter(o=>!e.has(o));t.collapsedState=n.concat(Array.from(t.view.collapsed)).sort()}function ac(t){let e=Pl(t.root,t.themes.kind);t.releasePage=Hl(t.root),t.liveEl=e.live,t.view=new oo(e,ab(t)),t.chrome=new so({onQuery:n=>t.search.run(n),onSearchKey:n=>t.search.handleKey(n),onSearchOpen:()=>t.focusSearch(),onSeverity:n=>t.applyFilters(()=>t.filters.toggleSeverity(n)),onToggleExceptions:n=>t.setExceptions(n),onAbout:n=>t.showAbout({focus:n}),onZoom:n=>t.view.zoomStep(n),onToggleLegend:n=>t.setLegend(n),onToggleFlow:n=>t.setFlow(n),onTogglePhaseIndex:n=>t.setPhaseIndexShown(n),onOverview:()=>t.toggleOverview(!0),onToggleRail:()=>t.toggleRail(),onFitWhole:()=>t.view.fitWhole(),onZoomToSelection:()=>t.zoomToSelection(),onExport:n=>ic(t,n),onShortcuts:()=>t.toggleShortcuts(!0),onMenuOpen:()=>X(t),onReview:()=>{let n=t.walk.active;t.walk.toggle(),!n&&t.walk.active&&_o(t)}},e.zoomBar),t.root.appendChild(t.chrome.header),t.walkBar=new bo({onFilter:n=>t.walk.setFilter(n),onStep:n=>{t.walk.step(n)},onExit:()=>{t.walk.stop(),_o(t)},onKey:n=>{let o=n.key;return o==="j"||o==="J"?t.walk.step(1):o==="k"||o==="K"?t.walk.step(-1):o==="]"?t.walk.stepQuote(1):o==="["?t.walk.stepQuote(-1):o==="u"||o==="U"?t.walk.jump("notObserved",n.shiftKey):o==="n"||o==="N"||o==="p"||o==="P"?t.walk.jump("findings",o==="p"||o==="P"):!1}}),t.notice=new vo({onWorkspaceHint:n=>t.bridge.post({v:1,type:"workspaceHint",action:n}),onReviewAffected:()=>{t.walk.start("changed")&&_o(t)}}),t.root.appendChild(t.notice.root),t.root.appendChild(e.body),e.body.appendChild(e.main),e.main.appendChild(t.walkBar.root),t.search=new go(t.chrome.searchInput,t.chrome.results,{index:()=>t.index,activate:n=>t.activateHit(n),onQueryChanged:n=>{t.filters.patch({query:n}),t.saveSoon()},blurToCanvas:()=>t.view.canvasEl.focus()}),t.rail=new fo({onTab:n=>t.setRailTab(n),onClearFilters:()=>t.clearFilters(),onSelectIssue:(n,o)=>{t.focusIssue(n,{fromList:"issues"}),t.doubleClick.arm(o,()=>t.openIssue(n,!1))},onOpenIssue:(n,o)=>t.openIssue(n,o),onSelectNode:(n,o)=>{t.select({kind:"node",id:n},{center:!0,reveal:!0,fromList:"outline"}),t.doubleClick.arm(o,()=>t.select({kind:"node",id:n},{center:!0,reveal:!0,open:!0,fromList:"outline"}))},onOpenNode:(n,o)=>t.select({kind:"node",id:n},{center:!0,reveal:!0,open:!0,focusEditor:o,fromList:"outline"}),onSelectEdge:n=>t.select({kind:"edge",id:n},{reveal:!0,fromList:"outline"}),onShowNode:n=>t.focusNode(n,{center:!0,pulse:!0}),onShowEdge:n=>t.select({kind:"edge",id:n},{reveal:!0}),onShowIssue:n=>t.focusIssue(n),onChallenge:()=>rc(t,"challenge"),onRefine:()=>rc(t,null),onOpen:(n,o)=>{t.walk.openQuote(n,!!o)||t.openLocation(n,o)},onResize:n=>t.setRailWidth(n),onSelectLane:n=>t.selectLane(n),onToggleCollapse:n=>{t.index&&t.index.isGroup(n)&&t.view.toggleCollapse(n)},onShowLimitations:()=>t.showAbout({at:"limitations",focus:!0}),onSheetToggle:()=>t.toggleRail(),onSheetCollapse:()=>{t.walk.stop()?_o(t):t.collapseSheet(!0)},onSheetResize:n=>t.setSheetFraction(n),sheetFraction:()=>t.sheetFraction,sheetFractionMax:()=>t.sheetFractionMax(),bodyHeight:()=>e.body.getBoundingClientRect().height}),e.body.appendChild(t.rail.root),_(t.rail.root,"pointerdown",()=>{t.railChosen=!0}),_(t.rail.root,"keydown",()=>{t.railChosen=!0}),t.legend=new po(n=>t.setLegend(n)),e.canvas.appendChild(t.legend.root),t.sheet=new mo(()=>t.toggleShortcuts(!1)),t.root.appendChild(t.sheet.root),t.root.appendChild(t.chrome.more.panel),t.root.appendChild(t.chrome.status),t.root.appendChild(t.liveEl),t.setRailWidth(t.railWidth),t.setRailOpen(t.railOpen)}function _o(t){try{t.view.canvasEl.focus()}catch{}}function ab(t){let e=(o,i)=>t.select({kind:"node",id:o},{showClaim:!0,open:!0,focusEditor:i}),n=(o,i)=>t.select({kind:"edge",id:o},{showClaim:!0,open:!0,focusEditor:i});return{keep:t.filters.keep,activateNode:(o,i)=>{t.select({kind:"node",id:o},{showClaim:!0}),t.doubleClick.arm(i,()=>e(o,!1))},activateEdge:(o,i)=>{t.select({kind:"edge",id:o},{showClaim:!0}),t.doubleClick.arm(i,()=>n(o,!1))},openNode:e,openEdge:n,clearFilters:()=>t.clearFilters(),announce:o=>t.announce(o),afterCollapse:()=>{ko(t),t.view.applySelection(t.selection),Q(t),t.saveSoon()},onViewportChange:o=>{t.viewportState={x:o.x,y:o.y,zoom:o.zoom},t.saveSoon()},onPhaseIndexChanged:()=>{X(t),t.saveSoon()},onKeyDown:o=>t.onKeyDown(o),onBackgroundClick:()=>t.clearSelection(),keptTarget:()=>{let o=t.selection;if(!o)return null;if(o.kind!=="issue")return{kind:o.kind,id:o.id};let i=t.index?t.index.issueById.get(o.id):void 0;return i?i.nodeIds[0]?{kind:"node",id:i.nodeIds[0]}:i.edgeIds.length?{kind:"edge",id:i.edgeIds[0]}:null:null}}}function rc(t,e){let n=t.root.querySelector(".mlv-workflow__refine"),o=t.root.querySelector(".mlv-workflow__composer"),i=t.root.querySelector(".mlv-workflow__intent");!n||!o||!i||(o.hidden||n.click(),n.click(),e&&(i.value=e,i.dispatchEvent(new Event("change",{bubbles:!0}))))}function As(t){let e={v:1,type:"openLocation",file:t.file,absFile:t.absFile,line:t.line,col:t.col,endLine:t.endLine,endCol:t.endCol,preview:!0};return t.evidenceId&&(e.evidenceId=t.evidenceId),t.evidenceId&&t.cell!==void 0&&(e.cell=t.cell),e}function lc(t,e,n=!1){if(!t.caps.canOpenSource)return;let o=e.evidenceId?t.freshness.reasonOf(e.file):void 0;if(o){let r=e.file+": "+me[o]+". Not opened"+(o==="elsewhere"?".":"; the cited lines may no longer be there.");t.view.toast(r),t.announce(r);return}t.bridge.saveState(t.getState());let i=As(e);n&&(i.focus=!0),t.bridge.post(i);let s=we(e);t.announce(n?"Opening "+s+" in the editor.":"Opening "+s+" beside the diagram. Focus stays here; Alt+Enter moves it to the editor."),!n&&!t.openHintShown&&(t.openHintShown=!0,t.view.toast("Opening the cited lines beside the diagram. Focus stays here; Alt+Enter moves it to the editor."))}var lb=["about","issues","inspector","outline"];function So(t){if(!t||typeof t!="object")return null;let{kind:e,id:n}=t;return e!=="node"&&e!=="edge"&&e!=="issue"||typeof n!="string"||!n?null:{kind:e,id:n}}function Mo(t){return typeof t=="string"&&lb.indexOf(t)>=0?t:null}function dc(t){return{focusSearch:()=>t.focusSearch(),escape:()=>t.dismissTopmost(),cycleIssue:e=>{if(t.walking())return t.walkFindings(e);let n=t.visibleIssues();if(!n.length)return!1;let o=t.selectedIssueId(),s=((o?n.findIndex(r=>r.id===o):-1)+(e?-1:1)+n.length)%n.length;return t.focusIssue(n[s].id),!0},zoom:e=>t.zoom(e),fit:()=>t.fit(),toggleFocusMode:()=>t.toggleFocusMode(),zoomToSelection:()=>t.zoomToSelection(),overview:()=>t.overview(),toggleLegend:()=>t.toggleLegend(),toggleFlow:()=>t.toggleFlow(),replayFlow:()=>t.replayFlow(),toggleCollapse:()=>t.collapseSelection(),openSelection:e=>t.openSelection(e),move:e=>t.move(e),toggleSeverity:e=>{let n=ne[e];n&&t.toggleSeverity(n)},toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:()=>t.toggleShortcuts(),cycleConnections:e=>t.cycleConnections(e),walking:()=>t.walking(),review:()=>t.review(),walkStep:e=>t.walkStep(e),walkQuote:e=>t.walkQuote(e),walkNotObserved:e=>t.walkNotObserved(e)}}function cc(t){return{focusSearch:()=>t.focusSearch(),dismissTopmost:()=>{if(t.sheetOpen())t.toggleShortcuts(!1);else{if(t.closeHeaderPanels())return!0;if(t.closePhasePopover())return!0;if(t.legendOpen())t.closeLegend();else{if(t.closeOverview())return!0;if(t.endWalk())return!0;if(t.collapseSheet())return!0;if(t.view().isFocusLocked)t.view().toggleFocusMode(t.selection());else if(t.selection())t.view().blurFocusedEdge(),t.clearSelection();else{let e=t.view().canvasEl,n=e.ownerDocument?e.ownerDocument.activeElement:null;if(!n||!e.contains(n))return!1;e.blur()}}}return!0},visibleIssues:()=>t.visibleIssues(),selectedIssueId:()=>{let e=t.selection();return e&&e.kind==="issue"?e.id:null},focusIssue:e=>t.focusIssue(e),zoom:e=>t.view().zoomStep(e),fit:()=>t.view().fit(),toggleFocusMode:()=>t.view().toggleFocusMode(t.selection()),zoomToSelection:()=>t.zoomToSelection(),overview:()=>t.toggleOverview(),toggleLegend:()=>t.toggleLegend(),toggleFlow:()=>t.toggleFlow(),replayFlow:()=>t.view().flow.replay()?(t.announce("Playing the connection flow again."),!0):!1,collapseSelection:()=>{let e=t.selection(),n=t.index();if(!e||e.kind!=="node"||!n)return!1;let o=n.isGroup(e.id)?e.id:n.parentOf.get(e.id)||null;return!o||!n.isGroup(o)?!1:(t.view().toggleCollapse(o),!0)},openSelection:e=>t.openSelection(e),move:e=>t.move(e),toggleSeverity:e=>t.toggleSeverity(e),toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:()=>t.toggleShortcuts(),cycleConnections:e=>db(t,e),walking:()=>t.walking(),review:()=>t.review(),walkStep:e=>t.walkStep(e),walkQuote:e=>t.walkQuote(e),walkNotObserved:e=>t.walkJump("notObserved",e),walkFindings:e=>t.walkJump("findings",e)}}function db(t,e){let n=t.selection(),o=null;if(n&&n.kind==="node"?o=n.id:n&&n.kind==="edge"&&(o=t.edgeAnchor()),!o)return!1;let i=t.view().incidentRoutes(o);if(!i.length)return t.view().toast("That node has no connections in this view."),!0;let s=n&&n.kind==="edge"?n.id:null,r=-1;for(let l=0;l(t.view.holdHover(),i(...s))}return n}var hb=new Set(["walking","visibleIssues","selectedIssueId","dismissTopmost","overview","toggleShortcuts","focusSearch","toggleLegend","toggleRail","focusRailTabs"]);function fb(t,e){let n={};for(let[o,i]of Object.entries(e))n[o]=hb.has(o)?i:(...s)=>(t.view.overviewOpen&&(t.view.closeOverview(!1),X(t)),i(...s));return n}function pb(t){return cc({view:()=>t.view,index:()=>t.index,selection:()=>t.selection,edgeAnchor:()=>t.edgeAnchor,setEdgeAnchor:e=>{t.edgeAnchor=e},select:e=>t.select(e),clearSelection:()=>t.clearSelection(),focusSearch:()=>t.focusSearch(),visibleIssues:()=>mb(t),focusIssue:e=>t.focusIssue(e,{fromList:"issues"}),openSelection:e=>{let n=t.selection;if(!n)return!1;if(t.walk.active&&t.walkOwns(n))return t.walk.reopen(e);let o=t.locOf(n);return o&&t.openLocation(o,e),!0},zoomToSelection:()=>t.zoomToSelection(),move:e=>gb(t,e),toggleSeverity:e=>t.applyFilters(()=>t.filters.toggleSeverity(e)),toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:e=>t.toggleShortcuts(e),sheetOpen:()=>t.sheet.open,closeHeaderPanels:()=>t.closeHeaderPanels(),collapseSheet:()=>t.collapseSheet(!1),announce:e=>t.announce(e),toggleLegend:()=>t.setLegend(!t.legendOpen),legendOpen:()=>t.legendOpen,closeLegend:()=>t.setLegend(!1),toggleFlow:()=>t.setFlow(!t.flowOn),closePhasePopover:()=>t.view.closePhasePopover(),closeOverview:()=>t.view.closeOverview(!0)?(X(t),!0):!1,toggleOverview:()=>t.toggleOverview(),walking:()=>t.walk.active,endWalk:()=>t.walk.stop(),review:()=>t.walk.toggle(),walkStep:e=>t.walk.step(e),walkQuote:e=>t.walk.stepQuote(e),walkJump:(e,n)=>t.walk.jump(e,n)})}function mb(t){return t.graph?t.graph.issues.filter(t.filters.keep):[]}function gb(t,e){let n=t.view.takeArrowLane(),o=n&&t.index?t.index.roots(n)[0]:void 0;if(o){let r=t.view.frame?t.view.frame.boxes.get(o):void 0;t.select({kind:"node",id:o},{center:!!r&&!t.view.viewport.isVisible(r)});let a=t.view.nodeElement(o);a&&a.focus();return}let i=t.view.nextSelection(t.selection,e);if(!i)return;t.select({kind:"node",id:i.id},{center:!i.visible});let s=t.view.nodeElement(i.id);s&&s.focus()}function Lo(t,e,n){if(!e||typeof e!="object")return;e.filters&&t.filters.restore(e.filters);let o=Mo(e.railTab);if(o&&(t.railTab=o),Array.isArray(e.collapsed)){t.collapsedState=e.collapsed.slice();let s=t.index;s&&t.view.setCollapsed(e.collapsed.filter(r=>s.isGroup(r)))}let i=So(e.selection);n&&i&&(t.selection=i),e.phaseIndex==="folded"||e.phaseIndex==="hidden"?t.view.setPhaseIndex({folded:e.phaseIndex==="folded",hidden:e.phaseIndex==="hidden"}):e.phaseIndex===void 0&&e.minimapCollapsed===!0?t.view.setPhaseIndex({folded:!0}):e.phaseIndex===void 0&&n&&t.view.setPhaseIndex({folded:!1,hidden:!1}),typeof e.flow=="boolean"&&t.setFlow(e.flow),typeof e.legendOpen=="boolean"&&t.setLegend(e.legendOpen),n&&t.index&&(t.view.relayout(),X(t),Q(t),t.view.applySelection(t.selection)),e.viewport&&t.view.viewport.set(e.viewport)}function hc(t){ko(t);let e={viewport:{...t.viewportState},selection:t.selection?{...t.selection}:null,collapsed:t.collapsedState.slice(),filters:t.filters.snapshot(),railTab:t.railTab};if(t.view.phaseIndexHidden?e.phaseIndex="hidden":t.view.phaseIndexFolded&&(e.phaseIndex="folded"),t.flowOn||(e.flow=!1),t.legendOpen&&(e.legendOpen=!0),t.graph&&t.workflowRevision){e.workflowRevision=t.workflowRevision;let n=br(t.root);n&&(e.composer=n),t.railMode==="sheet"&&t.railOpen&&(e.sheetOpen=!0);let o=t.walk.viewState();o&&(e.walk=o)}return e}function fc(t){try{return t.loadState()}catch{return null}}function pc(t,e){Ks(e,{init:(n,o)=>{t.caps=o||t.caps,t.setTheme(n),X(t),Q(t)},workflow:n=>{n!==t.workflowDocument&&t.setWorkflow(n)},workflowStatus:n=>Er(t,n),hostNotice:(n,o)=>t.showHostNotice(n,o),stale:n=>t.setStale(n),actionResult:n=>t.onActionResult(n),theme:n=>t.setTheme(n),revealNode:(n,o)=>t.revealClaim({kind:"node",id:n},{center:o}),revealEdge:n=>t.revealClaim({kind:"edge",id:n}),revealIssue:n=>t.revealClaim({kind:"issue",id:n}),restoreState:n=>Lo(t,n,!0),onUnknown:n=>t.bridge.post({v:1,type:"log",level:"debug",message:"ignored unknown message type: "+n})})}var vb=150,bb=100;function wb(t){if(!t||typeof t!="object")return null;let e=t,n=typeof e.filter=="string"&&kt.indexOf(e.filter)>=0?e.filter:null,o=e.claim;if(!n||!o||typeof o!="object"||o.kind!=="node"&&o.kind!=="edge"&&o.kind!=="issue"||typeof o.id!="string"||!o.id)return null;let i={filter:n,claim:{kind:o.kind,id:o.id}},s=e.quote;return typeof s=="number"&&Number.isInteger(s)&&s>0&&s!!this.app.freshness.reasonOf(e);this.app=e}index(){return this.app.index}claims(){let e=this.index();return e?(e!==this.orderIndex&&(this.order=Ql(e),this.orderIndex=e),this.order):[]}changedOffered(){return $l(this.app.freshness.list().map(e=>e.reason))}counts(){let e=this.index();return e?td(this.claims(),e,this.isStale):{notObserved:0,findings:0,changed:0,all:0}}offered(){return nd(this.counts(),this.changedOffered())}listFor(e){let n=this.index();return n?ed(this.claims(),e,n,this.isStale):[]}current(){return this.position>=0&&this.position=0?i.filter:o.indexOf("notObserved")>=0?"notObserved":"all";let r=this.listFor(s);if(!r.length)return this.app.announce("This revision has no claims to walk."),!1;let a=this.active;a||this.app.openRailForWalk(),this.filter=s,this.list=r;let l=i&&i.filter===s?i:null;return this.position=Math.max(0,_t(r,l?l.claim:null,this.claims())),this.quote=l&&ue(l.claim,this.current())?Math.min(l.quote||0,Math.max(0,this.currentLocs().length-1)):0,this.active=!0,a||this.app.onWalkShown(!0),this.show("open"),!0}stop(e=!0){return this.active?(this.active=!1,this.cancelTimer(),this.app.bridge.post({v:1,type:"walk",state:"end"}),this.remember(),this.app.onWalkShown(!1),e&&this.app.announce("Review ended. r resumes it here."),this.app.saveSoon(),!0):!1}toggle(){this.active?this.stop():this.start()}step(e){if(!this.active||!this.list.length)return!1;let n=Math.max(0,Math.min(this.list.length-1,this.position+e));return n===this.position?(this.app.announce(e>0?"Claim "+this.list.length+" of "+this.list.length+" is the last one.":"Claim 1 of "+this.list.length+" is the first one."),!0):(this.position=n,this.quote=0,this.show("open"),!0)}stepQuote(e){if(!this.active)return!1;let n=this.currentLocs();if(n.length<2)return this.app.announce(n.length?"This claim has one quote.":"This claim cites no lines."),!0;let o=Math.max(0,Math.min(n.length-1,this.quote+e));return o===this.quote?(this.app.announce(e>0?"Quote "+n.length+" of "+n.length+" is the last one.":"Quote 1 of "+n.length+" is the first one."),!0):(this.quote=o,this.app.announce("Quote "+(o+1)+" of "+n.length+": "+Ji(n[o])+"."),this.scheduleOpen(),this.remember(),this.render("quote"),this.app.saveSoon(),!0)}jump(e,n){let o=this.index();if(!o)return!1;let i=e;if(!this.active)return this.start(i);let s=this.claims(),r=d=>e==="findings"?d.kind==="issue":ze(uo(o,d)),a=s.findIndex(d=>ue(d,this.current())),l=null;for(let d=1;d<=s.length;d++){let c=((a<0?n?0:-1:a)+(n?-d:d)+s.length*2)%s.length;if(r(s[c])){l=s[c];break}}return l?(this.list.some(d=>ue(d,l))||(this.filter=i,this.list=this.listFor(i)),this.position=Math.max(0,this.list.findIndex(d=>ue(d,l))),this.quote=0,this.show("open"),!0):(this.app.announce(e==="findings"?"This revision has no findings.":"No claim is marked inferred or unresolved in this revision."),!0)}setFilter(e){if(!this.active||this.offered().indexOf(e)<0)return;let n=this.current();this.filter=e,this.list=this.listFor(e),this.position=Math.max(0,_t(this.list,n,this.claims()));let o=ue(n,this.current());o||(this.quote=0),this.show(o?"keep":"open"),this.app.announce(co(e,this.list.length)+". "+this.announcement())}reopen(e){return!this.active||!this.current()?!1:(this.cancelTimer(),this.sendOpen(e),!0)}openQuote(e,n){if(!this.active||!e.evidenceId)return!1;let o=this.currentLocs().findIndex(i=>i.evidenceId===e.evidenceId);return o<0?!1:(this.quote=o,this.cancelTimer(),this.sendOpen(n),this.remember(),!0)}followSelection(e){if(!this.active||this.selecting||!e)return;let n=this.list.findIndex(o=>ue(o,e));n<0||n===this.position||(this.position=n,this.quote=0,this.cancelTimer(),this.latestSeq=0,this.app.bridge.post({v:1,type:"walk",state:"clear"}),this.status=this.unansweredStatus("idle"),this.remember(),this.render("claim"))}onDocument(e){let n=this.app.workflowRevision;e&&(this.active&&this.stop(!1),this.remembered=null,this.list=[],this.position=-1);let o=this.restored;if(this.restored=null,o&&o.revision===n&&!this.remembered){this.remembered={revision:n,state:o.state},o.state.active&&this.resume();return}if(!this.active)return;let i=this.current();this.list=this.listFor(this.filter),this.position=Math.max(0,_t(this.list,i,this.claims())),this.render()}resume(){let e=this.remembered?this.remembered.state:null;if(!e)return;let n=this.offered();this.filter=n.indexOf(e.filter)>=0?e.filter:"all",this.list=this.listFor(this.filter),this.list.length&&(this.position=Math.max(0,_t(this.list,e.claim,this.claims())),this.quote=ue(e.claim,this.current())?Math.min(e.quote||0,Math.max(0,this.currentLocs().length-1)):0,this.active=!0,this.app.onWalkShown(!0),this.show("rest"))}onStale(e=[]){if(!this.active)return;this.offered().indexOf(this.filter)<0&&(this.filter="all");let n=this.current();if(this.list=this.listFor(this.filter),this.position=Math.max(0,_t(this.list,n,this.claims())),!ue(n,this.current())){this.quote=0,this.cancelTimer(),this.latestSeq=0,this.app.bridge.post({v:1,type:"walk",state:"clear"}),this.show("rest");return}if(this.timer!==null)this.status=this.unansweredStatus("opening");else if(this.latestSeq===0&&(this.status.state==="idle"||this.status.state==="blocked"))this.status=this.unansweredStatus("idle");else if(this.status.state==="blocked"&&this.status.loc){let o=this.status.loc.file,i=e.find(r=>r.path===o),s=this.app.freshness.reasonOf(o);(i?i.reason:void 0)!==s&&(s||yb(this.blockedReason))&&(this.latestSeq=0,this.status=this.unansweredStatus("idle"))}this.render()}onResult(e,n){if(!(!this.active||n!==this.latestSeq||e.outcome==="cancelled")){if(e.outcome==="done")this.status=this.statusFor("done",void 0,this.latestFocus);else{this.blockedReason=e.outcome==="blocked"?e.reason:void 0;let o=e.message||(e.outcome==="blocked"?"Not opened.":"VS Code could not show the file.");this.status=this.statusFor(e.outcome,o),this.app.announce(ad(e.outcome,o))}this.render()}}cancelTimer(){this.timer!==null&&clearTimeout(this.timer),this.timer=null}statusFor(e,n,o=!1){let i=this.currentLocs(),s=Math.min(this.quote,Math.max(0,i.length-1)),r={state:e,quote:s,quotes:i.length};return i[s]&&e!=="none"&&e!=="unavailable"&&(r.loc=i[s]),n&&(r.message=n),o&&(e==="opening"||e==="done")&&(r.focusEditor=!0),r}unansweredStatus(e){let n=this.currentLocs(),o=n[Math.min(this.quote,n.length-1)];if(!n.length||!o)return this.statusFor("none");if(!this.app.caps.canOpenSource)return this.statusFor("unavailable");let i=this.localBlock(o);return i?this.statusFor("blocked",i):this.statusFor(e)}localBlock(e){let n=e.evidenceId?this.app.freshness.reasonOf(e.file):void 0;return n?e.file+": "+me[n]+"; not opened.":null}scheduleOpen(){this.cancelTimer(),this.latestSeq=0,this.status=this.unansweredStatus("opening"),this.status.state!=="unavailable"&&(this.timer=setTimeout(()=>{this.timer=null,this.sendOpen(!1)},vb))}get pendingOpen(){return this.timer!==null}flushOpen(){this.timer!==null&&(this.cancelTimer(),this.sendOpen(!1))}sendOpen(e){if(!this.active)return;let n=this.currentLocs(),o=n[Math.min(this.quote,n.length-1)];if(!o){this.app.bridge.post({v:1,type:"walk",state:"clear"}),this.status=this.statusFor("none"),this.render();return}if(!this.app.caps.canOpenSource){this.status=this.statusFor("unavailable"),this.render();return}let i=++this.seq;this.latestSeq=i,this.latestFocus=e,this.remember(),this.app.bridge.saveState(this.app.getState());let s=As(o);s.walk=!0,s.seq=i,e&&(s.focus=!0),this.app.postRequest(s,a=>this.onResult(a,i));let r=this.localBlock(o);this.blockedReason=r?this.app.freshness.reasonOf(o.file):void 0,this.status=r?this.statusFor("blocked",r):this.statusFor("opening",void 0,e),this.render()}show(e){let n=this.current();if(n){this.selecting=!0;try{this.app.showWalkClaim(n)}finally{this.selecting=!1}e==="open"?this.scheduleOpen():e==="rest"&&(this.latestSeq=0,this.status=this.unansweredStatus("idle")),this.remember(),this.render(e==="keep"?!1:"claim"),this.app.announce(this.announcement()),this.app.saveSoon()}}announcement(){let e=this.index(),n=this.current();return e&&n?id(this.position,this.list.length,this.filter,e,n):""}render(e=!1){this.app.renderWalkBar(),os(this.app.rail.root,this.paneMark(),e)}paneMark(){if(!this.active)return null;let e=this.current();return!e||!ue(e,this.app.selection)?null:{quote:this.status.quote,status:this.status}}remember(){let e=this.app.workflowRevision,n=this.current();if(!e||!n)return;let o={filter:this.filter,claim:{kind:n.kind,id:n.id}};this.quote>0&&(o.quote=this.quote),this.active&&(o.active=!0),this.remembered={revision:e,state:o}}viewState(){let e=this.app.workflowRevision;if(!this.remembered||!e||this.remembered.revision!==e)return;let n={...this.remembered.state,claim:{...this.remembered.state.claim}};return this.active?n.active=!0:delete n.active,n}restore(e,n){let o=wb(n);this.restored=o?{revision:e,state:o}:null}destroy(){this.cancelTimer()}};var xb=32,Cs=900,Eb=620,kb=.47,mc=.25,gc=.75,_b=1500,Ao=class{constructor(e,n){this.graph=null;this.index=null;this.edgeAnchor=null;this.flowOn=!0;this.exceptionsOn=!1;this.filters=new sn;this.viewportState={x:0,y:0,zoom:1};this.selection=null;this.collapsedState=[];this.railTab="about";this.legendOpen=!1;this.freshness=new on;this.openHintShown=!1;this.railOpen=!0;this.railChosen=!1;this.dockedHidden=!1;this.railWidth=360;this.railMode="docked";this.sheetFraction=kb;this.paneColumns=1;this.workflowDocument=null;this.workflowRevision=null;this.restoredView=null;this.restoredComposer=null;this.restoredTab=null;this.restoredSelection=null;this.restoredSheet=null;this.pending=new Map;this.requestSerial=0;this.disposers=[];this.destroyed=!1;this.releaseRevealFocus=null;this.releasePage=()=>{};this.saveSoon=Fs(()=>this.bridge.saveState(this.getState()),250);this.root=e,this.bridge=n,this.caps=n.capabilities,this.themes=new rn(e,n.theme||"light"),this.doubleClick=new xn(e),this.disposers.push(()=>this.doubleClick.dispose()),this.walk=new Io(this),ac(this);let o=fc(n);o&&Lo(this,o,!1),o&&typeof o.workflowRevision=="string"&&o.viewport&&(this.restoredView={revision:o.workflowRevision,viewport:{...o.viewport}});let i=o&&typeof o.workflowRevision=="string"?yr(o.composer):null;o&&i&&(this.restoredComposer={revision:o.workflowRevision,composer:i});let s=o&&typeof o.workflowRevision=="string"?Mo(o.railTab):null;o&&s&&(this.restoredTab={revision:o.workflowRevision,tab:s});let r=o&&typeof o.workflowRevision=="string"?So(o.selection):null;o&&r&&(this.restoredSelection={revision:o.workflowRevision,selection:r}),o&&typeof o.workflowRevision=="string"&&o.sheetOpen===!0&&(this.restoredSheet=o.workflowRevision),o&&typeof o.workflowRevision=="string"&&this.walk.restore(o.workflowRevision,o.walk),this.disposers.push(n.onMessage(a=>this.onMessage(a))),typeof window<"u"&&this.disposers.push(_(window,"resize",()=>this.onResize())),this.disposers.push(_(e,"keydown",a=>{a.defaultPrevented||!(a.ctrlKey||a.metaKey)||a.altKey||a.key!=="f"&&a.key!=="F"||this.modalOpen(a.target)||(a.preventDefault(),this.focusSearch())})),this.disposers.push(_(e,"keydown",a=>{a.key!=="Escape"||a.defaultPrevented||!this.walk.active||this.modalOpen(a.target)||this.walk.stop()&&a.preventDefault()})),this.chrome.setWidth(this.rootWidth()),this.view.setPanelWidth(this.rootWidth())}rootWidth(){let e=this.root.getBoundingClientRect().width;return e>0?e:0}onResize(){let e=this.chrome.headerLayout;this.chrome.setWidth(this.rootWidth())!==e&&(X(this),this.renderWalkBar()),this.view.setPanelWidth(this.rootWidth()),this.autoRail(),this.paneColumns!==this.selectionColumns()&&Q(this)}selectionColumns(){return this.railMode==="sheet"&&this.rootWidth()>=Eb?2:1}railShown(){return this.railOpen}refreshChrome(){X(this)}focusSearch(){this.chrome.setSearchOpen(!0),this.search.focus()}showAbout(e={}){this.railTab="about",this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),Q(this),this.saveSoon();let n=e.at==="limitations"?this.rail.revealLimitations():e.focus?this.rail.activePanel():null;if(e.focus&&n)try{n.focus()}catch{}this.announce(e.at==="limitations"?"About: the coverage limitations.":"About this revision shown.")}modalOpen(e){if(this.sheet&&this.sheet.open||ni(this))return!0;let n=e;return!!n&&typeof n.closest=="function"&&!!n.closest(".mlv-sheet, .mlv-workflow__composer")}closeHeaderPanels(){return yn(this,!0)}headerPanelOpen(){return ni(this)}setWorkflow(e,n){let o=this.workflowRevision!==e.revision.id,i=n;if(!n||!n.viewport){let r=null;this.graph&&this.workflowRevision===e.revision.id?r={...this.viewportState}:!this.graph&&this.restoredView&&this.restoredView.revision===e.revision.id&&(r={...this.restoredView.viewport}),r&&(i={...n||{},viewport:r})}this.restoredView=null;let s=this.restoredComposer&&this.restoredComposer.revision===e.revision.id?this.restoredComposer.composer:null;if(this.restoredComposer=null,this.workflowRevision!==e.revision.id){let r=!this.graph&&this.restoredTab&&this.restoredTab.revision===e.revision.id?this.restoredTab.tab:null;this.railTab=r||"about"}if(this.restoredTab=null,!this.graph){let r=a=>a===e.revision.id;this.restoredSelection&&r(this.restoredSelection.revision)&&(this.selection=this.restoredSelection.selection),r(this.restoredSheet)&&(this.railChosen=!0,this.railOpen=!0)}this.restoredSelection=null,this.restoredSheet=null,this.workflowDocument=e,this.workflowRevision=e.revision.id,o&&this.view.closeOverview(!1),this.autoRail(),sc(this,wn(e),i,!0),kr(this,e,s),this.legendOtherKinds(),this.view.afterChromeChange(),this.walk.onDocument(o),this.syncNoticeReview()}postRequest(e,n){this.requestSerial+=1;let o="";for(let s=0;s<4;s++)o+=Math.floor(Math.random()*36).toString(36);let i="r"+this.requestSerial.toString(36)+"-"+o;for(this.pending.set(i,n);this.pending.size>xb;){let s=this.pending.keys().next().value;if(s===void 0)break;this.pending.delete(s)}return this.bridge.post({...e,requestId:i}),i}onActionResult(e){if(!e||typeof e.requestId!="string")return;let n=this.pending.get(e.requestId);n&&(this.pending.delete(e.requestId),n(e))}openLocation(e,n=!1){lc(this,e,n)}setStale(e){let n=this.freshness.list();this.freshness.set(e)&&(this.view.setStale(this.freshness.list()),this.index&&this.view.refresh(this.selection),X(this),Q(this),this.walk.onStale(n),this.syncNoticeReview())}showHostNotice(e,n){let o=!!n&&n.length===1&&n[0]==="checking";if(this.freshness.checking!==o&&(this.freshness.checking=o,X(this)),o)return;let i=this.notice.root.hidden;this.notice.update(e,n),this.syncNoticeReview();let s=this.root.querySelector(".mlv-body");s&&this.notice.root.nextSibling!==s&&this.root.insertBefore(this.notice.root,s),this.graph&&i!==this.notice.root.hidden&&this.view.afterChromeChange()}syncNoticeReview(){let e=this.graph&&this.walk.changedOffered()?this.walk.counts().changed:0;this.notice.setReview(e,this.walk.active)}renderWalkBar(){if(!this.walkBar)return;let e=this.walk;if(!e.active){this.walkBar.update(null);return}this.walkBar.update({position:e.position,total:e.list.length,filter:e.filter,counts:e.counts(),offered:e.offered(),status:e.status,narrow:this.chrome.headerLayout==="narrow"})}onWalkShown(e){this.renderWalkBar(),X(this),this.syncNoticeReview(),e||Q(this),this.graph&&this.view.afterSheetToggle()}openRailForWalk(){this.railMode!=="docked"||this.railOpen||(this.railChosen=!0,this.setRailOpen(!0))}walkOwns(e){return ue(this.walk.current(),e)}showWalkClaim(e){let n=this.index;if(!n)return;this.view.holdHover();let o=r=>{let a=n.edgeById.get(r);a&&(this.view.expandAncestors(a.source),this.view.expandAncestors(a.target))};if(e.kind==="issue"){let r=n.issueById.get(e.id);if(!r)return;for(let a of r.nodeIds)this.view.expandAncestors(a);for(let a of r.edgeIds)o(a)}else e.kind==="node"?this.view.expandAncestors(e.id):o(e.id);this.select(e,{tab:"inspector",showClaim:!0}),e.kind==="issue"?this.view.frameIssue(e.id):this.view.revealTarget({kind:e.kind,id:e.id});let i=this.view.canvasEl,s=i.ownerDocument?i.ownerDocument.activeElement:null;if(s&&s!==i&&i.contains(s))try{i.focus()}catch{}}revealClaim(e,n={}){let o=this.index;if(!o)return!1;if(!(e.kind==="node"?o.nodeById.has(e.id):e.kind==="edge"?o.edgeById.has(e.id):o.issueById.has(e.id))){let l="That claim is not in the revision shown here.";return this.view.toast(l),this.announce(l),!1}this.sheet.open&&this.sheet.hide(null),yn(this,!1),this.view.closeOverview(!1)&&X(this);let s=l=>{let d=o.edgeById.get(l);d&&(this.view.expandAncestors(d.source),this.view.expandAncestors(d.target))};if(e.kind==="issue"){let l=o.issueById.get(e.id);for(let d of l.nodeIds)this.view.expandAncestors(d);for(let d of l.edgeIds)s(d)}else e.kind==="node"?this.view.expandAncestors(e.id):s(e.id);this.openRailForWalk();let r=n.center!==!1;e.kind==="node"?this.select(e,{tab:"inspector",showClaim:!0,center:r,reveal:r,pulse:r}):this.select(e,{tab:"inspector",showClaim:!0}),e.kind==="issue"?this.view.frameIssue(e.id):e.kind==="edge"?this.view.frameEdge(e.id):e.kind==="node"&&!r&&this.view.revealTarget({kind:"node",id:e.id});let a=e.kind==="issue"?null:{kind:e.kind,id:e.id};return this.view.focusTarget(a),this.holdRevealFocus(a),!0}holdRevealFocus(e){this.releaseRevealFocus&&this.releaseRevealFocus();let n=this.root.ownerDocument,o=n?n.defaultView:null;if(!n||!o)return;let i=()=>{let a=n.activeElement;(!a||a===n.body||a===n.documentElement)&&this.view.focusTarget(e)};o.addEventListener("focus",i);let s=o.setTimeout(()=>r(),_b),r=()=>{o.removeEventListener("focus",i),o.clearTimeout(s),this.releaseRevealFocus===r&&(this.releaseRevealFocus=null)};this.releaseRevealFocus=r}setPhaseIndexShown(e){this.view.setPhaseIndex({hidden:!e}),X(this),this.saveSoon(),this.announce("Phase index "+(e?"shown":"hidden")+".")}toggleOverview(e=!1){if(this.view.overviewOpen){if(this.view.closeOverview(!0),e)try{this.view.canvasEl.focus()}catch{}}else this.view.openOverview();X(this)}setLegend(e){this.legendOpen=e,this.legend.setOpen(e),X(this),this.saveSoon()}setRailTab(e){this.railTab=e,this.railMode==="sheet"&&!this.railOpen&&(this.railChosen=!0,this.setRailOpen(!0)),Q(this),this.saveSoon()}showRailTab(e){this.railTab=e,this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),Q(this),this.saveSoon(),this.railMode==="sheet"&&this.rail.focusTab(e)}focusRailTabs(){this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),Q(this),this.rail.focusTab(this.railTab)}toggleRail(){this.railChosen=!0,this.railMode==="docked"&&(this.dockedHidden=this.railOpen),this.setRailOpen(!this.railOpen)}collapseSheet(e=!1){if(this.railMode!=="sheet"||!this.railOpen)return!1;if(this.railChosen=!0,this.setRailOpen(!1),this.announce("Bottom panel collapsed."),e)try{this.view.canvasEl.focus()}catch{}return!0}sheetFractionMax(){let e=this.root.querySelector(".mlv-body"),n=e?e.getBoundingClientRect().height:0;if(!(n>0))return gc;let o=Math.min(240,.45*n);return Math.max(mc,Math.min(gc,(n-o)/n))}setSheetFraction(e){let n=Math.max(mc,Math.min(this.sheetFractionMax(),e));Math.abs(n-this.sheetFraction)<.001||(this.sheetFraction=n,this.rail.setSheetFraction(n),this.railMode==="sheet"&&this.railOpen&&this.graph&&this.view.afterSheetToggle())}autoRail(){let e=this.root.getBoundingClientRect().width;if(!(e>0))return;let n=e-this.railWidth>=Cs?"docked":"sheet",o=this.railOpen;if(n!==this.railMode?o=n==="sheet"?this.railOpen&&this.railChosen:!this.dockedHidden:n==="docked"&&!this.railChosen&&(o=!0),n===this.railMode&&o===this.railOpen)return;let i=n!==this.railMode;this.railMode=n,this.rail.setMode(n,this.sheetFraction),this.setRailOpen(o,!0),i&&(this.saveSoon(),n==="docked"&&this.setRailWidth(this.railWidth),Q(this),X(this)),this.graph&&this.view.handleResize()}legendOtherKinds(){let e=new Map,n=0;for(let i of this.graph?.edges||[])if(i.kind==="unknown")n++;else{let s=i.authoredKind||i.kind;e.set(s,(e.get(s)||0)+1)}let o=Array.from(e,([i,s])=>({kind:i,count:s})).sort((i,s)=>s.count-i.count||(i.kinds.kind?1:0));this.legend.setKinds(o,n)}showRailForFinding(){return this.railChosen=!0,this.railOpen?!1:(this.setRailOpen(!0),!0)}setRailOpen(e,n=!1){let o=this.railOpen!==e;this.railOpen=e,e&&this.railMode==="docked"&&(this.dockedHidden=!1),this.rail.setOpen(e),o&&(this.saveSoon(),this.chrome&&X(this),e&&this.rail&&Q(this),!n&&this.railMode==="sheet"&&this.graph&&this.view.afterSheetToggle())}toggleShortcuts(e){if(typeof e=="boolean"?e:!this.sheet.open){this.sheet.show();return}this.sheet.hide(this.view.canvasEl)}setRailWidth(e){let n=this.rootWidth(),o=this.railMode==="docked"&&n-Cs>=280?n-Cs:560;this.railWidth=Math.max(280,Math.min(560,o,Math.round(e))),this.railMode==="docked"&&(this.rail.root.style.width=this.railWidth+"px")}setExceptions(e){this.exceptionsOn=e,e?this.view.canvasEl.setAttribute("data-exceptions","on"):this.view.canvasEl.removeAttribute("data-exceptions"),X(this),this.announce(e?"Observed claims faded; inferred and unresolved claims stand out.":"Observed claims shown normally.")}setFlow(e){this.flowOn=e,this.view.flow.setEnabled(e),this.view.flow.syncCanvas(),X(this),this.saveSoon()}select(e,n){e.kind!=="edge"&&(this.edgeAnchor=null),this.view.clearArrowLane(),this.selection=e,this.railTab=n&&n.tab?n.tab:this.selectionTab(n&&n.fromList);let o=!1;if(e.kind==="issue"&&(o=this.showRailForFinding()),n&&n.showClaim&&(o=this.showRailForClaim()||o),this.view.applySelection(e),Q(this),n&&n.reveal&&e.kind==="edge"?this.view.revealEdge(e.id):n&&n.center&&n.reveal?this.view.revealNode(e.id,!!n.pulse):n&&n.center?this.view.centerOnNode(e.id,!!n.pulse):o&&e.kind!=="issue"&&this.view.keepInView({kind:e.kind,id:e.id}),this.announceSelection(),this.walk.followSelection(e),n&&n.open)if(this.walk.active&&this.walkOwns(e))this.walk.reopen(!!n.focusEditor);else{let i=this.locOf(e);i&&this.openLocation(i,!!n.focusEditor)}this.saveSoon()}showRailForClaim(){return this.railOpen?(this.railChosen=!0,!1):this.railMode==="docked"&&this.railChosen?!1:(this.railChosen=!0,this.setRailOpen(!0),!0)}selectionTab(e){return e&&this.railTab===e&&this.railShown()?e:"inspector"}clearSelection(){this.selection&&(this.selection=null,this.view.applySelection(null),Q(this),this.saveSoon())}locOf(e){if(!this.index)return null;if(e.kind==="node"){let o=this.index.nodeById.get(e.id);return o&&o.loc.file?o.loc:null}if(e.kind==="edge"){let o=this.index.edgeById.get(e.id);return o&&o.loc.file?o.loc:null}let n=this.index.issueById.get(e.id);return n&&n.loc.file?n.loc:null}selectedNodeId(){let e=this.selection;if(!e||!this.index)return null;if(e.kind==="node")return e.id;if(e.kind!=="issue")return null;let n=this.index.issueById.get(e.id);return n&&n.nodeIds.length?n.nodeIds[0]:null}announceSelection(){let e=this.selection;if(!(!e||!this.index)){if(Za()){let n=hd(this.index,e);n&&this.announce(n);return}if(e.kind==="node"){let n=this.view.labelOf(e.id);n&&this.announce("Selected "+n)}else if(e.kind==="issue"){let n=this.index.issueById.get(e.id);n&&this.announce("Finding "+(n.short?n.short+" ("+n.code+")":n.code)+", "+n.severity+" severity: "+n.title)}}}applyFilters(e){e(),X(this),this.index&&(this.view.render(),this.view.applySelection(this.selection)),Q(this),this.saveSoon()}clearFilters(){this.search.clear(),this.applyFilters(()=>this.filters.reset())}selectLane(e){if(!this.index)return;let n=this.index.roots(e);if(!n.length){this.announce("That phase has no steps.");return}this.select({kind:"node",id:n[0]},{center:!0,pulse:!0,fromList:"outline"})}zoomToSelection(){let e=this.selectedNodeId();e&&this.view.zoomToNode(e)}announce(e){this.liveEl.textContent=e}activateHit(e){e.kind==="node"?this.focusNode(e.id,{center:!0,pulse:!0,showClaim:!0}):this.focusIssue(e.id)}onKeyDown(e){uc(this,e)}onMessage(e){pc(this,e)}focusNode(e,n){if(this.index){if(!this.index.nodeById.has(e)){this.view.toast("Node not found in this graph");return}this.view.expandAncestors(e),this.select({kind:"node",id:e},{center:n?n.center!==!1:!0,pulse:n?!!n.pulse:!1,showClaim:!!(n&&n.showClaim)})}}focusIssue(e,n){if(!this.index)return;let o=this.index.issueById.get(e);if(o){for(let i of o.nodeIds)this.view.expandAncestors(i);this.select({kind:"issue",id:e},{fromList:n&&n.fromList}),this.view.frameIssue(e)}}openIssue(e,n=!1){if(this.focusIssue(e,{fromList:"issues"}),!this.selection||this.selection.kind!=="issue"||this.selection.id!==e)return;if(this.walk.active&&this.walkOwns(this.selection)){this.walk.reopen(n);return}let o=this.locOf({kind:"issue",id:e});o&&this.openLocation(o,n)}setFilters(e){this.applyFilters(()=>this.filters.patch(e))}setTheme(e){this.themes.apply(e)}getState(){return hc(this)}destroy(){if(!this.destroyed){this.destroyed=!0,this.saveSoon.cancel(),this.releaseRevealFocus&&this.releaseRevealFocus(),this.walk.destroy(),this.pending.clear();for(let e of this.disposers)try{e()}catch{}this.disposers=[],this.themes.destroy(),this.chrome.destroy(),this.walkBar.destroy(),this.view.destroy(),this.releasePage(),D(this.root),this.root.classList.remove("mlv-root")}}};var Jt=null;function Sb(){if(Jt)return Jt;let t=typeof window<"u"?window:{};if(typeof t.acquireVsCodeApi=="function")try{Jt=t.acquireVsCodeApi()}catch{Jt=null}return Jt}function Mb(){if(typeof document>"u"||!document.body)return"light";let t=document.body.className||"";return t.indexOf("vscode-high-contrast")>=0?"hc":t.indexOf("vscode-dark")>=0?"dark":"light"}function Lb(t){if(typeof window>"u")return()=>{};let e=n=>{let o=n&&n.data;o&&typeof o=="object"&&t(o)};return window.addEventListener("message",e),()=>window.removeEventListener("message",e)}function vc(){let t=Sb(),e={canOpenSource:!0};return{host:"vscode",theme:Mb(),capabilities:e,post(n){t&&typeof t.postMessage=="function"&&t.postMessage(n)},onMessage(n){return Lb(n)},saveState(n){t&&typeof t.setState=="function"&&t.setState(n)},loadState(){if(t&&typeof t.getState=="function"){let n=t.getState();return n&&typeof n=="object"?n:null}return null}}}var Ib="0.3.0";function Ab(t,e,n){if(!t)throw new Error("MLView.mountWorkflow: a root element is required");if(!n)throw new Error("MLView.mountWorkflow: a HostBridge is required");let o=new Ao(t,n);return o.setWorkflow(e),o}var Cb={vscode(){return vc()}};return Mc(Tb);})(); diff --git a/vscode-extension/package.json b/vscode-extension/package.json index 064a19a6..c6ecb3e7 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -49,14 +49,30 @@ "title": "Open Generated Diagram", "category": "MLView", "icon": "$(open-preview)" + }, + { + "command": "mlview.revealInDiagram", + "title": "Reveal in Diagram", + "category": "MLView" } ], "menus": { + "commandPalette": [ + { + "command": "mlview.revealInDiagram", + "when": "resourceScheme =~ /^(file|vscode-notebook-cell)$/ && resourcePath in mlview.citedFiles" + } + ], "editor/context": [ { "command": "mlview.openGeneratedDiagram", "when": "resourceFilename =~ /\\.mlview\\.json$/", "group": "mlview@0" + }, + { + "command": "mlview.revealInDiagram", + "when": "resourceScheme =~ /^(file|vscode-notebook-cell)$/ && resourcePath in mlview.citedFiles", + "group": "mlview@1" } ], "editor/title": [ diff --git a/vscode-extension/src/authoredPanel.ts b/vscode-extension/src/authoredPanel.ts index 0356a5f2..77ff7aab 100644 --- a/vscode-extension/src/authoredPanel.ts +++ b/vscode-extension/src/authoredPanel.ts @@ -2,14 +2,16 @@ import { createHash } from 'node:crypto'; import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import * as vscode from 'vscode'; -import { createNonce, findRootHint, rootHintJumpText, rootHintText, staleBannerText, staleJumpText, staleToastText, themeKindOf, toEditorLine, type RootHint } from './authoredSupport'; +import { blockedOpenText, createNonce, findRootHint, hintFolderName, rootHintJumpText, rootHintText, staleBannerText, staleJumpText, staleToastText, themeKindOf, toEditorLine, type OpenBlockReason, type RootHint } from './authoredSupport'; import { MAX_EXPORT_BYTES, parseExportFileMessage, saveExportedFile } from './exportDiagram'; +import { CitationIndex, type ClaimRef } from './citationIndex'; import { DependencySet, identity } from './fileIdentity'; import type { Logger } from './log'; import { buildRefinementPrompt, REFINE_INTENTS, toPosixRelative, type RefineIntent, type RefineSelection } from './refinePrompt'; import { displayIssue, displayText } from './displayText'; +import { RevealInDiagram, type CitationState, type EditorPlace, type RevealPanel } from './revealCommand'; import { canonicalJson, jsonDepth, lenientRevision, MAX_JSON_DEPTH, RevisionLineage, semanticJson, type Candidate, type Verdict } from './revisionLineage'; -import { ID_PATTERN, MAX_DOCUMENT_BYTES, MAX_SOURCE_BYTES, quoteMatches, readSourceBytes, trackedFiles, validateWorkflow, validateWorkflowStructure, type ValidatedWorkflow, type ValidationIssue, type WorkflowEvidence } from './workflowDocument'; +import { ID_PATTERN, MAX_DOCUMENT_BYTES, MAX_SOURCE_BYTES, quoteMatches, readSourceBytes, trackedFiles, validateWorkflow, validateWorkflowStructure, type StaleFile, type ValidatedWorkflow, type ValidationIssue, type WorkflowEvidence } from './workflowDocument'; export const AUTHORED_VIEW_TYPE = 'mlview.authoredDiagram'; export const OPEN_AUTHORED_COMMAND = 'mlview.openGeneratedDiagram'; const RELOAD_DEBOUNCE_MS = 120; @@ -17,6 +19,11 @@ const DIRTY_THROTTLE_MS = 150; const RETRY_DELAYS_MS = [250, 1000, 4000]; const TRANSIENT_CODES = new Set(['EBUSY', 'EAGAIN', 'EPERM', 'EACCES', 'EMFILE', 'ENFILE']); const REQUEST_ID = /^[A-Za-z0-9_-]{1,64}$/; +/** + * Viewer M3 (step 14): a reveal the page may not have received (it was hidden, so VS Code discarded + * it, or it was reloading) is posted again after the page's next `ready`, if that comes this soon. + */ +export const REVEAL_REPLAY_MS = 5000; /** How long a jump into a notebook waits for VS Code to create the cited cell's editor. */ const CELL_EDITOR_WAIT_MS = 500; /** @@ -133,6 +140,8 @@ interface PanelHooks { changed(): void; /** Write the registry now; resolves once it is stored. */ flush(): Promise; + /** Viewer M3 (step 14): the panel validated again; the cited-files context key follows. */ + citationsChanged(): void; } /** * One source jump: the revision and freshness it was decided against, and its place in the order @@ -143,6 +152,29 @@ interface Jump { revision: string; freshness: number; seq: number; + /** The webview's own sequence number for this open, when it sent one (the review walk does). */ + openSeq?: number; +} +/** + * What became of one source jump (viewer M3): opened; dropped because a newer jump, a new revision + * or a freshness change overtook it; or blocked, with the reason, the short sentence the webview + * shows and the warning an ordinary (non-walk) open still raises as a notification. + */ +type JumpOutcome = + | { outcome: 'done' } + | { outcome: 'cancelled' } + | { outcome: 'blocked'; reason: OpenBlockReason; message: string; warning: string }; +const CANCELLED: JumpOutcome = { outcome: 'cancelled' }; +/** + * The jump checks of one revision and freshness version (viewer M3, KI-09): per cited file, the + * file's identity on disk when it was checked (device, inode, size and change times) and whether it + * was stale then. A walk that steps through 27 citations of one notebook reads and hashes it once, + * and runs at most one full validation, instead of once per step. + */ +interface JumpChecks { + revision: string; + freshness: number; + files: Map; } /** Outcome of reading the artifact bytes from disk (never from an editor buffer). */ export type ArtifactRead = { kind: 'bytes'; bytes: Uint8Array } | { kind: 'missing' } | { kind: 'unreadable'; detail: string; transient?: boolean }; @@ -320,11 +352,18 @@ export class AuthoredDiagramController implements vscode.Disposable { private sweeps: Promise = Promise.resolve(0); /** Front tabs already reported as dead without a registry entry, so each is logged once. */ private readonly unmatched = new WeakSet(); - constructor(private readonly ctx: vscode.ExtensionContext, private readonly log: Logger, private readonly validator: typeof validateWorkflow = validateWorkflow, private readonly io: AuthoredPanelIo = defaultIo, private readonly settleMs: number = RECOVERY_SETTLE_MS) { } + /** Viewer M3 (step 14): MLView: Reveal in Diagram and the `mlview.citedFiles` context key. */ + readonly revealer: RevealInDiagram; + constructor(private readonly ctx: vscode.ExtensionContext, private readonly log: Logger, private readonly validator: typeof validateWorkflow = validateWorkflow, private readonly io: AuthoredPanelIo = defaultIo, private readonly settleMs: number = RECOVERY_SETTLE_MS) { + this.revealer = new RevealInDiagram(() => [...this.panels.values()], log); + } register(): vscode.Disposable[] { const watcher = vscode.workspace.createFileSystemWatcher('**/*'); const registered = [ vscode.commands.registerCommand(OPEN_AUTHORED_COMMAND, (uri?: vscode.Uri) => this.open(uri)), + // Viewer M3 (step 14): the way back from the code; its context key lists the cited files + // of the open panels (`panelsChanged`, `citationsChanged`). + this.revealer.register(), vscode.window.registerWebviewPanelSerializer(AUTHORED_VIEW_TYPE, { deserializeWebviewPanel: async (panel, state: unknown) => this.restore(panel, state) }), // Saves and watcher events change the files on disk: revalidate. vscode.workspace.onDidSaveTextDocument(doc => this.diskChanged(doc.uri)), @@ -389,10 +428,13 @@ export class AuthoredDiagramController implements vscode.Disposable { // A closed tab leaves the registry. When the host shuts down (`dispose`), the // panels stay registered: their tabs stay open for the next host. void this.recordPanels(); + // The reveal command's list of cited files follows; the last panel empties it. + this.revealer.panelsChanged(); } }, this.hooks, this.validator, this.io); this.panels.set(key, authored); void this.recordPanels(); + this.revealer.panelsChanged(); return authored; } private async restore(panel: vscode.WebviewPanel, state: unknown): Promise { @@ -410,7 +452,8 @@ export class AuthoredDiagramController implements vscode.Disposable { } private readonly hooks: PanelHooks = { changed: () => { void this.recordPanels(); }, - flush: () => this.recordPanels() + flush: () => this.recordPanels(), + citationsChanged: () => this.revealer.citationsChanged() }; /** * Queue a write of this window's registry entry (see `OPEN_PANELS_KEY`): every open panel, @@ -580,6 +623,7 @@ export class AuthoredDiagramController implements vscode.Disposable { for (const p of this.panels.values()) p.dispose(); this.panels.clear(); + this.revealer.dispose(); for (const d of this.disposables) d.dispose(); } @@ -620,7 +664,7 @@ function visibleEditorFor(document: vscode.TextDocument, timeoutMs: number): Pro }); } const listFiles = (rels: readonly string[]): string => rels.slice(0, 3).map(rel => displayText(rel, 200)).join(', ') + (rels.length > 3 ? `, and ${rels.length - 3} more` : ''); -class AuthoredPanel implements vscode.Disposable { +class AuthoredPanel implements vscode.Disposable, RevealPanel { private disposed = false; private ready = false; /** The displayed revision's latest validation. */ @@ -651,12 +695,30 @@ class AuthoredPanel implements vscode.Disposable { private rootHint: RootHint | undefined; /** The whole-range highlight of the last source jump; disposing it clears it from every editor. */ private highlight: vscode.TextEditorDecorationType | undefined; + /** The editor and selection the last source jump set, until the walk releases it (see `releaseJumpSelection`). */ + private jumpSelection: { editor: vscode.TextEditor; selection: vscode.Selection } | undefined; /** The number of the latest source jump (see `Jump`). */ private jumpSeq = 0; + /** + * The highest `seq` the webview page sent with an open (viewer M3); an open with a lower or equal + * one is superseded. A new page starts again from 0 (`ready`). + */ + private lastOpenSeq = 0; + /** The cached checks behind source jumps, for the current revision and freshness (`JumpChecks`). */ + private jumpChecks: JumpChecks | undefined; /** The editor group the last jump showed its source in (`navigationColumn`). */ private jumpColumn: vscode.ViewColumn | undefined; /** Editor groups the reader used outside the panel, most recent first (`navigationColumn`). */ private recentColumns: vscode.ViewColumn[] = []; + /** + * Viewer M3 (step 14): the claims the displayed revision cites, by file and range, for MLView: + * Reveal in Diagram. Built from the validated document whenever it changes. + */ + private citations: CitationIndex | undefined; + /** The cited files' identities on disk (realpath), for `citations` under the current root; rebuilt after each validation. */ + private citedIdentities: Promise> | undefined; + /** A reveal the page may not have received yet (`REVEAL_REPLAY_MS`). */ + private pendingReveal: { ref: ClaimRef; revision: string; until: number } | undefined; constructor(private readonly panel: vscode.WebviewPanel, private readonly artifact: vscode.Uri, private folder: vscode.WorkspaceFolder, private readonly ctx: vscode.ExtensionContext, private readonly log: Logger, private readonly onDispose: () => void, private readonly hooks: PanelHooks, private readonly validator: typeof validateWorkflow, private readonly io: AuthoredPanelIo) { this.artifactRel = toPosixRelative(folder.uri.fsPath, artifact.fsPath); this.dependencies.addPath(artifact.fsPath); @@ -855,6 +917,9 @@ class AuthoredPanel implements vscode.Disposable { if (verdict === 'adopt' || verdict === 'refresh') { this.rejection = undefined; this.lastValid = value; + // Viewer M3 (step 14): the citation index follows the validated document. + if (value && this.citations?.document !== value.document) + this.citations = CitationIndex.from(value.document); } else { this.rejection = this.rejectionFor(candidate, verdict); @@ -865,6 +930,9 @@ class AuthoredPanel implements vscode.Disposable { candidateFiles = value?.files ?? []; } } + // The cited files' identities are looked up again after every validation (a folder change + // can move the root, a rename can move a file). + this.citedIdentities = undefined; await this.rebuildDependencies(candidateTracked, candidateFiles); await this.computeDirty(); this.rootHint = await this.computeRootHint(); @@ -902,6 +970,9 @@ class AuthoredPanel implements vscode.Disposable { this.scheduleRetry(); else this.retryCount = 0; + // Viewer M3 (step 14): a new revision, or a changed or restored cited file, changes the + // reveal command's list of cited files. + this.hooks.citationsChanged(); } /** * The stale cited and inspected files of the displayed revision, each with its reason, so the @@ -1091,7 +1162,7 @@ class AuthoredPanel implements vscode.Disposable { if (version === this.dirtyVersion) this.dirty = result; } - private actionResult(requestId: string | undefined, action: 'exportFile' | 'copy' | 'refineWorkflow', outcome: 'done' | 'cancelled' | 'failed', extra: { message?: string; name?: string } = {}): void { + private actionResult(requestId: string | undefined, action: 'exportFile' | 'copy' | 'refineWorkflow' | 'openLocation', outcome: 'done' | 'cancelled' | 'failed' | 'blocked', extra: { message?: string; name?: string; reason?: OpenBlockReason; seq?: number } = {}): void { if (requestId) this.post({ v: 1, type: 'actionResult', requestId, action, outcome, ...extra }); } @@ -1104,6 +1175,8 @@ class AuthoredPanel implements vscode.Disposable { const requestId = typeof m.requestId === 'string' && REQUEST_ID.test(m.requestId) ? m.requestId : undefined; if (m.type === 'ready') { this.ready = true; + // A new page numbers its opens from the start again. + this.lastOpenSeq = 0; this.post({ v: 1, type: 'init', @@ -1131,10 +1204,20 @@ class AuthoredPanel implements vscode.Disposable { this.lastPostedBanner = message; if (message) this.post({ v: 1, type: 'workflowError', message, retained: this.lineage.displayed !== null, codes }); + // Viewer M3 (step 14): a reveal the old page may have missed, after the document. + this.replayReveal(); return; } if (m.type === 'openLocation') { - await this.openEvidence(m); + await this.openEvidence(m, requestId); + return; + } + // Viewer M3: the review walk ended (`end`), or moved to a claim it opens nothing for + // (`clear`: no quote, or a claim the reader only selected). Its highlight goes, and a walk + // open still on its way (a notebook waiting for its cell editor) no longer lands. + if (m.type === 'walk') { + if (m.state === 'end' || m.state === 'clear') + this.endWalk(); return; } if (m.type === 'workspaceHint') { @@ -1246,19 +1329,54 @@ class AuthoredPanel implements vscode.Disposable { void vscode.window.showInformationMessage(`MLView refinement prompt copied. Paste it into the assistant that authored this diagram.${note}`); this.actionResult(requestId, 'refineWorkflow', 'done'); } - private async openEvidence(m: Record): Promise { + /** + * One `openLocation` request. `focus: true` is the explicit "open and go to the editor" gesture; + * every other open keeps the keyboard on the diagram. Viewer M3, for the review walk: `seq` + * (a positive integer, increasing per page) drops an open that a later one already replaced; + * `requestId` asks for one `actionResult` (`done`, `blocked` with the reason, `cancelled` when it + * was superseded, `failed` when VS Code could not show the file); `walk: true` raises no + * notification for a blocked open, whose reason goes back to the webview instead; `highlight: + * false` selects and reveals the range without the whole-range decoration. + */ + private async openEvidence(m: Record, requestId: string | undefined): Promise { const id = typeof m.evidenceId === 'string' ? m.evidenceId : undefined; - // `focus: true` is the explicit "open and go to the editor" gesture; every other open keeps - // the keyboard on the diagram. const focus = m.focus === true; + const walk = m.walk === true; + const highlight = m.highlight !== false; + const seq = typeof m.seq === 'number' && Number.isSafeInteger(m.seq) && m.seq > 0 ? m.seq : undefined; + const answer = (outcome: JumpOutcome | { outcome: 'failed'; message: string }): void => { + const extra = outcome.outcome === 'blocked' ? { message: outcome.message, reason: outcome.reason } : outcome.outcome === 'failed' ? { message: outcome.message } : {}; + this.actionResult(requestId, 'openLocation', outcome.outcome, seq === undefined ? extra : { ...extra, seq }); + }; + if (seq !== undefined) { + // Superseded: the page already sent a later open (messages can overtake one another + // across a page reload, never within one page, but the order is the page's to decide). + if (seq <= this.lastOpenSeq) { + answer(CANCELLED); + return; + } + this.lastOpenSeq = seq; + } const shown = this.lastValid; const evidence = shown?.document.evidence.find(x => x.id === id); - if (!shown || !evidence) + if (!shown || !evidence) { + if (walk) { + this.clearHighlight(); + this.releaseJumpSelection(); + } + answer({ outcome: 'blocked', reason: 'unknown', message: blockedOpenText('unknown', ''), warning: '' }); return; - const jump: Jump = { revision: shown.document.revision.id, freshness: this.freshnessVersion, seq: ++this.jumpSeq }; + } + const jump: Jump = { revision: shown.document.revision.id, freshness: this.freshnessVersion, seq: ++this.jumpSeq, ...(seq !== undefined ? { openSeq: seq } : {}) }; let behind = false; + let outcome: JumpOutcome; try { - await this.jumpTo(evidence, shown, jump, focus, () => { behind = true; }); + outcome = await this.jumpTo(evidence, shown, jump, { focus, highlight }, () => { behind = true; }); + } + catch (error) { + this.log.warn(`source navigation failed: ${error instanceof Error ? error.message : String(error)}`); + answer({ outcome: 'failed', message: `VS Code could not show ${displayText(evidence.file, 200)}.` }); + return; } finally { // The jump's own check found files the panel has not caught up with (no watcher event @@ -1266,52 +1384,146 @@ class AuthoredPanel implements vscode.Disposable { if (behind) this.sourceChanged(); } + // Viewer M3: an open from the review walk raises no notification (no toast storm while + // stepping through changed files); the webview shows the reason in its walk bar instead. + if (outcome.outcome === 'blocked' && !walk && outcome.warning) + void vscode.window.showWarningMessage(outcome.warning); + // Viewer M3 (step 11): a blocked walk open also clears the previous claim's highlight, so + // the editor beside never shows an earlier claim's lines while the walk says this claim's + // file was not opened. A blocked Enter, double-click or Open link keeps it, as in M2. + if (outcome.outcome === 'blocked' && walk) { + this.clearHighlight(); + this.releaseJumpSelection(); + } + answer(outcome); } - private async jumpTo(evidence: WorkflowEvidence, shown: ValidatedWorkflow, jump: Jump, focus: boolean, markBehind: () => void): Promise { - let fresh: ValidatedWorkflow; + private async jumpTo(evidence: WorkflowEvidence, shown: ValidatedWorkflow, jump: Jump, options: { focus: boolean; highlight: boolean }, markBehind: () => void): Promise { + const checked = await this.checkCitedFile(evidence, shown, jump, markBehind); + if ('outcome' in checked) + return checked; + const file = displayText(evidence.file, 200); + const staleFile = checked.stale; + if (staleFile) { + const hint = this.rootHint; + if (hint && hint.files.includes(evidence.file)) + return { outcome: 'blocked', reason: 'elsewhere', message: blockedOpenText('elsewhere', file, { folder: listFiles([hintFolderName(hint)]) }), warning: rootHintJumpText(evidence.id, file, hint, listFiles) }; + return { outcome: 'blocked', reason: staleFile.reason, message: blockedOpenText(staleFile.reason, file, { revisionId: jump.revision }), warning: staleJumpText(evidence.id, file, staleFile.reason, jump.revision) }; + } + const open = await this.findOpenDocument(evidence); + if (!this.navigationCurrent(jump)) + return CANCELLED; + if (!this.unsavedTextStillCites(evidence, open)) + return { outcome: 'blocked', reason: 'unsaved', message: blockedOpenText('unsaved', file), warning: `MLView: unsaved changes in ${file} no longer contain the lines cited by evidence ${evidence.id}; save or revert the file, then try again.` }; + return this.navigate(evidence, jump, open, options); + } + /** + * Whether the cited file is stale, decided once per revision, freshness version and file + * identity (`JumpChecks`): from the cache while the file's identity on disk is unchanged; else + * by hashing that one file when the displayed revision's validation read it fresh; else by a + * full validation, whose verdicts are cached for every cited file. The identities are taken + * before the file is read, so a cached verdict is never newer than the bytes it was decided on. + */ + private async checkCitedFile(evidence: WorkflowEvidence, shown: ValidatedWorkflow, jump: Jump, markBehind: () => void): Promise { + const signature = await this.fileSignature(evidence.file); + const cached = this.cachedCheck(jump, evidence.file, signature); + if (cached) { + if (!this.navigationCurrent(jump)) + return CANCELLED; + return { stale: cached.stale }; + } + if (!this.navigationCurrent(jump)) + return CANCELLED; if (await this.unchangedSinceValidation(shown, evidence)) { // The cited file's bytes are the ones the displayed revision was validated against, so // the quote still matches: no full revalidation for this jump. if (!this.navigationCurrent(jump)) - return; - fresh = shown; + return CANCELLED; + this.storeChecks(jump, new Map([[evidence.file, signature]]), shown.stale); + return { stale: undefined }; } - else { - const displayed = this.lineage.displayed; - let result: ValidationResult | undefined; - try { - result = await this.validate(shown.document, displayed && !displayed.verified ? displayed.baseline : undefined); - } - catch { - result = { issues: [{ path: '$', message: 'validation failed' }] }; - } - if (!result || !this.navigationCurrent(jump)) - return; - if (!result.value) { - const first = result.issues[0]; - void vscode.window.showWarningMessage(`MLView: evidence ${evidence.id} could not be checked (${first ? displayIssue(first) : 'unknown problem'}); source navigation was stopped.`); - return; - } - fresh = result.value; - if (JSON.stringify(fresh.stale) !== JSON.stringify(shown.stale)) - markBehind(); + const cited = [...new Set(shown.document.evidence.map(e => e.file))]; + const signatures = new Map(await Promise.all(cited.map(async rel => [rel, rel === evidence.file ? signature : await this.fileSignature(rel)] as const))); + const displayed = this.lineage.displayed; + let result: ValidationResult | undefined; + try { + result = await this.validate(shown.document, displayed && !displayed.verified ? displayed.baseline : undefined); } - const staleFile = fresh.stale.find(s => s.rel === evidence.file); - if (staleFile) { - const hint = this.rootHint; - void vscode.window.showWarningMessage(hint && hint.files.includes(evidence.file) - ? rootHintJumpText(evidence.id, displayText(evidence.file, 200), hint, listFiles) - : staleJumpText(evidence.id, displayText(evidence.file, 200), staleFile.reason, jump.revision)); - return; + catch { + result = { issues: [{ path: '$', message: 'validation failed' }] }; + } + if (!result || !this.navigationCurrent(jump)) + return CANCELLED; + if (!result.value) { + const first = result.issues[0]; + const issue = first ? displayIssue(first) : 'unknown problem'; + return { outcome: 'blocked', reason: 'unchecked', message: blockedOpenText('unchecked', displayText(evidence.file, 200), { issue }), warning: `MLView: evidence ${evidence.id} could not be checked (${issue}); source navigation was stopped.` }; + } + const fresh = result.value; + // The panel is behind (no watcher event yet): it validates again and moves to a new + // freshness version, so these verdicts are not kept. + if (JSON.stringify(fresh.stale) !== JSON.stringify(shown.stale)) + markBehind(); + else + this.storeChecks(jump, signatures, fresh.stale); + return { stale: fresh.stale.find(s => s.rel === evidence.file) }; + } + /** A cited file's identity on disk, or why it has none; any change to the file changes it. */ + private async fileSignature(rel: string): Promise { + try { + const stat = await fs.stat(path.resolve(this.folder.uri.fsPath, rel), { bigint: true }); + return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`; } - const open = await this.findOpenDocument(evidence); - if (!this.navigationCurrent(jump)) + catch (error) { + const code = (error as NodeJS.ErrnoException | undefined)?.code; + return `error:${typeof code === 'string' ? code : 'unknown'}`; + } + } + private cachedCheck(jump: Jump, rel: string, signature: string): { stale: StaleFile | undefined } | undefined { + const checks = this.jumpChecks; + if (!checks || checks.revision !== jump.revision || checks.freshness !== jump.freshness) + return undefined; + const entry = checks.files.get(rel); + return entry && entry.signature === signature ? { stale: entry.stale } : undefined; + } + /** Keep the verdicts for `signatures` while the jump's revision and freshness are still current. */ + private storeChecks(jump: Jump, signatures: ReadonlyMap, stale: readonly StaleFile[]): void { + if (this.disposed || this.lastValid?.document.revision.id !== jump.revision || this.freshnessVersion !== jump.freshness) return; - if (!this.unsavedTextStillCites(evidence, open)) { - void vscode.window.showWarningMessage(`MLView: unsaved changes in ${displayText(evidence.file, 200)} no longer contain the lines cited by evidence ${evidence.id}; save or revert the file, then try again.`); + if (!this.jumpChecks || this.jumpChecks.revision !== jump.revision || this.jumpChecks.freshness !== jump.freshness) + this.jumpChecks = { revision: jump.revision, freshness: jump.freshness, files: new Map() }; + for (const [rel, signature] of signatures) + this.jumpChecks.files.set(rel, { signature, stale: stale.find(s => s.rel === rel) }); + } + /** + * Viewer M3: the walk ended, or moved to a claim with nothing to open. Its highlight is cleared + * and an open still on its way is dropped. + */ + private endWalk(): void { + this.jumpSeq++; + this.clearHighlight(); + this.releaseJumpSelection(); + } + /** + * Viewer M3 (live check): the walk ended, cleared or was blocked, and its highlight went, but the + * editor kept the earlier claim's lines selected (drawn in the inactive-selection colour). When + * that editor is still visible and its selection is still exactly the one the jump set, it is + * collapsed to the start of the cited range; a selection the reader changed is left alone. + */ + private releaseJumpSelection(): void { + const set = this.jumpSelection; + this.jumpSelection = undefined; + if (!set || !vscode.window.visibleTextEditors.includes(set.editor)) return; + const same = (a: vscode.Position, b: vscode.Position): boolean => a.line === b.line && a.character === b.character; + const current = set.editor.selection; + if (!current || !same(current.anchor, set.selection.anchor) || !same(current.active, set.selection.active)) + return; + try { + set.editor.selection = new vscode.Selection(set.selection.anchor, set.selection.anchor); + } + catch (error) { + this.log.warn(`could not collapse the walk's selection: ${error instanceof Error ? error.message : String(error)}`); } - await this.navigate(evidence, jump, open, focus); } /** * True when the cited file is fresh in the displayed revision's last validation and its bytes @@ -1361,9 +1573,13 @@ class AuthoredPanel implements vscode.Disposable { } return true; } - /** The jump may still act: same revision and freshness, and no newer jump has started. */ + /** + * The jump may still act: same revision and freshness, no newer jump has started, and (for an + * open the webview numbered) the page has sent no later open (viewer M3). + */ private navigationCurrent(jump: Jump): boolean { - return !this.disposed && this.lastValid?.document.revision.id === jump.revision && this.freshnessVersion === jump.freshness && this.jumpSeq === jump.seq; + return !this.disposed && this.lastValid?.document.revision.id === jump.revision && this.freshnessVersion === jump.freshness && this.jumpSeq === jump.seq + && (jump.openSeq === undefined || jump.openSeq === this.lastOpenSeq); } /** * The panel's own editor group. VS Code revives a restored panel with column 0 and reports its @@ -1426,23 +1642,23 @@ class AuthoredPanel implements vscode.Disposable { } /** * Open the cited source beside the panel, select the whole cited range and highlight it. - * `focus` false (the default gesture) keeps the keyboard on the diagram. + * `focus` false (the default gesture) keeps the keyboard on the diagram; `highlight` false + * leaves the whole-range decoration off. */ - private async navigate(e: WorkflowEvidence, jump: Jump, open: { text?: vscode.TextDocument; notebook?: vscode.NotebookDocument }, focus: boolean): Promise { + private async navigate(e: WorkflowEvidence, jump: Jump, open: { text?: vscode.TextDocument; notebook?: vscode.NotebookDocument }, options: { focus: boolean; highlight: boolean }): Promise { const uri = open.notebook?.uri ?? open.text?.uri ?? vscode.Uri.file(path.join(this.folder.uri.fsPath, e.file)); const start = toEditorLine(e.line), end = toEditorLine(e.endLine); - if (e.cell !== undefined) { - await this.navigateCell(e.cell, uri, start, end, jump, focus); - return; - } + if (e.cell !== undefined) + return this.navigateCell(e.cell, displayText(e.file, 200), uri, start, end, jump, options); const doc = await vscode.workspace.openTextDocument(uri); if (!this.navigationCurrent(jump)) - return; - const editor = await vscode.window.showTextDocument(doc, { preview: true, preserveFocus: !focus, viewColumn: this.navigationColumn(doc) }); + return CANCELLED; + const editor = await vscode.window.showTextDocument(doc, { preview: true, preserveFocus: !options.focus, viewColumn: this.navigationColumn(doc) }); this.noteJumpColumn(editor.viewColumn); if (!this.navigationCurrent(jump)) - return; - this.showRange(editor, citedRange(doc, start, end)); + return CANCELLED; + this.showRange(editor, citedRange(doc, start, end), options.highlight); + return { outcome: 'done' }; } /** * A notebook citation: show the notebook with the cited cell selected and revealed, then @@ -1450,36 +1666,39 @@ class AuthoredPanel implements vscode.Disposable { * only once the cell is drawn; when it does not appear in time, the cell stays selected and * revealed without the line highlight. */ - private async navigateCell(index: number, uri: vscode.Uri, start: number, end: number, jump: Jump, focus: boolean): Promise { + private async navigateCell(index: number, file: string, uri: vscode.Uri, start: number, end: number, jump: Jump, options: { focus: boolean; highlight: boolean }): Promise { const notebook = await vscode.workspace.openNotebookDocument(uri); if (!this.navigationCurrent(jump)) - return; + return CANCELLED; // VS Code clamps cellAt's index, so a removed cell must be detected by count. - if (index >= notebook.cellCount) { - void vscode.window.showWarningMessage(`MLView: notebook cell ${index} no longer exists.`); - return; - } + if (index >= notebook.cellCount) + return { outcome: 'blocked', reason: 'cell-missing', message: blockedOpenText('cell-missing', file, { cell: index }), warning: `MLView: notebook cell ${index} no longer exists.` }; const cell = notebook.cellAt(index); const cells = new vscode.NotebookRange(index, index + 1); - const notebookEditor = await vscode.window.showNotebookDocument(notebook, { preview: true, preserveFocus: !focus, viewColumn: this.navigationColumn(cell.document, notebook), selections: [cells] }); + const notebookEditor = await vscode.window.showNotebookDocument(notebook, { preview: true, preserveFocus: !options.focus, viewColumn: this.navigationColumn(cell.document, notebook), selections: [cells] }); this.noteJumpColumn(notebookEditor.viewColumn); if (!this.navigationCurrent(jump)) - return; + return CANCELLED; notebookEditor.revealRange(cells, vscode.NotebookEditorRevealType.InCenterIfOutsideViewport); const cellEditor = await visibleEditorFor(cell.document, CELL_EDITOR_WAIT_MS); // A newer jump may have started during the wait: it owns the selection and the highlight. if (!this.navigationCurrent(jump)) - return; + return CANCELLED; if (cellEditor) - this.showRange(cellEditor, citedRange(cell.document, start, end)); + this.showRange(cellEditor, citedRange(cell.document, start, end), options.highlight); else this.clearHighlight(); + return { outcome: 'done' }; } - /** Select the whole range, reveal it, and move the highlight to it. */ - private showRange(editor: vscode.TextEditor, range: vscode.Range): void { - editor.selection = new vscode.Selection(range.start, range.end); + /** Select the whole range, reveal it, and move the highlight to it (`highlight` false: only clear the old one). */ + private showRange(editor: vscode.TextEditor, range: vscode.Range, highlight = true): void { + const selection = new vscode.Selection(range.start, range.end); + editor.selection = selection; + this.jumpSelection = { editor, selection }; editor.revealRange(range, vscode.TextEditorRevealType.InCenter); this.clearHighlight(); + if (!highlight) + return; this.highlight = vscode.window.createTextEditorDecorationType(HIGHLIGHT_STYLE()); editor.setDecorations(this.highlight, [range]); } @@ -1487,6 +1706,120 @@ class AuthoredPanel implements vscode.Disposable { this.highlight?.dispose(); this.highlight = undefined; } + /* ── viewer M3 (step 14): MLView: Reveal in Diagram (see revealCommand.ts) ── */ + + /** The displayed revision's title, for the diagram picker. */ + documentTitle(): string { + return this.lastValid?.document.title ?? this.panel.title; + } + artifactPath(): string { + return this.artifactRel; + } + /** + * The displayed revision's cited files that are unchanged since publishing, as absolute paths + * under this panel's workspace folder: the reveal command's context key (M3 review, F1). A stale + * file (changed, missing, unreadable, too large, or found only under the folder the root hint + * names) is left out, as the command would not match it. + */ + citedFiles(): string[] { + const index = this.citations; + const shown = this.lastValid; + if (this.disposed || !index || !shown || index.document !== shown.document) + return []; + const stale = new Set(shown.stale.map(s => s.rel)); + const root = this.folder.uri.fsPath; + return index.files().filter(rel => !stale.has(rel)).map(rel => path.resolve(root, rel)); + } + /** + * What the displayed revision says about the reader's file: not cited; a cited file found in the + * folder the root hint names instead of the workspace root; cited but stale (changed, missing, + * unreadable or too large since publishing); cited in the other form (notebook cells + * against a text editor); cited, but no range still has its quote at the cited lines of the + * editor's text (unsaved edits moved them, or a change nothing reported yet); or cited, with the + * ranges that still hold. The file is matched by its workspace-relative path, then by identity + * on disk (a symlinked folder, a case alias). + */ + async citationState(place: EditorPlace): Promise { + const index = this.citations; + const shown = this.lastValid; + if (this.disposed || !index || !shown || index.document !== shown.document) + return { kind: 'none' }; + const rel = await this.citedRel(index, place.fsPath); + if (this.disposed || this.citations !== index) + return { kind: 'none' }; + if (!rel) { + // The parent-folder case: the file is a cited one, unchanged, in the folder the root + // hint names, not at the workspace root the diagram is validated against. + const hint = this.rootHint; + const underHint = hint ? toPosixRelative(hint.base, place.fsPath) : undefined; + return hint && underHint && hint.files.includes(underHint) && index.ranges(underHint).length + ? { kind: 'elsewhere', rel: underHint, folder: hintFolderName(hint) } + : { kind: 'none' }; + } + const stale = shown.stale.find(s => s.rel === rel); + if (stale) + return { kind: 'stale', rel, reason: stale.reason, revision: index.revision }; + const ranges = index.ranges(rel); + const sameForm = ranges.filter(range => (range.evidence.cell !== undefined) === place.notebook); + if (!sameForm.length) + return { kind: 'form', rel, cells: ranges.some(range => range.evidence.cell !== undefined) }; + const holding = sameForm.filter(range => { + const lines = place.linesFor(range.evidence); + return lines !== undefined && quoteMatches(range.evidence.quote, lines, range.evidence.line, range.evidence.endLine); + }); + if (!holding.length) + return { kind: 'moved', rel, dirty: place.dirty }; + return { kind: 'cited', rel, revision: index.revision, ranges: holding }; + } + /** The cited file `fsPath` is, as the evidence records spell it, or undefined. */ + private async citedRel(index: CitationIndex, fsPath: string): Promise { + const root = this.folder.uri.fsPath; + const lexical = toPosixRelative(root, fsPath); + if (index.ranges(lexical).length) + return lexical; + if (!this.citedIdentities) { + const files = index.files(); + this.citedIdentities = Promise.all(files.map(rel => identity(path.resolve(root, rel)))).then(ids => new Map(ids.map((id, i) => [id, files[i]!]))); + } + const [ids, target] = await Promise.all([this.citedIdentities, identity(fsPath)]); + return ids.get(target); + } + /** A change is waiting to be checked: validate now, so the answer is about the files as they are. */ + async settle(): Promise { + if (this.pendingCheck && !this.disposed) + await this.reload(); + } + recheck(): void { + this.sourceChanged(); + } + /** + * Show the claim in the diagram: post `reveal {kind, id}` (the webview selects it, brings it into + * view and shows it in the Selection tab), and move the keyboard focus to this panel, in its own + * group. This is the one place the viewer moves the focus by itself: the reader asked for the + * diagram. False when the displayed revision is no longer `revision` or lacks the claim. + * + * VS Code discards a hidden panel's page (this panel does not retain its context), so a page + * that was hidden or reloading may miss the frame: it is posted again after the page's next + * `ready`, if that comes within REVEAL_REPLAY_MS. + */ + revealClaim(ref: ClaimRef, revision: string): boolean { + const index = this.citations; + if (this.disposed || !index || index.revision !== revision || this.lastValid?.document !== index.document || !index.claim(ref)) + return false; + this.pendingReveal = { ref: { kind: ref.kind, id: ref.id }, revision, until: Date.now() + REVEAL_REPLAY_MS }; + if (this.ready) + this.post({ v: 1, type: 'reveal', kind: ref.kind, id: ref.id }); + this.panel.reveal(undefined, false); + return true; + } + /** After a page's `ready`: post the reveal it may have missed, once, while it is recent and its revision is still shown. */ + private replayReveal(): void { + const pending = this.pendingReveal; + this.pendingReveal = undefined; + if (!pending || Date.now() > pending.until || this.citations?.revision !== pending.revision || !this.citations.claim(pending.ref)) + return; + this.post({ v: 1, type: 'reveal', kind: pending.ref.kind, id: pending.ref.id }); + } private post(message: unknown): void { if (!this.disposed) void this.panel.webview.postMessage(message); @@ -1496,8 +1829,18 @@ class AuthoredPanel implements vscode.Disposable { * theme and capabilities on the bridge before the viewer mounts, mounts on the first * `workflow`, and shows `workflowError` banners until then, in a `
`. After the mount the
      * viewer's own listener applies every later frame, and draws the banner itself.
+     *
+     * Viewer M3: VS Code's webview host forwards every keydown to the workbench from a listener on
+     * the page's window, even a key the page already handled, and the workbench runs any keybinding
+     * that matches. MEASURED live (VS Code 1.139): one Escape on a card collapsed the bottom sheet
+     * and also dismissed a VS Code notification. An Escape the viewer acted on (it collapsed the
+     * sheet, closed the legend, cleared the selection) marks the event handled, and a listener on
+     * the document stops it there, before it reaches the window: one Escape, one action. It is added
+     * once the viewer has mounted, so it runs after the viewer's own document listeners. (A listener
+     * on the window would come too late: VS Code's forwarder is attached to that window before this
+     * page's scripts run, and it stays attached; checked live.)
      */
-    private render(): void { const nonce = createNonce(); const script = this.panel.webview.asWebviewUri(vscode.Uri.joinPath(this.ctx.extensionUri, 'media', 'mlview.js')); const style = this.panel.webview.asWebviewUri(vscode.Uri.joinPath(this.ctx.extensionUri, 'media', 'mlview.css')); this.panel.webview.html = `
`; } + private render(): void { const nonce = createNonce(); const script = this.panel.webview.asWebviewUri(vscode.Uri.joinPath(this.ctx.extensionUri, 'media', 'mlview.js')); const style = this.panel.webview.asWebviewUri(vscode.Uri.joinPath(this.ctx.extensionUri, 'media', 'mlview.css')); this.panel.webview.html = `
`; } dispose(): void { if (this.disposed) return; @@ -1505,6 +1848,10 @@ class AuthoredPanel implements vscode.Disposable { this.scheduler.dispose(); this.cancelRetry(); this.clearHighlight(); + this.jumpSelection = undefined; + this.citations = undefined; + this.citedIdentities = undefined; + this.pendingReveal = undefined; if (this.dirtyTimer !== undefined) { clearTimeout(this.dirtyTimer); this.dirtyTimer = undefined; diff --git a/vscode-extension/src/authoredSupport.ts b/vscode-extension/src/authoredSupport.ts index efaa0e1f..590d01c8 100644 --- a/vscode-extension/src/authoredSupport.ts +++ b/vscode-extension/src/authoredSupport.ts @@ -84,6 +84,33 @@ export function staleJumpText(evidenceId: string, file: string, reason: StaleRea return `MLView: evidence ${evidenceId} cites ${file}, ${why}; navigation to it is blocked.`; } +/** + * Why the host did not open a cited range (viewer M3): a stale reason, the root hint's `elsewhere`, + * unsaved text that lost the cited lines, a notebook cell that no longer exists, a file that could + * not be checked, or evidence the displayed revision does not have. + */ +export type OpenBlockReason = StaleReasonWord | 'elsewhere' | 'unsaved' | 'cell-missing' | 'unchecked' | 'unknown'; + +/** + * The short sentence a blocked open answers with (`actionResult.message`), for the review walk's + * bar: what is wrong with the file and that nothing was opened. `file` is already bounded and + * escaped (displayText); the cell index is the contract's zero-based one, as the viewer shows it. + */ +export function blockedOpenText(reason: OpenBlockReason, file: string, detail: { revisionId?: string; folder?: string; cell?: number; issue?: string } = {}): string { + const revision = detail.revisionId ? `revision ${detail.revisionId}` : 'the revision'; + switch (reason) { + case 'changed': return `${file} changed after ${revision} was published; not opened.`; + case 'missing': return `${file} is missing since ${revision} was published; not opened.`; + case 'unreadable': return `${file} can no longer be read; not opened.`; + case 'too-large': return `${file} has grown past the size MLView can check; not opened.`; + case 'elsewhere': return `${file} is not in the workspace root; it is unchanged in ${detail.folder ?? 'another folder'}. Not opened.`; + case 'unsaved': return `Unsaved changes in ${file} no longer contain the cited lines; not opened. Save or revert the file.`; + case 'cell-missing': return `Cell ${detail.cell ?? '?'} of ${file} no longer exists; not opened.`; + case 'unchecked': return `${file} could not be checked (${detail.issue ?? 'unknown problem'}); not opened.`; + default: return 'This evidence is not in the displayed revision; not opened.'; + } +} + /** A folder under the workspace root where the missing cited files exist with their published hashes. */ export interface RootHint { /** Absolute path of the folder the citations resolve against. */ diff --git a/vscode-extension/src/citationIndex.ts b/vscode-extension/src/citationIndex.ts new file mode 100644 index 00000000..f61409fa --- /dev/null +++ b/vscode-extension/src/citationIndex.ts @@ -0,0 +1,210 @@ +/** + * Viewer M3 (roadmap step 14), MLView: Reveal in Diagram: which claims of a validated revision cite + * a line of a file. + * + * Built from the document's evidence records only: the files, line ranges and notebook cells the + * model quoted, and the steps, connections and findings that cite each record (a finding's + * counter-evidence included). It reads no source code and infers nothing: a line is cited when it + * lies inside a range a claim quotes. The panel decides which ranges still hold (the file is not + * stale and the editor's text still has the quote at the cited lines) before it asks this index. + */ +import { displayText } from './displayText'; +import type { EvidenceBasis, WorkflowDocument, WorkflowEvidence } from './workflowDocument'; + +/** The webview's claim kinds: a step, a connection, a finding. */ +export type ClaimKind = 'node' | 'edge' | 'issue'; +export interface ClaimRef { + kind: ClaimKind; + id: string; +} +export interface CitedClaim extends ClaimRef { + /** The step's or connection's label, or the finding's title, as authored. */ + title: string; + /** `F1`, `F2`… by document order, as the diagram labels findings (viewer M2); findings only. */ + label?: string; + severity?: 'low' | 'medium' | 'high'; + basis: EvidenceBasis; + /** + * The phase labels the claim belongs to, in document order: a step's phase, a connection's + * source step's phase, and every phase a finding touches through the steps and connections it + * cites (the PR #14 rule the phase counts use). + */ + phases: string[]; + /** A connection's two ends, as their step labels. */ + ends?: [string, string]; + /** Place in document order among the claims of its kind. */ + order: number; +} +/** One cited range and the claims that cite it. */ +export interface CitedRange { + evidence: WorkflowEvidence; + claims: { claim: CitedClaim; counter: boolean }[]; +} +/** A claim found for a place in a file, with the range that cites it and how far that range is. */ +export interface ClaimMatch { + claim: CitedClaim; + evidence: WorkflowEvidence; + /** The range is the finding's counter-evidence. */ + counter: boolean; + /** Cells between the range and the place (notebooks), then lines between them; 0 and 0 inside. */ + cells: number; + lines: number; + /** The range lies above the place (when `lines` > 0). */ + above: boolean; +} +const KIND_ORDER: Record = { node: 0, edge: 1, issue: 2 }; +/** Steps, then connections, then findings; each in document order. */ +const byKind = (a: CitedClaim, b: CitedClaim): number => KIND_ORDER[a.kind] - KIND_ORDER[b.kind] || a.order - b.order; +export class CitationIndex { + private constructor( + readonly document: WorkflowDocument, + private readonly byFile: ReadonlyMap, + private readonly claims: ReadonlyMap + ) { } + /** The index of a validated document: its references are known to resolve. */ + static from(document: WorkflowDocument): CitationIndex { + const phaseLabel = new Map(document.phases.map(phase => [phase.id, phase.label])); + const phaseRank = new Map(document.phases.map((phase, rank) => [phase.id, rank])); + const nodes = new Map(document.nodes.map(node => [node.id, node])); + const edges = new Map(document.edges.map(edge => [edge.id, edge])); + const labels = (ids: Iterable): string[] => [...new Set(ids)].filter(id => phaseLabel.has(id)).sort((a, b) => phaseRank.get(a)! - phaseRank.get(b)!).map(id => phaseLabel.get(id)!); + const claims = new Map(); + const cites = new Map(); + const cite = (claim: CitedClaim, ids: readonly string[] | undefined, counter: boolean): void => { + for (const id of new Set(ids ?? [])) { + const list = cites.get(id) ?? []; + if (!list.some(entry => entry.claim === claim)) + list.push({ claim, counter }); + cites.set(id, list); + } + }; + document.nodes.forEach((node, order) => { + const claim: CitedClaim = { kind: 'node', id: node.id, title: node.label, basis: node.basis, phases: labels([node.phase]), order }; + claims.set(key(claim), claim); + cite(claim, node.evidence, false); + }); + document.edges.forEach((edge, order) => { + const source = nodes.get(edge.source); + const target = nodes.get(edge.target); + const claim: CitedClaim = { kind: 'edge', id: edge.id, title: edge.label, basis: edge.basis, phases: labels(source ? [source.phase] : []), ends: [source?.label ?? edge.source, target?.label ?? edge.target], order }; + claims.set(key(claim), claim); + cite(claim, edge.evidence, false); + }); + document.findings.forEach((finding, order) => { + const touched = [ + ...finding.nodeIds.map(id => nodes.get(id)?.phase), + ...(finding.edgeIds ?? []).map(id => nodes.get(edges.get(id)?.source ?? '')?.phase) + ].filter((phase): phase is string => typeof phase === 'string'); + const claim: CitedClaim = { kind: 'issue', id: finding.id, title: finding.title, label: `F${order + 1}`, severity: finding.severity, basis: finding.basis, phases: labels(touched), order }; + claims.set(key(claim), claim); + cite(claim, finding.evidence, false); + cite(claim, finding.counterEvidence, true); + }); + const byFile = new Map(); + for (const evidence of document.evidence) { + const cited = (cites.get(evidence.id) ?? []).slice().sort((a, b) => byKind(a.claim, b.claim)); + if (!cited.length) + continue; + const list = byFile.get(evidence.file) ?? []; + list.push({ evidence, claims: cited }); + byFile.set(evidence.file, list); + } + return new CitationIndex(document, byFile, claims); + } + get revision(): string { + return this.document.revision.id; + } + /** The workspace-relative files some claim cites, as the evidence records spell them. */ + files(): string[] { + return [...this.byFile.keys()]; + } + /** The ranges cited in `rel`, in document order. */ + ranges(rel: string): readonly CitedRange[] { + return this.byFile.get(rel) ?? []; + } + /** The claim `ref` names, when this revision has it. */ + claim(ref: ClaimRef): CitedClaim | undefined { + return this.claims.get(key(ref)); + } +} +const key = (ref: ClaimRef): string => `${ref.kind}\n${ref.id}`; +/** Whether a range is in the place's notebook cell (both undefined: a plain file). */ +const sameCell = (evidence: WorkflowEvidence, cell: number | undefined): boolean => evidence.cell === cell; +/** How far `evidence` lies from lines `start`..`end` of `cell`; a cell range never matches a plain file (MAX_SAFE_INTEGER cells). */ +function distance(evidence: WorkflowEvidence, cell: number | undefined, start: number, end: number): { cells: number; lines: number; above: boolean } { + const cells = evidence.cell === undefined || cell === undefined ? (evidence.cell === cell ? 0 : Number.MAX_SAFE_INTEGER) : Math.abs(evidence.cell - cell); + if (cells) + return { cells, lines: 0, above: evidence.cell !== undefined && cell !== undefined && evidence.cell < cell }; + if (evidence.endLine < start) + return { cells: 0, lines: start - evidence.endLine, above: true }; + if (evidence.line > end) + return { cells: 0, lines: evidence.line - end, above: false }; + return { cells: 0, lines: 0, above: false }; +} +/** Each claim once, from its nearest range. */ +function perClaim(ranges: readonly CitedRange[], cell: number | undefined, start: number, end: number): ClaimMatch[] { + const best = new Map(); + for (const range of ranges) { + const far = distance(range.evidence, cell, start, end); + if (far.cells === Number.MAX_SAFE_INTEGER) + continue; + for (const { claim, counter } of range.claims) { + const known = best.get(key(claim)); + if (!known || far.cells < known.cells || (far.cells === known.cells && far.lines < known.lines) || (far.cells === known.cells && far.lines === known.lines && known.counter && !counter)) + best.set(key(claim), { claim, evidence: range.evidence, counter, ...far }); + } + } + return [...best.values()]; +} +/** + * The claims whose cited lines include a line of `start`..`end` (one-based, inclusive) in the same + * notebook cell, or none: steps, then connections, then findings, each in document order. + */ +export function claimsAt(ranges: readonly CitedRange[], cell: number | undefined, start: number, end: number): ClaimMatch[] { + return perClaim(ranges.filter(range => sameCell(range.evidence, cell) && range.evidence.line <= end && range.evidence.endLine >= start), cell, start, end) + .sort((a, b) => byKind(a.claim, b.claim)); +} +/** + * The claims cited nearest to `start`..`end` (none of them includes it), at most `limit`: the same + * cell first, then the nearest cells, then the fewest lines away; ties in kind and document order. + */ +export function nearestClaims(ranges: readonly CitedRange[], cell: number | undefined, start: number, end: number, limit: number): ClaimMatch[] { + return perClaim(ranges, cell, start, end) + .sort((a, b) => a.cells - b.cells || a.lines - b.lines || byKind(a.claim, b.claim)) + .slice(0, Math.max(0, limit)); +} +/** "line 12" or "lines 12–14". */ +export function linesText(start: number, end: number): string { + return start === end ? `line ${start}` : `lines ${start}–${end}`; +} +/** + * Model text for a QuickPick row: bounded and escaped (displayText), and with VS Code's `$(icon)` + * syntax escaped, so a title cannot draw an icon. + */ +export function pickText(value: string, max = 120): string { + return displayText(value, max).replace(/\$\(/g, '\\$('); +} +const KIND_WORD: Record = { node: 'Step', edge: 'Connection', issue: 'Finding' }; +/** + * One claim as a QuickPick row: the kind and title (a finding's F label first), then its phase (a + * finding's severity first; a basis other than observed), then where it is cited and, for a + * nearest claim, how far from the cursor. + */ +export function claimPickText(match: ClaimMatch, nearest = false): { label: string; description: string; detail: string } { + const { claim, evidence } = match; + const title = claim.kind === 'edge' && claim.ends ? `${claim.ends[0]} → ${claim.ends[1]}` : claim.title; + const label = `${claim.label ? claim.label + ' ' : ''}${KIND_WORD[claim.kind]}: ${pickText(title)}`; + const description = [ + ...(claim.kind === 'edge' && claim.title ? [`“${pickText(claim.title, 80)}”`] : []), + ...(claim.severity ? [claim.severity] : []), + ...(claim.phases.length ? [pickText(claim.phases.join(', '), 80)] : []), + ...(claim.basis !== 'observed' ? [claim.basis] : []) + ].join(' · '); + const where = `${evidence.cell !== undefined ? `cell ${evidence.cell}, ` : ''}${linesText(evidence.line, evidence.endLine)}`; + let detail = `${match.counter ? 'Counter-evidence' : 'Cited'} at ${where}`; + if (nearest && match.cells) + detail += `, ${match.cells} ${match.cells === 1 ? 'cell' : 'cells'} ${match.above ? 'above' : 'below'}`; + else if (nearest && match.lines) + detail += `, ${match.lines} ${match.lines === 1 ? 'line' : 'lines'} ${match.above ? 'above' : 'below'}`; + return { label, description, detail }; +} diff --git a/vscode-extension/src/revealCommand.ts b/vscode-extension/src/revealCommand.ts new file mode 100644 index 00000000..4da0c75b --- /dev/null +++ b/vscode-extension/src/revealCommand.ts @@ -0,0 +1,302 @@ +/** + * Viewer M3 (roadmap step 14): MLView: Reveal in Diagram, the way from the code back to the diagram. + * + * The editor context menu (and the Command Palette) offers the command on a file an open diagram + * cites and that is unchanged since that revision was published: the `when` clause is + * `resourcePath in mlview.citedFiles` (with a file or notebook-cell scheme), and the context key + * `mlview.citedFiles` lists those files' paths. The list depends only on the open panels and their + * validation, so it is set before the reader right-clicks: VS Code's renderer evaluates the clause + * against the editor under the pointer at once. (M3 review, F1: a key worked out from the active + * editor was still false when a right-click in the editor beside came straight from the diagram, + * because the menu was read before the host's `setContext` came back; measured live.) What the + * list cannot know (unsaved edits that moved the quoted lines, a notebook opened as text) the + * command checks when it runs and explains in one line. A file opened through another path (a + * symlinked folder) is matched by the command, but the menu matches the path only. + * + * Run on a cited line or selection, it finds the claims whose cited lines include it; one is shown + * at once, a QuickPick lists several, and a place no claim cites offers the nearest claims in the + * file. The chosen claim is shown in its diagram, and the keyboard focus moves to that panel: the + * one place the viewer moves the focus by itself, because the reader asked for the diagram. + * + * Nothing here reads or interprets code: the claims and their ranges come from the panel's + * validated document (`CitationIndex`), and a range counts only while the editor's text still has + * the quote at the cited lines. No default keybinding, no hover, no CodeLens, no diagnostics. + */ +import * as path from 'node:path'; +import * as vscode from 'vscode'; +import { claimPickText, claimsAt, linesText, nearestClaims, pickText, type ClaimMatch, type ClaimRef, type CitedRange } from './citationIndex'; +import { displayText } from './displayText'; +import type { Logger } from './log'; +import { splitLines, type StaleReason, type WorkflowEvidence } from './workflowDocument'; + +export const REVEAL_COMMAND = 'mlview.revealInDiagram'; +/** The context key: the paths of the cited, unchanged files of every open panel (see `citedPathForms`). */ +export const CITED_FILES_CONTEXT = 'mlview.citedFiles'; +/** At most this many nearest claims are offered for a line no claim cites. */ +export const NEAREST_LIMIT = 30; + +/** Where the reader is: the file on disk (the notebook, for a cell), the cell, and the lines. */ +export interface EditorPlace { + fsPath: string; + /** The notebook cell's index, counted from 0 as the artifact records it; undefined in a plain file. */ + cell?: number; + /** The editor shows a notebook's cell (undefined for a text editor, even on an .ipynb file). */ + notebook: boolean; + /** The selection's first and last lines, one-based; the cursor's line when nothing is selected. */ + start: number; + end: number; + /** The text has unsaved changes. */ + dirty: boolean; + /** The editor's current text of the document or cell `evidence` addresses, as lines; undefined when it has none. */ + linesFor(evidence: WorkflowEvidence): readonly string[] | undefined; +} +/** What a panel says about the reader's file. */ +export type CitationState = + | { kind: 'none' } + | { kind: 'stale'; rel: string; reason: StaleReason; revision: string } + /** A cited file, unchanged, in the folder the panel's root hint names, not at the workspace root. */ + | { kind: 'elsewhere'; rel: string; folder: string } + /** The diagram cites the notebook by cell, and the file is open as text (or the other way round). */ + | { kind: 'form'; rel: string; cells: boolean } + /** No cited range still has its quote at the cited lines of the editor's text. */ + | { kind: 'moved'; rel: string; dirty: boolean } + | { kind: 'cited'; rel: string; revision: string; ranges: readonly CitedRange[] }; +/** The panel side of the command (authoredPanel.ts). */ +export interface RevealPanel { + /** The displayed revision's title, for the diagram picker. */ + documentTitle(): string; + /** The displayed revision's cited files that are unchanged since publishing, as absolute paths on disk. */ + citedFiles(): readonly string[]; + /** The artifact, relative to its workspace folder. */ + artifactPath(): string; + citationState(place: EditorPlace): Promise; + /** Validate now when a change is waiting to be checked, so the answer is about the files as they are. */ + settle(): Promise; + /** The file no longer has the quoted lines though nothing reported a change: validate again. */ + recheck(): void; + /** Show `ref` of revision `revision` and move the focus to the panel; false when the revision changed. */ + revealClaim(ref: ClaimRef, revision: string): boolean; +} +/** + * The spellings VS Code may give `resourcePath` for `fsPath`: the path itself (a text editor, a + * notebook editor) and its URI path (`/c:/x/nb.ipynb` on Windows, the path of a notebook cell's + * `vscode-notebook-cell` URI), with either case of a drive letter. On macOS and Linux they are one. + */ +export function citedPathForms(fsPath: string): string[] { + const forms = new Set([fsPath, vscode.Uri.file(fsPath).path]); + for (const form of [...forms]) { + const drive = /^(\/?)([A-Za-z]):/.exec(form); + if (drive) { + forms.add(drive[1] + drive[2]!.toLowerCase() + form.slice(drive[0].length - 1)); + forms.add(drive[1] + drive[2]!.toUpperCase() + form.slice(drive[0].length - 1)); + } + } + return [...forms]; +} + +/** The selection's lines, one-based. A selection that ends at the start of a line leaves that line out. */ +export function selectionLines(selection: { start: vscode.Position; end: vscode.Position }): { start: number; end: number } { + // A Selection is a Range: `start` is never after `end`, whichever way it was made. + const first = selection.start.line; + let last = Math.max(first, selection.end.line); + if (last > first && selection.end.character === 0) + last -= 1; + return { start: first + 1, end: last + 1 }; +} +/** The reader's place in `editor`, or undefined when it shows no file on disk (an untitled or remote file). */ +export function editorPlace(editor: vscode.TextEditor): EditorPlace | undefined { + const document = editor.document; + const { start, end } = selectionLines(editor.selection); + const memo = new Map(); + const lines = (doc: vscode.TextDocument): readonly string[] => { + let found = memo.get(doc); + if (!found) { + found = splitLines(doc.getText()); + memo.set(doc, found); + } + return found; + }; + if (document.uri.scheme === 'file') + return { fsPath: document.uri.fsPath, notebook: false, start, end, dirty: document.isDirty, linesFor: evidence => (evidence.cell === undefined ? lines(document) : undefined) }; + if (document.uri.scheme !== 'vscode-notebook-cell') + return undefined; + const key = document.uri.toString(); + for (const notebook of vscode.workspace.notebookDocuments) { + if (notebook.uri.scheme !== 'file') + continue; + const cell = notebook.getCells().find(candidate => candidate.document.uri.toString() === key); + if (!cell) + continue; + return { + fsPath: notebook.uri.fsPath, + cell: cell.index, + notebook: true, + start, + end, + dirty: notebook.isDirty, + // cellAt clamps its index in VS Code, so check the count first. + linesFor: evidence => (evidence.cell !== undefined && evidence.cell < notebook.cellCount ? lines(notebook.cellAt(evidence.cell).document) : undefined) + }; + } + return undefined; +} +const STALE_PHRASE: Record = { changed: 'changed', missing: 'went missing', unreadable: 'could no longer be read', 'too-large': 'grew too large to check' }; +/** "line 4 of train.py", "cell 3, lines 2–5 of notes.ipynb". */ +function placeText(place: EditorPlace): string { + return `${place.cell !== undefined ? `cell ${place.cell}, ` : ''}${linesText(place.start, place.end)} of ${displayText(path.basename(place.fsPath), 200)}`; +} +type ClaimItem = vscode.QuickPickItem & { ref: ClaimRef }; +type PanelItem = vscode.QuickPickItem & { index: number }; +export class RevealInDiagram implements vscode.Disposable { + private command: vscode.Disposable | undefined; + /** The list last sent with `setContext`, joined; undefined before the first. */ + private keyValue: string | undefined; + private disposed = false; + constructor(private readonly panels: () => readonly RevealPanel[], private readonly log: Logger) { } + /** Register the command (always: VS Code needs a contributed command to exist) and start with an empty list. */ + register(): vscode.Disposable { + this.command = vscode.commands.registerCommand(REVEAL_COMMAND, () => this.run()); + // A restarted extension host may find the list the previous one left. + this.setKey([]); + return this.command; + } + /** A panel opened or closed: the list follows; the last one empties it. */ + panelsChanged(): void { + this.update(); + } + /** A panel validated again (a new revision, a changed or restored file, a folder change): the list follows. */ + citationsChanged(): void { + this.update(); + } + /** The paths in the context key now (tests). */ + get citedPaths(): readonly string[] { + return this.keyValue ? this.keyValue.split('\u0000') : []; + } + /** + * Set the key to the cited, unchanged files of every open panel, in every spelling VS Code may + * give `resourcePath` (`citedPathForms`). It is sent only when the list changed. No editor + * listener is needed: the list does not depend on the active editor. + */ + update(): void { + if (this.disposed) + return; + const paths = new Set(); + for (const panel of this.panels()) { + let files: readonly string[]; + try { + files = panel.citedFiles(); + } + catch (error) { + this.log.warn(`could not list the cited files: ${error instanceof Error ? error.message : String(error)}`); + continue; + } + for (const file of files) + for (const form of citedPathForms(file)) + paths.add(form); + } + this.setKey([...paths].sort()); + } + private setKey(paths: string[]): void { + const value = paths.join('\u0000'); + if (this.keyValue === value) + return; + this.keyValue = value; + void Promise.resolve(vscode.commands.executeCommand('setContext', CITED_FILES_CONTEXT, paths)).catch((error: unknown) => this.log.warn(`could not set ${CITED_FILES_CONTEXT}: ${error instanceof Error ? error.message : String(error)}`)); + } + /** MLView: Reveal in Diagram. */ + async run(): Promise { + const panels = this.panels(); + if (!panels.length) { + void vscode.window.showInformationMessage('MLView: no diagram is open. Open a generated diagram, then use Reveal in Diagram on a line it cites.'); + return; + } + const editor = vscode.window.activeTextEditor; + const place = editor ? editorPlace(editor) : undefined; + if (!place) { + void vscode.window.showInformationMessage('MLView: Reveal in Diagram works in the editor of a cited source file or notebook cell. Put the cursor on a cited line, then try again.'); + return; + } + await Promise.all(panels.map(panel => panel.settle())); + const states = await Promise.all(panels.map(async panel => ({ panel, state: await panel.citationState(place) }))); + const cited = states.filter((entry): entry is { panel: RevealPanel; state: Extract } => entry.state.kind === 'cited'); + const where = placeText(place); + if (!cited.length) { + for (const entry of states) + if (entry.state.kind === 'moved' && !entry.state.dirty) + entry.panel.recheck(); + void vscode.window.showInformationMessage(this.notCited(states.map(entry => entry.state), place)); + return; + } + let chosen = cited[0]!; + if (cited.length > 1) { + const items: PanelItem[] = cited.map((entry, index) => { + const count = claimsAt(entry.state.ranges, place.cell, place.start, place.end).length; + return { + label: pickText(entry.panel.documentTitle()), + description: pickText(entry.panel.artifactPath(), 200), + detail: count ? `${count} ${count === 1 ? 'claim cites' : 'claims cite'} ${linesText(place.start, place.end)}` : `No claim cites ${linesText(place.start, place.end)}; the nearest are offered`, + index + }; + }); + const picked = await vscode.window.showQuickPick(items, { title: 'Reveal in Diagram', placeHolder: `${cited.length} open diagrams cite ${displayText(path.basename(place.fsPath), 200)}: choose the diagram`, matchOnDescription: true }); + if (!picked) + return; + chosen = cited[picked.index]!; + } + const ref = await this.chooseClaim(chosen.state.ranges, place, where); + if (!ref) + return; + if (!chosen.panel.revealClaim(ref, chosen.state.revision)) + void vscode.window.showInformationMessage('MLView: the diagram changed to another revision while you were choosing. Run Reveal in Diagram again.'); + } + /** One claim at the place, the reader's choice of several, or of the nearest ones; undefined when cancelled. */ + private async chooseClaim(ranges: readonly CitedRange[], place: EditorPlace, where: string): Promise { + const at = claimsAt(ranges, place.cell, place.start, place.end); + if (at.length === 1) + return refOf(at[0]!); + const nearest = !at.length; + const matches = nearest ? nearestClaims(ranges, place.cell, place.start, place.end, NEAREST_LIMIT) : at; + if (!matches.length) { + void vscode.window.showInformationMessage(`MLView: no claim in this diagram cites ${where}.`); + return undefined; + } + const items: ClaimItem[] = matches.map(match => ({ ...claimPickText(match, nearest), ref: refOf(match) })); + const picked = await vscode.window.showQuickPick(items, { + title: nearest ? `Reveal in Diagram: no claim cites ${where}` : 'Reveal in Diagram', + placeHolder: nearest ? 'The nearest claims in this file: choose one to show it in the diagram' : `${at.length} claims cite ${where}: choose one to show it in the diagram`, + matchOnDescription: true, + matchOnDetail: true + }); + return picked?.ref; + } + /** Why no open diagram can answer for this file, in one sentence. */ + private notCited(states: readonly CitationState[], place: EditorPlace): string { + const file = displayText(path.basename(place.fsPath), 200); + const moved = states.find((state): state is Extract => state.kind === 'moved'); + if (moved?.dirty) + return `MLView: the unsaved text of ${file} no longer has the quoted lines where the diagram cites them, so the cursor cannot be matched to a claim. Undo those edits to use Reveal in Diagram here.`; + if (moved) + return `MLView: ${file} no longer has the quoted lines where the diagram cites them; MLView is checking it again.`; + const elsewhere = states.find((state): state is Extract => state.kind === 'elsewhere'); + if (elsewhere) + return `MLView: the diagram cites ${displayText(elsewhere.rel, 200)} from the workspace root, where it is missing; this copy in ${displayText(elsewhere.folder, 200)} is unchanged. Add that folder to the workspace (the diagram's notice offers it), then try again.`; + const stale = states.find((state): state is Extract => state.kind === 'stale'); + if (stale) + return `MLView: ${file} ${STALE_PHRASE[stale.reason]} after revision ${displayText(stale.revision, 200)} was published, so its lines no longer match the diagram. Ask the assistant for a fresh revision.`; + const form = states.find((state): state is Extract => state.kind === 'form'); + if (form) + return form.cells + ? `MLView: the diagram cites ${file} by notebook cell. Open it in the notebook editor and use Reveal in Diagram in a cell.` + : `MLView: the diagram cites ${file} as a text file. Open it in the text editor and use Reveal in Diagram there.`; + return `MLView: no open diagram cites ${file}.`; + } + dispose(): void { + if (this.disposed) + return; + this.setKey([]); + this.disposed = true; + this.command?.dispose(); + this.command = undefined; + } +} +const refOf = (match: ClaimMatch): ClaimRef => ({ kind: match.claim.kind, id: match.claim.id }); diff --git a/vscode-extension/src/testEntry.ts b/vscode-extension/src/testEntry.ts index d9898443..a1dd1fa2 100644 --- a/vscode-extension/src/testEntry.ts +++ b/vscode-extension/src/testEntry.ts @@ -1,4 +1,4 @@ -export { createNonce, themeKindOf, toEditorLine, staleBreakdown, staleBannerText, staleToastText, staleJumpText, rootHintCandidates, mostlyMissing, findRootHint, rootHintText, rootHintJumpText, hintFolderName } from './authoredSupport'; +export { createNonce, themeKindOf, toEditorLine, staleBreakdown, staleBannerText, staleToastText, staleJumpText, blockedOpenText, rootHintCandidates, mostlyMissing, findRootHint, rootHintText, rootHintJumpText, hintFolderName } from './authoredSupport'; export { decodeExportPayload, defaultExportName, @@ -33,5 +33,8 @@ export { OPEN_PANELS_TTL_MS, MAX_OTHER_SESSIONS, RECOVERY_SETTLE_MS, - OPEN_AUTHORED_COMMAND + OPEN_AUTHORED_COMMAND, + REVEAL_REPLAY_MS } from './authoredPanel'; +export { CitationIndex, claimsAt, nearestClaims, claimPickText, pickText, linesText } from './citationIndex'; +export { RevealInDiagram, editorPlace, selectionLines, citedPathForms, REVEAL_COMMAND, CITED_FILES_CONTEXT, NEAREST_LIMIT } from './revealCommand'; diff --git a/vscode-extension/test/activation.test.js b/vscode-extension/test/activation.test.js index dde05895..155400f5 100644 --- a/vscode-extension/test/activation.test.js +++ b/vscode-extension/test/activation.test.js @@ -17,12 +17,17 @@ function context() { }; } -test('activation registers only the authored workflow surface', () => { +// Viewer M3 (roadmap step 14), changed deliberately: activation registers a second command, MLView: +// Reveal in Diagram, the owner-approved widening of the manifest (see packaging.test.js). Its +// context key, the list of cited files, starts empty, and nothing else is registered. +test('activation registers only the authored workflow surface and Reveal in Diagram', () => { vscode.__recorded.commands.clear(); vscode.__recorded.serializers.clear(); + vscode.__recorded.contexts.clear(); const ctx = context(); extension.activate(ctx); - assert.deepEqual([...vscode.__recorded.commands.keys()], ['mlview.openGeneratedDiagram']); + assert.deepEqual([...vscode.__recorded.commands.keys()], ['mlview.openGeneratedDiagram', 'mlview.revealInDiagram']); + assert.deepEqual(vscode.__recorded.contexts.get('mlview.citedFiles'), [], 'the cited-files key starts empty'); assert.ok(vscode.__recorded.serializers.has('mlview.authoredDiagram')); assert.ok(ctx.subscriptions.length >= 2); extension.deactivate(); diff --git a/vscode-extension/test/bootstrap.test.js b/vscode-extension/test/bootstrap.test.js index 7830b58d..85bee5fd 100644 --- a/vscode-extension/test/bootstrap.test.js +++ b/vscode-extension/test/bootstrap.test.js @@ -34,7 +34,9 @@ function stubEnvironment() { } }; elements.set('mlview-root', root); + const documentListeners = []; const document = { + addEventListener(type, listener, options) { documentListeners.push({ type, listener, options }); }, getElementById: (id) => elements.get(id) || null, createElement: (tag) => { const element = { @@ -61,7 +63,9 @@ function stubEnvironment() { loadState() { return state; } }; const mounts = []; + const windowListeners = []; const window = { + addEventListener(type, listener, options) { windowListeners.push({ type, listener, options }); }, MLView: { bridges: { vscode: () => bridge }, mountWorkflow(target, doc, withBridge) { @@ -70,7 +74,7 @@ function stubEnvironment() { } } }; - return { window, document, root, bridge, mounts, deliver: (message) => listener(message) }; + return { window, document, root, bridge, mounts, windowListeners, documentListeners, deliver: (message) => listener(message) }; } test('bootstrap posts one ready, stashes init before mount, and mounts on the first workflow only', async () => { @@ -141,3 +145,29 @@ test('after the mount the bootstrap draws no banner and removes a leftover one', assert.equal(env.root.children.length, 0, 'the bootstrap ignores the stale frame'); assert.equal(env.bridge.posted.length, 1, 'only the bootstrap ready is ever posted'); }); + +// Viewer M3: VS Code's webview host forwards every keydown to the workbench from a listener on the +// page's window, even a key the page already handled. Once the viewer has mounted, the bootstrap +// stops an Escape the viewer acted on at the document, before it reaches that window; any other +// key, and an Escape nobody handled, still goes to VS Code. +test('after the mount the bootstrap stops an Escape the viewer handled before VS Code forwards it', async () => { + const source = await bootstrapSource(); + const env = stubEnvironment(); + vm.runInNewContext(source, { window: env.window, document: env.document, Object }); + const keydown = () => env.documentListeners.filter((entry) => entry.type === 'keydown'); + assert.equal(keydown().length, 0, 'nothing is intercepted before the viewer mounts'); + env.deliver({ v: 1, type: 'workflow', document: { revision: { id: 'r1' } } }); + env.deliver({ v: 1, type: 'workflow', document: { revision: { id: 'r2' } } }); + assert.equal(keydown().length, 1, 'one document keydown listener, added once at the mount'); + assert.notEqual(keydown()[0].options, true, 'a bubble-phase listener, so it runs after the viewer handled the key'); + assert.equal(env.windowListeners.length, 0, 'nothing on the window, where VS Code forwards from'); + const press = (key, defaultPrevented) => { + const event = { key, defaultPrevented, stopped: false, stopPropagation() { event.stopped = true; } }; + keydown()[0].listener(event); + return event.stopped; + }; + assert.equal(press('Escape', true), true, 'a handled Escape stops at the document'); + assert.equal(press('Escape', false), false, 'an Escape the viewer did not use still reaches VS Code'); + assert.equal(press('Enter', true), false, 'other keys are left alone'); + assert.equal(press('p', true), false); +}); diff --git a/vscode-extension/test/mock-vscode.js b/vscode-extension/test/mock-vscode.js index 72213560..df774a85 100644 --- a/vscode-extension/test/mock-vscode.js +++ b/vscode-extension/test/mock-vscode.js @@ -317,7 +317,13 @@ const recorded = { activeEditorListeners: [], activeNotebookListeners: [], /** H10: every languages.registerCodeLensProvider registration. */ - codeLensProviders: [] + codeLensProviders: [], + /** + * Viewer M3 (step 14): `setContext` is a built-in command, kept out of `executedCommands`: the + * context keys as they are now, and every setContext call, in order. + */ + contexts: new Map(), + contextCalls: [] }; const configValues = new Map(); @@ -365,6 +371,8 @@ let visibleTextEditors = []; let visibleNotebookEditors = []; /** Whether showNotebookDocument makes the selected cells' editors visible (as VS Code does once it draws them). */ let notebookCellEditors = true; +/** Opt-in: showTextDocument's editor becomes the visible one in its column, as a preview tab replaces the last. */ +let shownEditorsVisible = false; /** * Viewer M2 live fix: the column an editor shown with `viewColumn` lands in. With tab groups set @@ -397,7 +405,8 @@ function makeTextEditor(document, viewColumn) { revealRange(range, revealType) { editor.revealed.push({ range, revealType }); }, - selection: undefined + // Like VS Code, an editor always has a selection: the cursor at the start until something moves it. + selection: new Selection(new Position(0, 0), new Position(0, 0)) }; return editor; } @@ -497,6 +506,8 @@ function makeWebviewPanel(viewType, title, showOptions, options, existingTab) { tab: undefined, posted: [], revealed: 0, + /** Viewer M3 (step 14): every reveal(viewColumn, preserveFocus) call, in order. */ + revealCalls: [], disposed: false, webview: { html: '', @@ -512,8 +523,9 @@ function makeWebviewPanel(viewType, title, showOptions, options, existingTab) { /** Play the webview's part. */ fire: (message) => messages.fire(message), postedTypes: () => panel.posted.map((m) => m.type), - reveal() { + reveal(viewColumn, preserveFocus) { panel.revealed += 1; + panel.revealCalls.push({ viewColumn, preserveFocus }); }, onDidDispose: disposal.event, onDidChangeViewState: viewState.event, @@ -727,6 +739,7 @@ const vscode = { showTextDocument: async (document, options) => { const editor = makeTextEditor(document, resolveColumn(options && options.viewColumn, document && document.uri && path.basename(document.uri.fsPath))); recorded.shownDocuments.push({ document, options, editor }); + if (shownEditorsVisible) visibleTextEditors = [...visibleTextEditors.filter((other) => other.viewColumn !== editor.viewColumn), editor]; return editor; }, /** @@ -926,6 +939,11 @@ const vscode = { return { dispose: () => recorded.commands.delete(id) }; }, executeCommand: async (id, ...args) => { + if (id === 'setContext') { + recorded.contexts.set(args[0], args[1]); + recorded.contextCalls.push({ key: args[0], value: args[1] }); + return undefined; + } recorded.executedCommands.push({ id, args }); return undefined; } @@ -970,7 +988,8 @@ const vscode = { __setVisibleTextEditors(specs) { visibleTextEditors = (specs || []).map((spec) => ({ document: makeDocument(Uri.file(spec.path)), - viewColumn: spec.viewColumn + viewColumn: spec.viewColumn, + selection: new Selection(new Position(0, 0), new Position(0, 0)) })); vscode.window.activeTextEditor = visibleTextEditors.find(editor => editor.viewColumn === specs?.find(spec => spec.active)?.viewColumn); return visibleTextEditors; @@ -994,7 +1013,19 @@ const vscode = { for (const listener of [...recorded.activeNotebookListeners]) listener(editor); return editor; } - const editor = spec ? { document: makeDocument(Uri.file(spec.path)), viewColumn: spec.viewColumn } : undefined; + // Viewer M3 (step 14): `{ cellOf: notebookPath, cell }` is the editor of one cell of an open + // notebook; `selection: [startLine, startChar, endLine, endChar]` (zero-based, default the + // start of the document) is its selection. + let document; + if (spec && spec.cellOf !== undefined) { + const notebook = notebookDocuments.find((candidate) => candidate.uri.fsPath === spec.cellOf); + if (!notebook) throw new Error(`notebook is not open: ${spec.cellOf}`); + document = notebook.cellAt(spec.cell).document; + } else if (spec) { + document = makeDocument(Uri.file(spec.path)); + } + const [l1, c1, l2, c2] = (spec && spec.selection) || [0, 0, 0, 0]; + const editor = spec ? { document, viewColumn: spec.viewColumn, selection: new Selection(new Position(l1, c1), new Position(l2 === undefined ? l1 : l2, c2 === undefined ? c1 : c2)) } : undefined; vscode.window.activeTextEditor = editor; for (const listener of [...recorded.activeEditorListeners]) listener(editor); return editor; @@ -1003,6 +1034,10 @@ const vscode = { __setNotebookCellEditors(enabled) { notebookCellEditors = !!enabled; }, + /** Whether showTextDocument's editor joins visibleTextEditors, replacing the one in its column (default false). */ + __setShownEditorsVisible(enabled) { + shownEditorsVisible = !!enabled; + }, /** Give `openTextDocument` real text for one absolute path. */ __setDocument(fsPath, text) { documents.set(docKey(fsPath), text); @@ -1164,6 +1199,8 @@ const vscode = { recorded.closedTabs.length = 0; recorded.activeEditorListeners.length = 0; recorded.activeNotebookListeners.length = 0; + recorded.contexts.clear(); + recorded.contextCalls.length = 0; vscode.window.activeNotebookEditor = undefined; tabGroups = []; tabEvents.dispose(); @@ -1189,6 +1226,7 @@ const vscode = { notebookDocuments = []; visibleTextEditors = []; visibleNotebookEditors = []; + shownEditorsVisible = false; vscode.window.activeTextEditor = undefined; for (const key of [ 'saveListeners', diff --git a/vscode-extension/test/packaging.test.js b/vscode-extension/test/packaging.test.js index d4da2465..9ae387e7 100644 --- a/vscode-extension/test/packaging.test.js +++ b/vscode-extension/test/packaging.test.js @@ -6,8 +6,24 @@ const path = require('node:path'); const root = path.join(__dirname, '..'); const manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); -test('manifest exposes only the authored artifact command and activation path', () => { - assert.deepEqual(manifest.contributes.commands.map(x => x.command), ['mlview.openGeneratedDiagram']); +// Viewer M3 (roadmap step 14), changed deliberately: the manifest's one widening, approved by the +// owner, is a SECOND command, MLView: Reveal in Diagram (the way from the code back to the diagram). +// It is offered in the editor context menu and the Command Palette only on a file an open diagram +// cites, unchanged since publishing (`resourcePath in mlview.citedFiles`, a list the extension keeps +// of the open panels' cited files, so the renderer can decide for the editor under the pointer at +// once: M3 review, F1), has no default keybinding, and adds no activation event, setting, hover, +// CodeLens or diagnostics. +test('manifest exposes only the authored artifact command, Reveal in Diagram, and the activation path', () => { + assert.deepEqual(manifest.contributes.commands.map(x => x.command), ['mlview.openGeneratedDiagram', 'mlview.revealInDiagram']); + const reveal = manifest.contributes.commands.find(x => x.command === 'mlview.revealInDiagram'); + assert.equal(reveal.title, 'Reveal in Diagram'); + assert.equal(reveal.category, 'MLView'); + const gated = (list) => (list || []).filter(x => x.command === 'mlview.revealInDiagram'); + const when = 'resourceScheme =~ /^(file|vscode-notebook-cell)$/ && resourcePath in mlview.citedFiles'; + assert.deepEqual(gated(manifest.contributes.menus['editor/context']).map(x => x.when), [when]); + assert.deepEqual(gated(manifest.contributes.menus.commandPalette).map(x => x.when), [when]); + assert.deepEqual(gated(manifest.contributes.menus['editor/title']), []); + assert.equal(manifest.contributes.keybindings, undefined, 'no default keybinding'); assert.deepEqual(manifest.activationEvents, [ 'onCommand:mlview.openGeneratedDiagram', 'onWebviewPanel:mlview.authoredDiagram', diff --git a/vscode-extension/test/reveal-host.test.js b/vscode-extension/test/reveal-host.test.js new file mode 100644 index 00000000..7999444b --- /dev/null +++ b/vscode-extension/test/reveal-host.test.js @@ -0,0 +1,540 @@ +'use strict'; +/** + * Viewer M3 (roadmap step 14): MLView: Reveal in Diagram, the host side. The citation index built + * from the validated document (claims by cited file and range), the `mlview.citedFiles` context key + * (the open panels' cited files that are unchanged since publishing) and the manifest's `when` + * clause evaluated against it for the editor under the pointer, the index following each + * revalidation, the claim and diagram QuickPicks, the `reveal {kind, id}` frame and the one explicit + * focus move, notebook cell editors, and everything going with the last panel. Mock `vscode` only: + * none of this is a live check (the clause is evaluated here by a small stand-in for VS Code's). + */ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const h = require('./panel-helpers'); +const { api, vscode } = h; + +const fixtures = []; +test.afterEach(() => h.cleanup(fixtures)); +async function open(options) { + const fixture = await h.openPanel(options); + fixtures.push(fixture); + return fixture; +} +const KEY = 'mlview.citedFiles'; +const key = () => vscode.__recorded.contexts.get(KEY); +const MANIFEST = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')); +/** + * A stand-in for VS Code's when-clause evaluation, for the clause shapes the manifest uses: terms + * joined by `&&`, each `key =~ /re/`, `key in listKey` or a bare key (truthy). VS Code's renderer + * evaluates the clause against the context of the editor under the pointer, with the context keys + * as they are at that moment: no extension code runs in between. + */ +function evaluateWhen(clause, context) { + return clause.split('&&').map((term) => term.trim()).every((term) => { + let m = /^([\w.]+) =~ \/(.*)\/$/.exec(term); + if (m) return new RegExp(m[2]).test(String(context[m[1]] ?? '')); + m = /^([\w.]+) in ([\w.]+)$/.exec(term); + if (m) { + const list = context[m[2]]; + return Array.isArray(list) ? list.includes(context[m[1]]) : !!list && typeof list === 'object' && Object.prototype.hasOwnProperty.call(list, context[m[1]]); + } + if (/^[\w.]+$/.test(term)) return !!context[term]; + throw new Error('clause term not understood: ' + term); + }); +} +/** + * Whether the editor context menu offers Reveal in Diagram on a right-click in an editor of `fsPath` + * (a text file, or with `cell` a notebook cell), with the keys as they are now and whatever editor + * VS Code calls active (the diagram may have the focus: no active text editor at all). + */ +function offered(fsPath, { cell = false } = {}) { + const item = MANIFEST.contributes.menus['editor/context'].find((x) => x.command === 'mlview.revealInDiagram'); + // VS Code's `resourcePath`: the fsPath of a file URI, the path of any other URI (a notebook cell's + // `vscode-notebook-cell` URI has the notebook's path). + const context = cell + ? { resourceScheme: 'vscode-notebook-cell', resourcePath: vscode.Uri.file(fsPath).path } + : { resourceScheme: 'file', resourcePath: vscode.Uri.file(fsPath).fsPath }; + for (const [name, value] of vscode.__recorded.contexts) context[name] = value; + return evaluateWhen(item.when, context); +} +const reveals = (panel) => panel.posted.filter((m) => m.type === 'reveal'); +const infos = () => vscode.__recorded.messages.filter((m) => m[0] === 'info').map((m) => m[1]); +const picks = () => vscode.__recorded.quickPicks; +const plain = (value) => JSON.parse(JSON.stringify(value)); +/** Run MLView: Reveal in Diagram as the context menu does. */ +const runReveal = () => vscode.__recorded.commands.get('mlview.revealInDiagram')(); +/** Wait until every pending context-key computation has settled. */ +const settle = async () => { for (let i = 0; i < 5; i++) await h.tick(); }; + +const SOURCE = 'import x\ntrain()\nsave()\n\n\nlog()\n'; +/** + * source.py: lines 2-3 cited by the step Fit, the connection Fit -> Weights and (as counter-evidence) + * the finding F1; line 6 by the step Log and F1. other.py line 1 by Fit. + */ +function citedDoc(revision = { id: 'r1' }) { + const document = h.workflow('source.py', revision); + document.evidence = [ + { id: 'e', file: 'source.py', line: 2, endLine: 3, quote: 'train()\nsave()' }, + { id: 'e2', file: 'source.py', line: 6, endLine: 6, quote: 'log()' }, + { id: 'e3', file: 'other.py', line: 1, endLine: 1, quote: 'other()' } + ]; + document.nodes[0].evidence = ['e', 'e3']; + document.nodes.push({ id: 'log', label: 'Log', phase: 'p', basis: 'observed', evidence: ['e2'] }); + document.edges[0].evidence = ['e']; + document.findings[0].evidence = ['e2']; + document.findings[0].counterEvidence = ['e']; + document.coverage.inspectedFiles = ['source.py', 'other.py']; + const files = { 'source.py': SOURCE, 'other.py': 'other()\n', 'plain.py': 'pass\n' }; + return { document: h.verify(document, files), files }; +} +async function openCited(extra = {}) { + const { document, files } = citedDoc(); + return open({ raw: document, files, ...extra }); +} +/** Put the cursor (zero-based line) or a selection in `rel` of the fixture's root. */ +function cursor(fixture, rel, line, endLine = line, endChar = 0) { + return vscode.__setActiveEditor({ path: path.join(fixture.root, rel), viewColumn: 1, selection: [line, 0, endLine, endChar] }); +} + +/* ── the index ───────────────────────────────────────────────────────── */ + +test('the citation index finds the claims whose cited lines include the cursor or overlap the selection: steps, connections, findings', () => { + const { document } = citedDoc(); + const index = api.CitationIndex.from(document); + assert.deepEqual(index.files().sort(), ['other.py', 'source.py']); + const ranges = index.ranges('source.py'); + const at = (start, end) => api.claimsAt(ranges, undefined, start, end).map((m) => `${m.claim.kind}:${m.claim.id}${m.counter ? ' (counter)' : ''}`); + assert.deepEqual(at(2, 2), ['node:n', 'edge:flow', 'issue:risk (counter)']); + assert.deepEqual(at(3, 3), ['node:n', 'edge:flow', 'issue:risk (counter)']); + assert.deepEqual(at(1, 1), []); + assert.deepEqual(at(4, 5), []); + assert.deepEqual(at(6, 6), ['node:log', 'issue:risk']); + // A selection overlapping both ranges: each claim once, a supporting citation before a counter one. + assert.deepEqual(at(3, 6), ['node:n', 'node:log', 'edge:flow', 'issue:risk']); + // A notebook cell's ranges match only in that cell. + const notebook = api.CitationIndex.from({ ...document, evidence: [{ id: 'e', file: 'nb.ipynb', cell: 2, line: 1, endLine: 2, quote: 'q' }, { id: 'e2', file: 'nb.ipynb', cell: 5, line: 1, endLine: 1, quote: 'r' }, { id: 'e3', file: 'other.py', line: 1, endLine: 1, quote: 'other()' }] }); + const cells = notebook.ranges('nb.ipynb'); + assert.deepEqual(api.claimsAt(cells, 2, 1, 1).map((m) => m.claim.id), ['n', 'flow', 'risk']); + assert.deepEqual(api.claimsAt(cells, 5, 1, 1).map((m) => m.claim.id), ['log', 'risk']); + assert.deepEqual(api.claimsAt(cells, 3, 1, 1), []); + assert.deepEqual(api.claimsAt(cells, undefined, 1, 1), [], 'a plain file never matches a cell'); +}); + +test('the nearest claims: the same cell first, then the fewest lines away, each claim once from its nearest range', () => { + const { document } = citedDoc(); + const index = api.CitationIndex.from(document); + const near = api.nearestClaims(index.ranges('source.py'), undefined, 4, 4, 30); + assert.deepEqual(near.map((m) => [m.claim.id, m.lines, m.above]), [['n', 1, true], ['flow', 1, true], ['risk', 1, true], ['log', 2, false]]); + assert.equal(api.nearestClaims(index.ranges('source.py'), undefined, 4, 4, 2).length, 2, 'at most the limit'); + const notebook = api.CitationIndex.from({ ...document, evidence: [{ id: 'e', file: 'nb.ipynb', cell: 1, line: 3, endLine: 4, quote: 'q' }, { id: 'e2', file: 'nb.ipynb', cell: 6, line: 1, endLine: 1, quote: 'r' }, { id: 'e3', file: 'nb.ipynb', cell: 4, line: 9, endLine: 9, quote: 's' }] }); + const cells = notebook.ranges('nb.ipynb'); + assert.deepEqual(api.nearestClaims(cells, 4, 1, 1, 30).map((m) => [m.claim.id, m.cells, m.lines]), [['n', 0, 8], ['log', 2, 0], ['risk', 2, 0], ['flow', 3, 0]]); +}); + +test('claims carry their kind, title, F label, phases and basis; QuickPick text is bounded and cannot draw icons', () => { + const { document } = citedDoc(); + document.phases.push({ id: 'q', label: 'Evaluate' }); + document.nodes.push({ id: 'ev', label: 'Score', phase: 'q', basis: 'observed', evidence: ['e3'] }); + document.edges.push({ id: 'to-ev', source: 'ev', target: 'n', label: 'scores', basis: 'observed', evidence: ['e3'] }); + document.findings.push({ id: 'second', title: 'Bad $(alert) title\nwith a newline', message: 'm', severity: 'low', nodeIds: [], edgeIds: ['to-ev'], basis: 'observed', evidence: ['e3'], counterEvidence: [] }); + const index = api.CitationIndex.from(document); + const risk = index.claim({ kind: 'issue', id: 'risk' }); + assert.deepEqual({ label: risk.label, severity: risk.severity, phases: risk.phases, basis: risk.basis }, { label: 'F1', severity: 'medium', phases: ['Train'], basis: 'inferred' }); + const second = index.claim({ kind: 'issue', id: 'second' }); + assert.equal(second.label, 'F2', 'F labels by document order'); + assert.deepEqual(second.phases, ['Evaluate'], 'a finding touches the phase of a cited connection\'s source (PR #14)'); + const flow = index.claim({ kind: 'edge', id: 'flow' }); + assert.deepEqual(flow.ends, ['Fit', 'Weights']); + const at = (rel, line) => api.claimsAt(index.ranges(rel), undefined, line, line); + const [step, connection, finding] = at('source.py', 2).map((m) => api.claimPickText(m)); + assert.deepEqual(step, { label: 'Step: Fit', description: 'Train', detail: 'Cited at lines 2–3' }); + assert.deepEqual(connection, { label: 'Connection: Fit → Weights', description: '“produces” · Train · inferred', detail: 'Cited at lines 2–3' }); + assert.deepEqual(finding, { label: 'F1 Finding: Unverified output', description: 'medium · Train · inferred', detail: 'Counter-evidence at lines 2–3' }); + const bad = at('other.py', 1).find((m) => m.claim.id === 'second'); + assert.equal(api.claimPickText(bad).label, 'F2 Finding: Bad \\$(alert) title\\u000awith a newline'); + const near = api.nearestClaims(index.ranges('source.py'), undefined, 4, 4, 30).map((m) => api.claimPickText(m, true).detail); + assert.deepEqual(near, ['Cited at lines 2–3, 1 line above', 'Cited at lines 2–3, 1 line above', 'Counter-evidence at lines 2–3, 1 line above', 'Cited at line 6, 2 lines below']); + assert.equal(api.pickText('x'.repeat(500)).length, 121); +}); + +test('selection lines: the cursor\'s line; a selection; one that ends at the start of a line leaves that line out', () => { + const sel = (l1, c1, l2, c2) => new vscode.Selection(new vscode.Position(l1, c1), new vscode.Position(l2, c2)); + assert.deepEqual(api.selectionLines(sel(4, 3, 4, 3)), { start: 5, end: 5 }); + assert.deepEqual(api.selectionLines(sel(1, 0, 3, 2)), { start: 2, end: 4 }); + assert.deepEqual(api.selectionLines(sel(1, 0, 3, 0)), { start: 2, end: 3 }); + assert.deepEqual(api.selectionLines(sel(2, 5, 2, 0)), { start: 3, end: 3 }); +}); + +/* ── the context key ─────────────────────────────────────────────────── */ + +test('M3 review F1: the menu item is offered on a cited, unchanged file while a panel is open, even when the diagram has the focus (no active text editor)', async () => { + const fixture = await openCited(); + const file = (rel) => path.join(fixture.root, rel); + // The reader pressed Enter on a card: the editor beside shows source.py, and the focus is still in + // the diagram, so VS Code reports no active text editor. The right-click comes next, at once. + vscode.__setActiveEditor(undefined); + await settle(); + assert.equal(offered(file('source.py')), true, 'source.py is cited: offered on the first right-click'); + assert.equal(offered(file('other.py')), true, 'other.py is cited'); + assert.equal(offered(file('plain.py')), false, 'plain.py is not cited'); + assert.equal(offered(path.join(fixture.root, '..', 'elsewhere.py')), false); + assert.ok(Array.isArray(key()), 'the key is a list'); + // The active editor plays no part: the list is the same whichever editor is active. + const before = JSON.stringify(key()); + cursor(fixture, 'plain.py', 0); + await settle(); + assert.equal(JSON.stringify(key()), before); + assert.equal(offered(file('source.py')), true, 'coming from an uncited editor too'); + // Only the cited files of the displayed revision, absolute, in every spelling VS Code may use. + assert.deepEqual([...new Set(key())].sort(), [...new Set([file('other.py'), file('source.py')].flatMap(api.citedPathForms))].sort()); +}); + +test('cited path forms: the path and its URI path; either case of a Windows drive letter', () => { + assert.deepEqual(api.citedPathForms('/repo/train.py'), ['/repo/train.py']); + const windows = api.citedPathForms('c:\\repo\\nb.ipynb'); + for (const form of ['c:\\repo\\nb.ipynb', 'C:\\repo\\nb.ipynb']) assert.ok(windows.includes(form), form); +}); + +test('the item goes when the cited file goes stale and comes back when it is restored', async () => { + const fixture = await openCited(); + const file = path.join(fixture.root, 'source.py'); + cursor(fixture, 'source.py', 1); + await settle(); + assert.equal(offered(file), true); + fs.writeFileSync(file, SOURCE + '# edited\n'); + await h.diskEvent(fixture.panel, 'change', file); + await settle(); + assert.equal(offered(file), false, 'changed since publishing'); + assert.equal(offered(path.join(fixture.root, 'other.py')), true, 'the other cited file is still offered'); + // The command (from a keybinding the reader made, say) says why, plainly, and reveals nothing. + await runReveal(); + assert.equal(reveals(fixture.panel).length, 0); + assert.match(infos().at(-1), /^MLView: source\.py changed after revision r1 was published, so its lines no longer match the diagram\./); + fs.writeFileSync(file, SOURCE); + await h.diskEvent(fixture.panel, 'change', file); + await settle(); + assert.equal(offered(file), true, 'restored'); +}); + +test('unsaved edits that move the cited lines: the item stays (the list cannot see the buffer) and the command explains in one line', async () => { + const fixture = await openCited(); + const file = path.join(fixture.root, 'source.py'); + cursor(fixture, 'source.py', 1); + await settle(); + const type = async (text, dirty) => { + vscode.__setDocument(file, text); + vscode.__setDirty(file, dirty); + const document = await vscode.workspace.openTextDocument(vscode.Uri.file(file)); + vscode.window.activeTextEditor.document = document; + for (const listener of [...vscode.__recorded.changeListeners]) listener({ document, contentChanges: [] }); + }; + // Two lines added at the top: no quote is at its cited lines any more. + await type('# a\n# b\n' + SOURCE, true); + await settle(); + assert.equal(offered(file), true); + await runReveal(); + assert.match(infos().at(-1), /^MLView: the unsaved text of source\.py no longer has the quoted lines where the diagram cites them/); + assert.equal(reveals(fixture.panel).length, 0); + // Undone: the command reveals again (three claims cite line 2: the reader picks the first). + await type(SOURCE, false); + vscode.__answerQuickPick(0); + await runReveal(); + assert.equal(reveals(fixture.panel).length, 1); +}); + +test('the index and the list follow a new revision', async () => { + const fixture = await openCited(); + const plainFile = path.join(fixture.root, 'plain.py'); + const otherFile = path.join(fixture.root, 'other.py'); + cursor(fixture, 'plain.py', 0); + await settle(); + assert.equal(offered(plainFile), false); + // Revision r2 cites plain.py instead of other.py. + const { document, files } = citedDoc({ id: 'r2', parent: 'r1' }); + document.evidence[2] = { id: 'e3', file: 'plain.py', line: 1, endLine: 1, quote: 'pass' }; + document.coverage.inspectedFiles = ['source.py', 'plain.py']; + h.writeJson(fixture.artifact, h.verify(document, files)); + await h.diskEvent(fixture.panel, 'change', fixture.artifact); + assert.equal(h.shownRevision(fixture.panel), 'r2'); + await settle(); + assert.equal(offered(plainFile), true, 'plain.py is cited by r2'); + await runReveal(); + assert.deepEqual(plain(reveals(fixture.panel).at(-1)), { v: 1, type: 'reveal', kind: 'node', id: 'n' }); + assert.equal(offered(otherFile), false, 'other.py is no longer cited'); +}); + +/* ── the command ─────────────────────────────────────────────────────── */ + +test('one claim at the cursor is revealed at once: the reveal frame, and the focus moves to the panel', async () => { + const fixture = await openCited(); + cursor(fixture, 'other.py', 0); + await runReveal(); + assert.equal(picks().length, 0, 'no QuickPick for one claim'); + assert.deepEqual(plain(reveals(fixture.panel)), [{ v: 1, type: 'reveal', kind: 'node', id: 'n' }]); + assert.deepEqual(fixture.panel.revealCalls.at(-1), { viewColumn: undefined, preserveFocus: false }, 'the one explicit focus move, in the panel\'s own group'); + assert.equal(vscode.__recorded.shownDocuments.length, 0, 'nothing is opened'); +}); + +test('several claims at the cursor: a QuickPick lists them by kind, title, F label and phase; the chosen one is revealed', async () => { + const fixture = await openCited(); + cursor(fixture, 'source.py', 2); + vscode.__answerQuickPick(2); + await runReveal(); + assert.equal(picks().length, 1); + const { items, options } = picks()[0]; + assert.deepEqual(items.map((i) => i.label), ['Step: Fit', 'Connection: Fit → Weights', 'F1 Finding: Unverified output']); + assert.deepEqual(items.map((i) => i.description), ['Train', '“produces” · Train · inferred', 'medium · Train · inferred']); + assert.match(options.placeHolder, /^3 claims cite line 3 of source\.py/); + assert.equal(options.matchOnDescription, true); + assert.deepEqual(plain(reveals(fixture.panel)), [{ v: 1, type: 'reveal', kind: 'issue', id: 'risk' }]); + // A selection over both ranges lists each claim once. + cursor(fixture, 'source.py', 2, 5, 3); + await runReveal(); + assert.deepEqual(picks()[1].items.map((i) => i.label), ['Step: Fit', 'Step: Log', 'Connection: Fit → Weights', 'F1 Finding: Unverified output']); + assert.match(picks()[1].options.placeHolder, /^4 claims cite lines 3–6 of source\.py/); + assert.equal(reveals(fixture.panel).length, 1, 'cancelled: nothing more is revealed'); +}); + +test('no claim at the cursor: the nearest claims in the file are offered, nearest first', async () => { + const fixture = await openCited(); + cursor(fixture, 'source.py', 3); + vscode.__answerQuickPick(3); + await runReveal(); + const { items, options } = picks()[0]; + assert.equal(options.title, 'Reveal in Diagram: no claim cites line 4 of source.py'); + assert.equal(options.placeHolder, 'The nearest claims in this file: choose one to show it in the diagram'); + assert.deepEqual(items.map((i) => [i.label, i.detail]), [ + ['Step: Fit', 'Cited at lines 2–3, 1 line above'], + ['Connection: Fit → Weights', 'Cited at lines 2–3, 1 line above'], + ['F1 Finding: Unverified output', 'Counter-evidence at lines 2–3, 1 line above'], + ['Step: Log', 'Cited at line 6, 2 lines below'] + ]); + assert.deepEqual(plain(reveals(fixture.panel)), [{ v: 1, type: 'reveal', kind: 'node', id: 'log' }]); +}); + +test('a file no open diagram cites, and no editor or no panel, are said plainly', async () => { + const fixture = await openCited(); + cursor(fixture, 'plain.py', 0); + await runReveal(); + assert.equal(infos().at(-1), 'MLView: no open diagram cites plain.py.'); + vscode.__setActiveEditor(undefined); + await runReveal(); + assert.match(infos().at(-1), /^MLView: Reveal in Diagram works in the editor of a cited source file or notebook cell\./); + fixture.panel.dispose(); + cursor(fixture, 'source.py', 1); + await runReveal(); + assert.match(infos().at(-1), /^MLView: no diagram is open\./); + assert.equal(picks().length, 0); + assert.equal(reveals(fixture.panel).length, 0); +}); + +test('the parent-folder case: a cited file found only under the folder the notice names is explained, not revealed', async () => { + const document = h.verify(h.workflow(), { 'source.py': 'fit()\n' }); + const fixture = await open({ raw: document, files: { 'copy/source.py': 'fit()\n' }, artifactName: 'copy/run.mlview.json' }); + assert.deepEqual(h.lastBanner(fixture.panel).codes, ['root-hint']); + cursor(fixture, 'copy/source.py', 0); + await settle(); + assert.equal(offered(path.join(fixture.root, 'copy/source.py')), false, 'the diagram is validated against the workspace root, where the file is missing'); + await runReveal(); + assert.equal(infos().at(-1), 'MLView: the diagram cites source.py from the workspace root, where it is missing; this copy in ./copy/ is unchanged. Add that folder to the workspace (the diagram\'s notice offers it), then try again.'); + assert.equal(reveals(fixture.panel).length, 0); +}); + +test('with several panels, the one whose artifact cites the file; when more than one does, the reader chooses', async () => { + const fixture = await openCited(); + const { controller, root } = fixture; + // A second diagram that cites only plain.py. + const only = citedDoc().document; + only.title = 'Second'; + only.evidence = [{ id: 'e', file: 'plain.py', line: 1, endLine: 1, quote: 'pass' }]; + only.nodes[0].evidence = ['e']; + only.nodes.pop(); + only.edges[0].evidence = ['e']; + only.findings[0].evidence = ['e']; + only.findings[0].counterEvidence = []; + only.coverage.inspectedFiles = ['plain.py']; + const second = path.join(root, 'second.mlview.json'); + h.writeJson(second, h.verify(only, { 'plain.py': 'pass\n' })); + await controller.open(vscode.Uri.file(second)); + const panel2 = vscode.__recorded.panels.at(-1); + panel2.fire({ v: 1, type: 'ready' }); + await h.tick(); + // other.py: only the first diagram cites it, so it is used without asking. + cursor(fixture, 'other.py', 0); + await runReveal(); + assert.equal(picks().length, 0); + assert.equal(reveals(fixture.panel).length, 1); + assert.equal(reveals(panel2).length, 0); + // A third diagram that also cites other.py: the reader chooses the diagram, then the claim. + const third = path.join(root, 'third.mlview.json'); + const thirdDoc = citedDoc().document; + thirdDoc.title = 'Third $(zap)'; + h.writeJson(third, thirdDoc); + await controller.open(vscode.Uri.file(third)); + const panel3 = vscode.__recorded.panels.at(-1); + panel3.fire({ v: 1, type: 'ready' }); + await h.tick(); + cursor(fixture, 'other.py', 0); + vscode.__answerQuickPick(1); + await runReveal(); + assert.equal(picks().length, 1); + assert.deepEqual(picks()[0].items.map((i) => [i.label, i.description, i.detail]), [ + ['Authored', 'run.mlview.json', '1 claim cites line 1'], + ['Third \\$(zap)', 'third.mlview.json', '1 claim cites line 1'] + ]); + assert.match(picks()[0].options.placeHolder, /^2 open diagrams cite other\.py/); + assert.equal(reveals(panel3).length, 1, 'revealed in the chosen diagram'); + assert.equal(reveals(fixture.panel).length, 1, 'not in the other'); + assert.deepEqual(panel3.revealCalls.at(-1), { viewColumn: undefined, preserveFocus: false }); +}); + +test('a hidden or reloading page gets the reveal after its next ready, once, and only while it is recent', async () => { + const fixture = await openCited(); + const { panel } = fixture; + cursor(fixture, 'other.py', 0); + await runReveal(); + assert.equal(reveals(panel).length, 1, 'posted at once to the page'); + // VS Code discarded the page (hidden) and loads it again: the new page gets the reveal after the document. + panel.posted.length = 0; + panel.fire({ v: 1, type: 'ready' }); + await h.tick(); + assert.deepEqual(panel.postedTypes().filter((t) => ['workflow', 'reveal'].includes(t)), ['workflow', 'reveal']); + // Once: a later page load does not repeat it. + panel.posted.length = 0; + panel.fire({ v: 1, type: 'ready' }); + await h.tick(); + assert.equal(reveals(panel).length, 0); + // Not recent any more: a page that loads after REVEAL_REPLAY_MS does not get it. + await runReveal(); + const now = Date.now; + try { + Date.now = () => now() + api.REVEAL_REPLAY_MS + 1; + panel.posted.length = 0; + panel.fire({ v: 1, type: 'ready' }); + await h.tick(); + assert.equal(reveals(panel).length, 0); + } finally { + Date.now = now; + } +}); + +test('a revision that changes while the reader chooses reveals nothing and says so', async () => { + const fixture = await openCited(); + cursor(fixture, 'source.py', 1); + // The QuickPick answers after a new revision was adopted. + const original = vscode.window.showQuickPick; + vscode.window.showQuickPick = async (items, options) => { + const offered = await items; + const { document, files } = citedDoc({ id: 'r2', parent: 'r1' }); + h.writeJson(fixture.artifact, h.verify(document, files)); + await h.diskEvent(fixture.panel, 'change', fixture.artifact); + vscode.__recorded.quickPicks.push({ items: offered, options }); + return offered[0]; + }; + try { + await runReveal(); + } finally { + vscode.window.showQuickPick = original; + } + assert.equal(h.shownRevision(fixture.panel), 'r2'); + assert.equal(reveals(fixture.panel).length, 0); + assert.equal(infos().at(-1), 'MLView: the diagram changed to another revision while you were choosing. Run Reveal in Diagram again.'); +}); + +test('notebook cell editors: a cited notebook offers the item in its cells and reveals the claim; another cell offers the nearest; the raw file as text says how', async () => { + const document = h.workflow('notes.ipynb'); + document.evidence = [{ id: 'e', file: 'notes.ipynb', cell: 1, line: 2, endLine: 3, quote: 'fit()\nsave()' }]; + document.edges[0].evidence = ['e']; + document.findings[0].evidence = ['e']; + document.coverage.inspectedFiles = ['notes.ipynb']; + const notebookJson = JSON.stringify({ cells: [{ source: ['a()'] }, { source: ['x = 1\n', 'fit()\n', 'save()'] }], metadata: {}, nbformat: 4, nbformat_minor: 5 }); + const fixture = await open({ raw: document, files: { 'notes.ipynb': notebookJson } }); + const nb = path.join(fixture.root, 'notes.ipynb'); + vscode.__setNotebooks([{ path: nb, cells: [{ text: 'a()' }, { text: 'x = 1\nfit()\nsave()' }] }]); + vscode.__setActiveEditor({ cellOf: nb, cell: 1, viewColumn: 1, selection: [1, 0, 1, 0] }); + await settle(); + assert.equal(offered(nb, { cell: true }), true, 'in a cell editor of the cited notebook'); + assert.equal(offered(nb), true, 'and with the notebook editor\'s own resource'); + vscode.__answerQuickPick(0); + await runReveal(); + assert.deepEqual(picks()[0].items.map((i) => i.label), ['Step: Fit', 'Connection: Fit → Weights', 'F1 Finding: Unverified output']); + assert.match(picks()[0].options.placeHolder, /^3 claims cite cell 1, line 2 of notes\.ipynb/); + assert.deepEqual(plain(reveals(fixture.panel)), [{ v: 1, type: 'reveal', kind: 'node', id: 'n' }]); + // Another cell of the same notebook: the file is cited, so the item is offered; the nearest are offered. + vscode.__setActiveEditor({ cellOf: nb, cell: 0, viewColumn: 1 }); + await settle(); + await runReveal(); + assert.equal(picks()[1].options.title, 'Reveal in Diagram: no claim cites cell 0, line 1 of notes.ipynb'); + assert.deepEqual(picks()[1].items.map((i) => i.detail), ['Cited at cell 1, lines 2–3, 1 cell below', 'Cited at cell 1, lines 2–3, 1 cell below', 'Cited at cell 1, lines 2–3, 1 cell below']); + // The notebook opened as raw JSON text: the item is offered (the list cannot tell the editor's + // form), and the command says the diagram cites it by cell. + cursor(fixture, 'notes.ipynb', 0); + await settle(); + await runReveal(); + assert.equal(infos().at(-1), 'MLView: the diagram cites notes.ipynb by notebook cell. Open it in the notebook editor and use Reveal in Diagram in a cell.'); +}); + +test('the list follows the panels alone: no editor listener, one setContext per change, a panel that throws is skipped', () => { + const counts = () => [vscode.__recorded.activeEditorListeners.length, vscode.__recorded.changeListeners.length, vscode.__recorded.notebookChangeListeners.length]; + const before = counts(); + const warnings = []; + const log = { info() {}, warn: (m) => warnings.push(m), error() {}, debug() {} }; + let files = ['/repo/a.py']; + const panels = [{ citedFiles: () => files }, { citedFiles: () => { throw new Error('gone'); } }]; + const service = new api.RevealInDiagram(() => panels, log); + vscode.__recorded.contexts.delete(KEY); + service.panelsChanged(); + assert.deepEqual(key(), ['/repo/a.py']); + assert.deepEqual(counts(), before, 'no editor listener'); + assert.equal(warnings.length, 1, 'the panel that threw is logged and skipped'); + vscode.__recorded.contexts.delete(KEY); + service.citationsChanged(); + assert.equal(key(), undefined, 'an unchanged list is not sent again'); + files = ['/repo/a.py', '/repo/b.py']; + service.citationsChanged(); + assert.deepEqual(key(), ['/repo/a.py', '/repo/b.py']); + service.dispose(); + assert.deepEqual(key(), []); +}); + +test('everything goes with the last panel: the list empties; the command stays registered', async () => { + const fixture = await openCited(); + const { controller, root } = fixture; + const service = controller.revealer; + const second = path.join(root, 'second.mlview.json'); + h.writeJson(second, citedDoc().document); + await controller.open(vscode.Uri.file(second)); + const panel2 = vscode.__recorded.panels.at(-1); + panel2.fire({ v: 1, type: 'ready' }); + await h.tick(); + await settle(); + const sourceFile = path.join(root, 'source.py'); + assert.equal(offered(sourceFile), true); + fixture.panel.dispose(); + await settle(); + assert.equal(offered(sourceFile), true, 'the second diagram cites the file too'); + panel2.dispose(); + await settle(); + assert.deepEqual(key(), [], 'emptied with the last panel'); + assert.deepEqual(service.citedPaths, []); + assert.equal(offered(sourceFile), false); + // The list does not depend on the editor: an editor change after the last panel changes nothing. + cursor(fixture, 'other.py', 0); + await settle(); + assert.deepEqual(key(), []); + assert.ok(vscode.__recorded.commands.has('mlview.revealInDiagram'), 'a contributed command stays registered'); + controller.dispose(); + assert.equal(vscode.__recorded.commands.has('mlview.revealInDiagram'), false, 'until the extension is disposed'); +}); + +test('controller dispose with panels open empties the list and removes the command', async () => { + const fixture = await openCited(); + await settle(); + assert.equal(offered(path.join(fixture.root, 'source.py')), true); + fixture.controller.dispose(); + await settle(); + assert.deepEqual(key(), []); + assert.equal(vscode.__recorded.commands.has('mlview.revealInDiagram'), false); +}); diff --git a/vscode-extension/test/walk-host.test.js b/vscode-extension/test/walk-host.test.js new file mode 100644 index 00000000..64ddd78f --- /dev/null +++ b/vscode-extension/test/walk-host.test.js @@ -0,0 +1,440 @@ +'use strict'; +/** + * Viewer M3, the host side of the review walk (roadmap step 12): numbered opens (`seq`) whose + * superseded requests are dropped, one `actionResult` per open with a `requestId`, blocked opens of + * stale or missing files that never open them and raise no notification for the walk, the + * whole-range highlight with its overview-ruler mark cleared at the walk's end and on dispose, the + * walk's own selection collapsed when the walk ends, clears or is blocked (a selection the reader + * changed is kept), and the jump checks cached per revision and freshness version. Mock `vscode` + * only: none of this is a live check. + */ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const fsp = require('node:fs/promises'); +const path = require('node:path'); +const h = require('./panel-helpers'); +const { api, vscode } = h; + +const fixtures = []; +test.afterEach(() => h.cleanup(fixtures)); +async function open(options) { + const fixture = await h.openPanel(options); + fixtures.push(fixture); + return fixture; +} +const shown = () => vscode.__recorded.shownDocuments; +const warnings = () => vscode.__recorded.messages.filter((m) => m[0] === 'warn').map((m) => m[1]); +const openResults = (panel) => h.results(panel).filter((m) => m.action === 'openLocation'); +const range = (r) => [r.start.line, r.start.character, r.end.line, r.end.character]; +const walkOpen = (panel, evidenceId, seq, extra = {}) => panel.fire({ v: 1, type: 'openLocation', evidenceId, walk: true, seq, requestId: `w${seq}`, ...extra }); + +/** Two evidence records in two files, lines 2-3 of source.py and line 1 of other.py, published with hashes. */ +function twoFiles() { + const document = h.workflow(); + document.evidence = [ + { id: 'e', file: 'source.py', line: 2, endLine: 3, quote: 'train()\nsave()' }, + { id: 'e2', file: 'other.py', line: 1, endLine: 1, quote: 'other()' } + ]; + document.nodes[0].evidence = ['e', 'e2']; + document.coverage.inspectedFiles = ['source.py', 'other.py']; + const files = { 'source.py': 'import x\ntrain()\nsave()\n', 'other.py': 'other()\n' }; + return { document: h.verify(document, files), files }; +} + +function notebookDocument() { + const document = h.workflow('notes.ipynb'); + document.evidence = [ + { id: 'e', file: 'notes.ipynb', cell: 1, line: 2, endLine: 3, quote: 'fit()\nsave()' }, + { id: 'e2', file: 'source.py', line: 1, endLine: 1, quote: 'fit()' } + ]; + document.nodes[0].evidence = ['e', 'e2']; + document.coverage.inspectedFiles = ['notes.ipynb', 'source.py']; + const notebook = JSON.stringify({ cells: [{ source: ['a()'] }, { source: ['x = 1\n', 'fit()\n', 'save()'] }], metadata: {}, nbformat: 4, nbformat_minor: 5 }); + return { document, files: { 'notes.ipynb': notebook, 'source.py': 'fit()\n' } }; +} +async function openNotebookPanel({ cellEditors = true } = {}) { + const { document, files } = notebookDocument(); + const fixture = await open({ raw: document, files, ready: false }); + vscode.__setNotebooks([{ path: path.join(fixture.root, 'notes.ipynb'), cells: [{ text: 'a()' }, { text: 'x = 1\nfit()\nsave()' }] }]); + vscode.__setNotebookCellEditors(cellEditors); + fixture.panel.fire({ v: 1, type: 'ready' }); + await h.tick(); + return fixture; +} + +test('a walk open is answered done, keeps focus on the diagram and highlights the range with an overview-ruler mark', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + walkOpen(panel, 'e', 1); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered'); + assert.deepEqual(openResults(panel)[0], { v: 1, type: 'actionResult', requestId: 'w1', action: 'openLocation', outcome: 'done', seq: 1 }); + assert.equal(shown().length, 1); + assert.equal(shown()[0].options.preserveFocus, true, 'preserveFocus stays the default'); + const [style] = vscode.__recorded.decorationTypes; + assert.equal(style.options.overviewRulerColor.id, 'editorOverviewRuler.rangeHighlightForeground'); + assert.equal(style.options.overviewRulerLane, vscode.OverviewRulerLane.Full); + assert.deepEqual(shown()[0].editor.decorations[0].ranges.map(range), [[1, 0, 2, 'save()'.length]]); +}); + +test('an open whose seq is not above the last one is dropped and answered cancelled', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + walkOpen(panel, 'e', 5); + await h.waitFor(() => openResults(panel).length === 1, 'the first open was not answered'); + walkOpen(panel, 'e2', 3); + walkOpen(panel, 'e2', 5); + await h.waitFor(() => openResults(panel).length === 3, 'the late opens were not answered'); + assert.deepEqual(openResults(panel).map((r) => [r.seq, r.outcome]), [[5, 'done'], [3, 'cancelled'], [5, 'cancelled']]); + assert.equal(shown().length, 1, 'neither late open touched an editor'); + assert.equal(path.basename(shown()[0].document.uri.fsPath), 'source.py'); + // A malformed seq is ignored, not trusted: the open is treated as unnumbered. + panel.fire({ v: 1, type: 'openLocation', evidenceId: 'e2', seq: 'x', requestId: 'm1' }); + await h.waitFor(() => openResults(panel).length === 4, 'the unnumbered open was not answered'); + assert.equal(openResults(panel)[3].outcome, 'done'); + assert.equal('seq' in openResults(panel)[3], false); +}); + +test('a burst of numbered opens navigates once, for the last, and answers every one', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + for (let seq = 1; seq <= 12; seq++) walkOpen(panel, seq % 2 ? 'e' : 'e2', seq); + await h.waitFor(() => openResults(panel).length === 12, 'not every open was answered'); + const outcomes = openResults(panel).map((r) => [r.seq, r.outcome]).sort((a, b) => a[0] - b[0]); + assert.deepEqual(outcomes, [...Array.from({ length: 11 }, (_, i) => [i + 1, 'cancelled']), [12, 'done']]); + assert.equal(shown().length, 1, 'only the last open showed its source'); + assert.equal(path.basename(shown()[0].document.uri.fsPath), 'other.py'); + assert.equal(vscode.__recorded.decorationTypes.length, 1); +}); + +test('a later open supersedes a notebook open still waiting for its cell editor', async () => { + const { panel } = await openNotebookPanel({ cellEditors: false }); + walkOpen(panel, 'e', 1); + await h.waitFor(() => vscode.__recorded.shownNotebooks.length === 1, 'the notebook open did not start'); + walkOpen(panel, 'e2', 2); + await h.waitFor(() => openResults(panel).length === 2, 'both opens were not answered', 3000); + assert.deepEqual(openResults(panel).map((r) => [r.seq, r.outcome]).sort((a, b) => a[0] - b[0]), [[1, 'cancelled'], [2, 'done']]); + const [highlight] = vscode.__recorded.decorationTypes; + assert.equal(highlight.disposed, false, 'the superseded notebook open left the later highlight alone'); +}); + +test('a changed or missing file is blocked with its reason, never opened, and a walk open raises no notification', async () => { + const { document, files } = twoFiles(); + const { panel, root } = await open({ raw: document, files: { 'source.py': files['source.py'] + '# edited\n' } }); + const before = warnings().length; + walkOpen(panel, 'e', 1); + walkOpen(panel, 'e2', 2, { requestId: 'w2' }); + await h.waitFor(() => openResults(panel).length === 2, 'the blocked opens were not answered'); + const [changed, missing] = openResults(panel).sort((a, b) => a.seq - b.seq); + // The first is superseded by the second before its check ends; ask again on its own. + assert.equal(changed.outcome, 'cancelled'); + assert.deepEqual(missing, { v: 1, type: 'actionResult', requestId: 'w2', action: 'openLocation', outcome: 'blocked', seq: 2, reason: 'missing', message: 'other.py is missing since revision r1 was published; not opened.' }); + walkOpen(panel, 'e', 3); + await h.waitFor(() => openResults(panel).length === 3, 'the changed open was not answered'); + assert.deepEqual(openResults(panel)[2], { v: 1, type: 'actionResult', requestId: 'w3', action: 'openLocation', outcome: 'blocked', seq: 3, reason: 'changed', message: 'source.py changed after revision r1 was published; not opened.' }); + assert.equal(shown().length, 0, 'a stale file is never opened'); + assert.equal(vscode.__recorded.decorationTypes.length, 0); + assert.equal(warnings().length, before, 'no notification for an open from the walk'); + assert.equal(fs.existsSync(path.join(root, 'other.py')), false); +}); + +test('an ordinary open of a stale file keeps its notification and, with a requestId, is answered blocked too', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files: { ...files, 'source.py': 'changed()\n' } }); + panel.fire({ v: 1, type: 'openLocation', evidenceId: 'e', requestId: 'plain1' }); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered'); + assert.equal(openResults(panel)[0].outcome, 'blocked'); + assert.equal(openResults(panel)[0].reason, 'changed'); + assert.ok(warnings().includes('MLView: evidence e cites source.py, which changed after revision r1 was published; navigation to it is blocked.')); + // Without a requestId nothing is answered (the M1 and M2 opens send none). + panel.fire({ v: 1, type: 'openLocation', evidenceId: 'e2' }); + await h.waitFor(() => shown().length === 1, 'the fresh file did not open'); + assert.equal(openResults(panel).length, 1); +}); + +test('a removed notebook cell and unknown evidence are answered blocked without a notification from the walk', async () => { + const { panel, root } = await openNotebookPanel(); + vscode.__setNotebooks([{ path: path.join(root, 'notes.ipynb'), cells: [{ text: 'a()' }] }]); + const before = warnings().length; + walkOpen(panel, 'e', 1); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered'); + assert.equal(openResults(panel)[0].outcome, 'blocked'); + assert.equal(openResults(panel)[0].reason, 'cell-missing'); + assert.equal(openResults(panel)[0].message, 'Cell 1 of notes.ipynb no longer exists; not opened.'); + walkOpen(panel, 'nope', 2); + await h.waitFor(() => openResults(panel).length === 2, 'the unknown evidence was not answered'); + assert.equal(openResults(panel)[1].reason, 'unknown'); + assert.equal(warnings().length, before); +}); + +test('the walk end clears the highlight and drops a walk open still on its way; dispose clears it too', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + walkOpen(panel, 'e', 1); + await h.waitFor(() => vscode.__recorded.decorationTypes.length === 1, 'the open did not highlight'); + panel.fire({ v: 1, type: 'walk', state: 'end' }); + assert.equal(vscode.__recorded.decorationTypes[0].disposed, true, 'the walk end cleared the highlight'); + // Any other state is ignored. + walkOpen(panel, 'e2', 2); + await h.waitFor(() => vscode.__recorded.decorationTypes.length === 2, 'the next open did not highlight'); + panel.fire({ v: 1, type: 'walk', state: 'paused' }); + assert.equal(vscode.__recorded.decorationTypes[1].disposed, false); + panel.dispose(); + assert.equal(vscode.__recorded.decorationTypes[1].disposed, true, 'closing the panel cleared the highlight'); +}); + +test('a walk clear (a claim with nothing to open) clears the highlight and drops a walk open on its way', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + walkOpen(panel, 'e', 1); + await h.waitFor(() => vscode.__recorded.decorationTypes.length === 1, 'the open did not highlight'); + panel.fire({ v: 1, type: 'walk', state: 'clear' }); + assert.equal(vscode.__recorded.decorationTypes[0].disposed, true, 'the clear removed the highlight'); + // The walk goes on: the next open highlights again. + walkOpen(panel, 'e2', 2); + await h.waitFor(() => vscode.__recorded.decorationTypes.length === 2, 'the next open did not highlight'); + assert.equal(vscode.__recorded.decorationTypes[1].disposed, false); +}); + +test('a walk clear drops a notebook open waiting for its cell editor', async () => { + const { panel } = await openNotebookPanel({ cellEditors: false }); + walkOpen(panel, 'e', 1); + await h.waitFor(() => vscode.__recorded.shownNotebooks.length === 1, 'the notebook open did not start'); + panel.fire({ v: 1, type: 'walk', state: 'clear' }); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered', 3000); + assert.equal(openResults(panel)[0].outcome, 'cancelled'); +}); + +test('a blocked walk open clears the previous claim\'s highlight; a blocked ordinary open keeps it', async () => { + const { document, files } = twoFiles(); + // other.py is missing on disk, so its evidence is blocked; source.py is fresh. + const { panel } = await open({ raw: document, files: { 'source.py': files['source.py'] } }); + walkOpen(panel, 'e', 1); + await h.waitFor(() => vscode.__recorded.decorationTypes.length === 1, 'the fresh open did not highlight'); + // An ordinary open of the missing file (Enter, an Open link) keeps the highlight, as in M2. + panel.fire({ v: 1, type: 'openLocation', evidenceId: 'e2', requestId: 'plain1' }); + await h.waitFor(() => openResults(panel).length === 2, 'the ordinary open was not answered'); + assert.equal(openResults(panel)[1].outcome, 'blocked'); + assert.equal(vscode.__recorded.decorationTypes[0].disposed, false, 'an ordinary blocked open leaves the highlight'); + // The walk's own open of it clears it: the editor no longer shows the earlier claim's lines. + walkOpen(panel, 'e2', 2); + await h.waitFor(() => openResults(panel).length === 3, 'the walk open was not answered'); + assert.equal(openResults(panel)[2].outcome, 'blocked'); + assert.equal(vscode.__recorded.decorationTypes[0].disposed, true, 'a blocked walk open clears the highlight'); + assert.equal(shown().length, 1, 'the missing file was never opened'); + // Unknown evidence from the walk clears it too (nothing to show for it). + walkOpen(panel, 'e', 3); + await h.waitFor(() => vscode.__recorded.decorationTypes.length === 2, 'the fresh open did not highlight again'); + walkOpen(panel, 'nope', 4); + await h.waitFor(() => openResults(panel).length === 5, 'the unknown open was not answered'); + assert.equal(openResults(panel)[4].reason, 'unknown'); + assert.equal(vscode.__recorded.decorationTypes[1].disposed, true); +}); + +/** Viewer M3 (live check): the selection a walk open set, as [startLine, startChar, endLine, endChar]. */ +const sel = (editor) => range(editor.selection); + +test('the walk end and a walk clear collapse the selection the walk set to its start', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + vscode.__setShownEditorsVisible(true); + walkOpen(panel, 'e', 1); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered'); + const editor = shown()[0].editor; + assert.deepEqual(sel(editor), [1, 0, 2, 'save()'.length], 'the open selects the cited lines'); + panel.fire({ v: 1, type: 'walk', state: 'end' }); + assert.deepEqual(sel(editor), [1, 0, 1, 0], 'the walk end leaves the cursor at the start of the cited lines'); + assert.equal(vscode.__recorded.decorationTypes[0].disposed, true); + walkOpen(panel, 'e2', 2); + await h.waitFor(() => openResults(panel).length === 2, 'the next open was not answered'); + const other = shown()[1].editor; + assert.deepEqual(sel(other), [0, 0, 0, 'other()'.length]); + panel.fire({ v: 1, type: 'walk', state: 'clear' }); + assert.deepEqual(sel(other), [0, 0, 0, 0], 'a walk clear collapses it too'); + // Released once: a second end changes nothing, even if the reader selected the same lines again. + other.selection = new vscode.Selection(new vscode.Position(0, 0), new vscode.Position(0, 'other()'.length)); + panel.fire({ v: 1, type: 'walk', state: 'end' }); + assert.deepEqual(sel(other), [0, 0, 0, 'other()'.length]); +}); + +test('the walk end leaves a selection the reader changed, and an editor no longer shown', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + vscode.__setShownEditorsVisible(true); + walkOpen(panel, 'e', 1); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered'); + const editor = shown()[0].editor; + // The reader moved the cursor (or selected something else) in the editor beside. + editor.selection = new vscode.Selection(new vscode.Position(2, 0), new vscode.Position(2, 3)); + panel.fire({ v: 1, type: 'walk', state: 'end' }); + assert.deepEqual(sel(editor), [2, 0, 2, 3], 'the reader\'s selection is kept'); + assert.equal(vscode.__recorded.decorationTypes[0].disposed, true, 'the highlight still goes'); + // Only the end of the range differs: still the reader's, still kept. + walkOpen(panel, 'e', 2); + await h.waitFor(() => openResults(panel).length === 2, 'the second open was not answered'); + const again = shown()[1].editor; + again.selection = new vscode.Selection(new vscode.Position(1, 0), new vscode.Position(2, 2)); + panel.fire({ v: 1, type: 'walk', state: 'end' }); + assert.deepEqual(sel(again), [1, 0, 2, 2]); + // An editor that is no longer visible (its preview tab was replaced, or it was closed) is not touched. + walkOpen(panel, 'e', 3); + await h.waitFor(() => openResults(panel).length === 3, 'the third open was not answered'); + const closed = shown()[2].editor; + vscode.__setVisibleTextEditors([]); + panel.fire({ v: 1, type: 'walk', state: 'end' }); + assert.deepEqual(sel(closed), [1, 0, 2, 'save()'.length]); +}); + +test('a blocked walk open collapses the previous claim\'s selection; a blocked ordinary open keeps it', async () => { + const { document, files } = twoFiles(); + // other.py is missing on disk, so its evidence is blocked; source.py is fresh. + const { panel } = await open({ raw: document, files: { 'source.py': files['source.py'] } }); + vscode.__setShownEditorsVisible(true); + walkOpen(panel, 'e', 1); + await h.waitFor(() => openResults(panel).length === 1, 'the fresh open was not answered'); + const editor = shown()[0].editor; + panel.fire({ v: 1, type: 'openLocation', evidenceId: 'e2', requestId: 'plain1' }); + await h.waitFor(() => openResults(panel).length === 2, 'the ordinary open was not answered'); + assert.equal(openResults(panel)[1].outcome, 'blocked'); + assert.deepEqual(sel(editor), [1, 0, 2, 'save()'.length], 'an ordinary blocked open keeps the selection, as it keeps the highlight'); + walkOpen(panel, 'e2', 2); + await h.waitFor(() => openResults(panel).length === 3, 'the walk open was not answered'); + assert.equal(openResults(panel)[2].outcome, 'blocked'); + assert.deepEqual(sel(editor), [1, 0, 1, 0], 'a blocked walk open collapses it with the highlight'); + // Unknown evidence from the walk does the same. + walkOpen(panel, 'e', 3); + await h.waitFor(() => openResults(panel).length === 4, 'the fresh open was not answered again'); + const again = shown()[1].editor; + walkOpen(panel, 'nope', 4); + await h.waitFor(() => openResults(panel).length === 5, 'the unknown open was not answered'); + assert.deepEqual(sel(again), [1, 0, 1, 0]); +}); + +test('the walk end collapses the selection in a notebook cell editor', async () => { + const { panel, root } = await openNotebookPanel(); + walkOpen(panel, 'e', 1); + await h.waitFor(() => openResults(panel).length === 1, 'the notebook open was not answered', 3000); + assert.equal(openResults(panel)[0].outcome, 'done'); + const notebook = vscode.workspace.notebookDocuments.find((nb) => nb.uri.fsPath === path.join(root, 'notes.ipynb')); + const cellEditor = vscode.window.visibleTextEditors.find((e) => e.document === notebook.cellAt(1).document); + assert.ok(cellEditor, 'the cell editor is visible'); + assert.deepEqual(sel(cellEditor), [1, 0, 2, 'save()'.length]); + panel.fire({ v: 1, type: 'walk', state: 'end' }); + assert.deepEqual(sel(cellEditor), [1, 0, 1, 0]); +}); + +test('the walk end drops a notebook open waiting for its cell editor', async () => { + const { panel } = await openNotebookPanel({ cellEditors: false }); + walkOpen(panel, 'e', 1); + await h.waitFor(() => vscode.__recorded.shownNotebooks.length === 1, 'the notebook open did not start'); + panel.fire({ v: 1, type: 'walk', state: 'end' }); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered', 3000); + assert.equal(openResults(panel)[0].outcome, 'cancelled'); +}); + +test('highlight: false selects and reveals the range without the decoration, and clears the previous one', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + walkOpen(panel, 'e', 1); + await h.waitFor(() => vscode.__recorded.decorationTypes.length === 1, 'the open did not highlight'); + walkOpen(panel, 'e2', 2, { highlight: false }); + await h.waitFor(() => shown().length === 2, 'the second open did not show'); + assert.equal(vscode.__recorded.decorationTypes.length, 1, 'no new decoration'); + assert.equal(vscode.__recorded.decorationTypes[0].disposed, true, 'the previous highlight is gone'); + assert.deepEqual(range(shown()[1].editor.selection), [0, 0, 0, 'other()'.length]); +}); + +test('a new page numbers its opens from the start again', async () => { + const { document, files } = twoFiles(); + const { panel } = await open({ raw: document, files }); + walkOpen(panel, 'e', 9); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered'); + panel.fire({ v: 1, type: 'ready' }); + await h.tick(); + walkOpen(panel, 'e2', 1); + await h.waitFor(() => openResults(panel).length === 2, 'the open from the new page was not answered'); + assert.equal(openResults(panel)[1].outcome, 'done'); +}); + +test('a new page drops a numbered open the old page started', async () => { + const { panel } = await openNotebookPanel({ cellEditors: false }); + walkOpen(panel, 'e', 4); + await h.waitFor(() => vscode.__recorded.shownNotebooks.length === 1, 'the notebook open did not start'); + // The webview was recreated (hidden and shown again, or reloaded) while the open waited. + panel.fire({ v: 1, type: 'ready' }); + await h.waitFor(() => openResults(panel).length === 1, 'the open was not answered', 3000); + assert.equal(openResults(panel)[0].outcome, 'cancelled'); +}); + +/** + * Count fs.promises.readFile calls for `file` while `run` runs (the bundle reads through live getters). + * Matched by identity on disk: the host reads a cited file at its realpath, which on a Windows runner + * is the long form of a temp folder that os.tmpdir() spells in 8.3 short form (RUNNER~1). + */ +async function countReads(file, run) { + const original = fsp.readFile; + const identity = (p) => { + let real; + try { + real = fs.realpathSync.native(p); + } catch (_e) { + real = path.resolve(p); + } + return process.platform === 'win32' ? real.toLowerCase() : real; + }; + const want = identity(file); + let reads = 0; + fsp.readFile = function (target, ...rest) { + if (typeof target === 'string' && identity(target) === want) reads++; + return original.call(this, target, ...rest); + }; + try { + await run(); + } finally { + fsp.readFile = original; + } + return reads; +} + +test('jump checks are cached per freshness version: one read for a fresh file, one validation for a stale one', async () => { + let validations = 0; + const validator = async (...args) => { validations++; return api.validateWorkflow(...args); }; + const { document, files } = twoFiles(); + const { panel, root } = await open({ raw: document, files: { ...files, 'source.py': 'changed()\n' }, validator }); + assert.equal(validations, 1, 'one validation to display the revision'); + const other = path.join(root, 'other.py'); + let seq = 0; + const reads = await countReads(other, async () => { + for (let i = 1; i <= 4; i++) { + walkOpen(panel, 'e2', ++seq); + await h.waitFor(() => openResults(panel).length === seq, 'the fresh open was not answered'); + } + }); + assert.equal(reads, 1, 'four opens of an unchanged file read it once'); + for (let i = 1; i <= 3; i++) { + walkOpen(panel, 'e', ++seq); + await h.waitFor(() => openResults(panel).length === seq, 'the stale open was not answered'); + assert.equal(openResults(panel).at(-1).reason, 'changed'); + } + assert.equal(validations, 2, 'three opens of a stale file ran one full validation'); + // A watcher event starts a new freshness version: the next open checks again. + await h.diskEvent(panel, 'change', path.join(root, 'source.py')); + const afterEvent = validations; + walkOpen(panel, 'e', ++seq); + await h.waitFor(() => openResults(panel).length === seq, 'the open after the event was not answered'); + assert.equal(validations, afterEvent + 1, 'the cache did not outlive the freshness version'); + // A file rewritten with no watcher event has a new identity on disk: checked again, and blocked. + fs.writeFileSync(other, 'other()\n# edited\n'); + walkOpen(panel, 'e2', ++seq); + await h.waitFor(() => openResults(panel).length === seq, 'the open of the rewritten file was not answered'); + assert.equal(openResults(panel).at(-1).outcome, 'blocked'); + assert.equal(openResults(panel).at(-1).reason, 'changed'); +}); + +test('blocked-open wording names the file, the reason and that nothing was opened', () => { + assert.equal(api.blockedOpenText('changed', 'a.py', { revisionId: 'r2' }), 'a.py changed after revision r2 was published; not opened.'); + assert.equal(api.blockedOpenText('too-large', 'a.py'), 'a.py has grown past the size MLView can check; not opened.'); + assert.equal(api.blockedOpenText('elsewhere', 'a.py', { folder: './copy/' }), 'a.py is not in the workspace root; it is unchanged in ./copy/. Not opened.'); + assert.equal(api.blockedOpenText('unsaved', 'a.py'), 'Unsaved changes in a.py no longer contain the cited lines; not opened. Save or revert the file.'); + assert.equal(api.blockedOpenText('unchecked', 'a.py', { issue: 'bad' }), 'a.py could not be checked (bad); not opened.'); +}); diff --git a/webview/README.md b/webview/README.md index c3f69b75..a7a4da93 100644 --- a/webview/README.md +++ b/webview/README.md @@ -70,7 +70,8 @@ Viewer M1 Inspector content, the Selection pane since viewer M2 (no protocol cha - Phase colour by document order. `GraphIndex.phaseIndexOf(id)` is the phase's position among the declared phases; `render/phase.ts` stamps `data-phase-index` and `data-phase-tone` (index mod 8) on lanes, cards, - groups, connections, trunks and minimap dots, and node.css + groups, connections, trunks and (since viewer M3) the phase index's rows and + the phase overview's blocks, and node.css binds `--mlv-stage` from the tone (`--mlv-phase-0` … `--mlv-phase-7` in tokens.css, eight literals per theme kind, the contrast border in high contrast). `data-stage` stays, because the golden hashes it. @@ -112,7 +113,8 @@ Viewer M1 Inspector content, the Selection pane since viewer M2 (no protocol cha anchored top-left at 0.9. A document narrower or shorter than the canvas at that zoom is centred on that axis. `ViewportController.fit()` runs it (first paint, key 0, a refit on resize); `fitWhole()` is the ⋯ menu's - **Fit the whole diagram** and Overview. + **Fit the whole diagram** (Shift+0 was Overview, fold every group and fit + the whole, until viewer M3 made it the phase overview). The old top-anchored tall branch (`TALL_SCREENS`, `MIN_FIT_ZOOM`) is gone. `App.setWorkflow` still restores a viewport saved for the same revision. - Compact level (`data-lod="compact"`, below 0.62): node.css hides the icon @@ -154,8 +156,8 @@ golden is byte-identical): keys on these attributes, so the row never wraps. - `MoreMenu` (`src/ui/moremenu.ts`) is a menu button. Items carry `data-more-item` (`search`, `about` and `exceptions` only while the row has - folded them, marked `data-folded`; then `legend`, `flow`, - `minimap`, `rail`, `fit`, `zoomsel`, `svg`, `png`, `copy-svg`, + folded them, marked `data-folded`; then `review`, `overview`, `legend`, + `flow`, `phaseindex`, `rail`, `fit`, `zoomsel`, `svg`, `png`, `copy-svg`, `shortcuts`); the exports also carry `data-export-action`. The panel is mounted on the app root and repaints its checkboxes as it opens. The exports are always the whole diagram; Copy PNG, Print and the region @@ -182,7 +184,9 @@ golden is byte-identical): canvas (the extension sets no `enableFindWidget`, so the webview has no find bar of its own; Ctrl/Cmd+K did the same until the live-check fixes). Search rows print the title first and the location under it, cut from the start; `.mlv-result__count` heads the list. -- Chrome icons are 19 inline SVG paths in `src/icons.ts` (`uiIcon`), drawn in +- Chrome icons are 21 inline SVG paths in `src/icons.ts` (`uiIcon`; viewer M3 + added `review`, and step 13 replaced `minimap` with `phases` and + `phaseindex`), drawn in the codicon style; there is no icon font, so the CSP is unchanged. - `test/header-m2.test.mjs` injects the shipped stylesheet and stubs the root width to check the header at 1440, 900 and 541 px, the ⋯ menu, the removed @@ -301,13 +305,11 @@ byte-identical): accepts the new size (no refit of the first view) and reveals the selection. A selection already out of view, or no selection, keeps the old behaviour (`onResize`). -- The overview map's width rule is `CanvasView.setPanelWidth(width)`, which - sets `.mlv-minimap.is-narrow` at `MINIMAP_NARROW_W` (900 px, - `src/canvas/host.ts`) or less, in place of a media query. - `CanvasView.minimapUnavailable()` returns the reason the map is not drawn - (fewer than 30 cards, `is-narrow`, `is-short`) or `null`; the ⋯ menu's - `minimap` item is disabled and unchecked while it is not null and prints - the reason in `.mlv-moremenu__note`. +- The overview map's width rule was `CanvasView.setPanelWidth(width)`, with + `CanvasView.minimapUnavailable()` giving the ⋯ menu's disabled item its + reason. Viewer M3 step 13 replaced the map with the phase index; the + panel width now picks the index's form, and `phaseIndexUnavailable()` gives + the reason (below). - Keys: the only Ctrl/Cmd chord the viewer answers is `Mod+F` (`handleCanvasKey` in `src/ui/keymap.ts` returns false for the others, so they reach the workbench). `b` toggles the rail (was Ctrl+B) and `t` calls @@ -329,22 +331,246 @@ byte-identical): - `test/m2-live.test.mjs` holds the regression tests for fixes 1, 4, 5, 6 and 7; the hue test for fix 3 is in `test/calm-canvas.test.mjs`. +Viewer M3 review walk, host side, and Escape (no contract change, no new +setting; the golden is byte-identical): + +- `openLocation` takes four optional fields (`UiToHost` in `src/types.ts`): + `seq`, a positive integer that increases with every numbered open of a + page; `requestId`, which asks for one `actionResult`; `walk: true`, an open + from the review walk; and `highlight: false`, which selects the range + without the whole-range highlight. Opens without them behave as in M2. +- The host drops a numbered open whose `seq` is not above the last one it saw + on this page (answered `cancelled`), and an open a later open overtook while + it waited (a notebook's cell editor, for example). A new page (`ready`) + starts the numbering again. The walk sends a jump only after the reader has + stopped moving for `WALK_OPEN_DEBOUNCE_MS` (150 ms, `src/app/walk.ts`). +- `actionResult` answers `openLocation` too: `done`, `blocked` (not opened, + with `reason` and a short `message` such as "train.py changed after + revision r3 was published; not opened."), `cancelled` or `failed`. It + repeats the request's `seq`. `reason` is one of `changed`, `missing`, + `unreadable`, `too-large`, `elsewhere`, `unsaved`, `cell-missing`, + `unchecked` or `unknown` (`OpenBlockReason`). The message never holds an + absolute path. +- A blocked walk open raises no VS Code notification; the reason is only in + the result. A blocked open from Enter, a double-click or an Open link still + shows the M2 warning notification, and is answered as well when it carried + a `requestId`. +- `{ type: 'walk', state: 'end' }` is new: the walk ended, so the host clears + the cited-range highlight (and its overview-ruler mark) and drops a walk open + still waiting. Closing the panel clears it too. `state: 'clear'` (added with + step 11) does the same while the walk goes on: the walk moved to a claim it + opens nothing for (no quotes, or a claim the reader only selected). A + blocked walk open also clears the previous claim's highlight; a blocked + ordinary open keeps it. +- The checks behind a jump are cached per revision and freshness version, keyed + by each cited file's device, inode, size, and modification and change times + (`checkCitedFile` in `vscode-extension/src/authoredPanel.ts`). The first jump + into a file hashes that file once; later jumps into it read no file content + while those stay the same. A file whose stat changed without a watcher event + is checked again. +- Escape: `KeyCommands.escape()` returns whether the cascade did anything. + When the focus is already off the canvas and nothing is open or selected, + `handleCanvasKey` leaves Escape unconsumed so VS Code can use it (for + example to hide a notification). VS Code's webview host forwards every + keydown to the workbench, even one the page called `preventDefault()` on, + and its window listener runs before any page script. So after the mount + the panel's bootstrap (`render()` in `vscode-extension/src/authoredPanel.ts`) + adds a document-level keydown listener that stops an Escape the viewer + already handled (`defaultPrevented`) before it reaches that window + listener. The search box's Escape (`SearchController.handleKey`) is marked + handled too. `authoredEscapeHandshake` in `test/authored-handshake.mjs` pins + both with a stand-in forwarder attached before the scripts. + +Viewer M3 review walk, the viewer (roadmap step 11; no contract change, no new +setting; the golden is byte-identical): + +- `src/walk.ts` is pure: `claimOrder(index)` walks the drawn order (lanes, then + `roots` and `laneChildren` depth first, as the Outline lists them): each + step, its `outEdges`, then the findings whose first cited step in that order + it is; then any connection whose source no lane reached (none can today), + then findings citing no step, in document order (the roadmap is silent on + those). It also holds the + filters (`notObserved` uses `isNotObserved`, the same test as the header's + count; `changed` is offered unless every stale file is only in another + folder, the root hint's case), + `positionFor` (keep the claim, else the next one in drawn order), and the + bar, pane and live-region wording. +- `src/app/walk.ts` (`ReviewWalk`) runs it: start, stop, step, quotes, the + `u` and `n` / `p` jumps, filters, the debounced numbered open + (`openLocation` with `walk: true`, `seq`, `requestId` via + `App.postRequest`), only the latest answer shown, `walk: 'clear'` for a + claim with nothing to open, and the place per revision. A reader's own + selection of a claim in the list moves the walk without opening it. + M3 review fixes: `start(filter)` resumes the remembered claim only for the + same filter (another starts at position 0); any move resets `latestSeq`, so + a late answer for a claim or quote the walk has left is dropped; a claim the + walk is on but has not asked for gets `unansweredStatus('idle')` (the + viewer's own stale reason, never "Enter shows" for a stale file), and a + filter that keeps the claim keeps the status; `onStale()` shows (selects, announces, clears + the old highlight, does not open) the claim that takes the place of one that + left Changed files; a `done` after Alt+Enter carries `focusEditor`, and the + bar says the focus moved; `walkResultAnnouncement` says "not opened" once. +- `src/ui/walkbar.ts` is the bar: a labelled region at the end of `.mlv-main` + (after the canvas, before the rail), one `role="toolbar"` tab stop for + Previous and Next (`[data-walk-step]`, as k and j), the filters and Exit, + `data-layout="narrow"` below 620 px, where the visible place is "3/16" + (`aria-hidden`) and `.mlv-walkbar__posspoken` holds the whole sentence for a + screen reader. Escape inside it ends the walk; j, k, [, ], u, n and p + pressed there go to the walk. The header's Review and the ⋯ menu's item + focus the canvas when they start the walk (`buildAppUi`). +- `ViewState.walk` (`WalkViewState`: `filter`, `claim`, `quote` when not the + first, `active` while running) is written only once a walk ran for the + displayed revision, restored only for the same `workflowRevision` + (`sanitizeWalk`), and a remount resumes a running walk without posting an + open. No other walk state is kept; nothing is a verdict. +- Keys (`KEYMAP`, `handleCanvasKey`): `r` toggles, `u` / Shift+U, and only + while walking `j` / `k`, ↓ / ↑ and `[` / `]`; `n` / `p` follow the walk's + order while it runs (`CommandPort.walkFindings`). The Escape cascade ends + the walk after the legend and before the bottom sheet (`appkeys.ts`), and an + Escape inside the open sheet ends a running walk first. +- The header's `.mlv-header__review` goes with the revision chip (below + 620 px or at fit level 1); the ⋯ menu always has `review`. `HostNotice` + offers `[data-notice-action="review"]` for `stale` while claims cite a + changed file. The Selection pane marks the walk's quote (`applyWalkMark`: + `.mlv-quote.is-walk`, `.mlv-quote__walk`) in place and scrolls it into view + on `[` / `]`. +- `test/walk.test.mjs` covers the order on synthetic documents with the + vit-cc and yolov5-cc2 shapes (79 and 176 claims), filter counts against the + header, keys, debounce and `seq`, blocked answers, announcements, the place + per revision, Changed files, Tab order and 541 / 900 px. + `authoredWalkHandshake` in `test/authored-handshake.mjs` drives the real + host with the built viewer. + +Viewer M3 phase overview and phase index (roadmap step 13; no contract +change, no new setting; the golden is byte-identical): + +- `src/render/phaseoverview.ts` is pure. `overviewInput(index, keep)` lists + each lane's steps in the walk's and the Outline's order (`roots`, then + `laneChildren` depth first) with basis, depth and the F labels the severity + toggles keep, the findings per phase from `laneCounts` (the PR #14 rule), and + the connections as a partition: inside a phase, one `OverviewLink` per + ordered pair of phases, and any with a missing end. `phaseOverviewLayout(input, + w, h)` turns that into blocks, arrows (links to the next phase) and brackets + (all other links, an adjacent backward one included, packed into as few + slots as do not overlap). There are two title columns when a block is 600 px + wide or more, and one below 620 px of canvas. Blocks are trimmed from the one + with the most rows until the whole fits, never below `MIN_SLOTS` (two titles + and "… N more steps"); below that the overview scrolls. Per block + `listed + more` is its step count. +- `src/ui/overview.ts` (`PhaseOverview`) draws it as a `section.mlv-overview` + inside `.mlv-canvas`, beside (never inside) `.mlv-world`, so the routed + picture does not move. Blocks are `role="button"` with a roving tab stop, + named by `blockName()` and described by their list of titles. ↑ ↓ ← →, Home + and End move, Enter or Space go, and `)` or Shift+0 go back. The overview + stops only those keys. Escape goes on to the canvas's cascade, which closes + a legend (or phase list) opened over the overview before it leaves the + overview. Any other key reaches the canvas, where + `closingOverview` (`src/app/keys.ts`) closes it and then acts, except the + `KEEP_OPEN` commands (the shortcut sheet, search, legend, rail). The ◌ mark is + a drawn `.mlv-ovmark` ring, because macOS system fonts show nothing for the + character. The header's key is `OVERVIEW_KEY` (one line at 900 px) or, below + 620 px, the shorter `OVERVIEW_KEY_NARROW`; both say what a bracket's number + counts. A row's F tags are `aria-hidden` (the screen-reader text names them + once) and each row ends with a screen-reader separator. +- `src/render/phaseindex.ts` (`PhaseIndex`) replaced the minimap: a + `nav.mlv-phaseindex` inside the canvas, as a list of row buttons when the + panel is 1000 px wide or more and the canvas 350 px tall or more + (`PHASE_INDEX_LIST_MIN_W` / `_H` in `src/canvas/host.ts`) and the reader + has not folded it; otherwise as a pill, "k/N label", that opens the rows as + a popover (an Escape rung before the legend). The panel grows from + `INDEX_W` (288 px) to fit its longest name, up to `INDEX_MAX_W` (360 px); + jsdom's `coveredRect` takes the widest. A row's tooltip is the action; the + full name is on its label. `phasesInView` marks the lanes + on the canvas and picks the current one by visible share of the canvas plus + visible share of the lane. `ViewState.phaseIndex` is `'folded'` or + `'hidden'` when not the default; a state saved with `minimapCollapsed: true` + opens folded and is not written back. +- `ViewportController` (`src/render/canvas.ts`): `setCovered(fn)` takes the + index's `coveredRect` (measured, or its stylesheet sizes in jsdom). + `isVisible` counts a target under it as out of view, and `centerOn`, + `revealRect` and `frameRect` (for its anchor) pan the least distance off + it (`clearOf`). `animateTo(target, VIEW_ANIMATION_MS)` (240 ms, instant + under `motionMode() === 'reduced'`) moves the canvas-centre point in a + straight line with the zoom on a log scale; any other move cancels it. + `phasePlan(frame, k, w, h)` is the view of phase k at reading size, the + readable plan's rule for phase 1. +- `test/phase-overview.test.mjs` covers the geometry at the three sizes on the + vit-cc and yolov5-cc2 shapes (counts, partition, blocks inside the canvas, + columns, scrolling), link classification and slot sharing, reading order + and F labels, the keys, Escape giving the focus back, the animation and + reduced motion, names, the routed geometry unchanged by the overlay, the + index's rows, pill and position, and the focused card never under the + index (arrows, Outline, search, host reveal, findings, the walk) at + 1440x900, 900x800 and 541x798. + +Viewer M3 Reveal in Diagram (roadmap step 14; no contract change, no new +setting; the golden is byte-identical). The host side lives in +`vscode-extension/` (`src/revealCommand.ts`, `src/citationIndex.ts`); the +viewer's part is one inbound frame: + +- `reveal {v: 1, kind: 'node' | 'edge' | 'issue', id}` (`src/types.ts`). + `src/protocol.ts` checks that `id` is a non-empty string and dispatches to + `revealNode(id, true)`, `revealEdge(id)` or `revealIssue(id)`. The older + `revealNode {nodeId}` and `revealIssue {issueId}` frames now check their id + the same way and take the same path. A frame with a missing or empty id is + dropped. +- `App.revealClaim(sel, {center})` (`src/app.ts`): an id the displayed + revision lacks gets a toast and the announcement "That claim is not in the + revision shown here." Otherwise it closes the shortcut sheet, the Refine + popover (keeping its text) and the phase overview; expands the claim's + ancestors (both ends of a connection; the cited steps and connection ends + of a finding); opens a hidden docked rail or a collapsed sheet as the walk + does (`openRailForWalk`); selects with `showClaim` on the Selection tab; and + frames the claim: a step centred with one pulse, a finding through + `frameIssue`, a connection through `CanvasView.frameEdge`. A running walk + follows the selection (`followSelection`) without opening anything. +- `CanvasView.frameEdge(id)` frames the union of the connection's route and + its two ends at reading size with the source as anchor, so the anchor is + panned clear of the phase index. If the target end is then out of view, it + zooms out to the larger of the fits above and beside the index, when that + zoom is at least `FRAME_MIN_ZOOM` (0.45); otherwise the reading-size frame + stays. +- Focus: `CanvasView.focusTarget(target)` focuses the step's card (a group's + header), the connection's `.mlv-edge__hit`, or the canvas for a finding or a + bundled connection with no element of its own. The host moves the panel's + focus right after posting the frame, and VS Code 1.139 hands it over in two + steps that leave the page's `activeElement` on `` (measured live), so + `holdRevealFocus` refocuses the claim on a window `focus` that finds nothing + focused, for `REVEAL_FOCUS_HOLD_MS` (1500 ms). A focus elsewhere is left + alone. +- The host posts the frame again after the page's next `ready` (within + `REVEAL_REPLAY_MS`, 5 s, while the revision is still shown), because VS Code + discards a hidden panel's page and the panel does not retain its context. +- The shortcut sheet's note ends with the command + (`src/ui/shortcuts.ts`). +- `test/reveal.test.mjs` covers the three kinds and the focus each leaves, + the focus hold, folded groups, connections and findings, the older frames, + malformed frames and unknown ids, the overlays closing, the walk following, + a hidden rail opening, and every step card and every connection (all 41 and + 113 since the M3 review, not the first 40) on the canvas, above the sheet + and clear of the index at 1440x900, 900x800 and 541x798 on the vit-cc and + yolov5-cc2 shapes; a group, whose box can be larger than the canvas, is + checked as selected only. `authoredRevealHandshake` in + `test/authored-handshake.mjs` drives the real host with the built viewer. + Viewer M1 cleanup (no contract change): - The bundle contains only the authored path. Inbound, the viewer handles `init`, `theme`, `workflow`, `workflowError`, `stale` and `actionResult`, - plus `revealNode` and `revealIssue` (kept for the planned "Reveal in - Diagram") and `restoreState` (the tests drive collapse with it); any other + plus `reveal`, `revealNode` and `revealIssue` (Reveal in Diagram, since + viewer M3) and `restoreState` (the tests drive collapse with it); any other type is answered with a `log` frame and ignored. Outbound it posts `openLocation`, `workspaceHint`, `refineWorkflow`, `copy`, `exportFile` and - `log`; the host bootstrap posts `ready`. + `log`, and since viewer M3 `walk` (sent by the review walk); the host + bootstrap posts `ready`. - A saved `ViewState` with keys the viewer no longer writes (for example `showSuppressed` or `railGroupBy`) still loads; those keys are ignored. - `MLGraph` carries only what an authored document fills in. Fields of the retired analyzer graph (ghost, confidence, suppression, ports, diff and rollup data) and the `concern:` scope presets are gone. -- The stylesheet is 13 files concatenated in the order `build.mjs` lists - (viewer M2 removed `scope.css`). +- The stylesheet is 14 files concatenated in the order `build.mjs` lists + (viewer M2 removed `scope.css`; viewer M3 added `overview.css` after + `rail.css`). ## Screenshots (opt-in) @@ -373,7 +599,10 @@ node tools/screenshots/capture.mjs --viewer /path/to/main-worktree --out /tmp/sh diagram**, from the ⋯ menu), `filter` (the lowest severity with findings turned off), `search` (through the search icon or the ⋯ menu when the field is folded), `finding` (a finding with its suggestion), - `finding-pane` (that finding, then the Selection tab), `stale`, `stale-selected` and `narrow-selected` (900x800). Pick some with + `finding-pane` (that finding, then the Selection tab), `stale`, `stale-selected`, `narrow-selected` (900x800), + and since viewer M3 `overview` (Shift+0), `overview-go` (Shift+0, Home, ↓ ↓, + Enter: the move to phase 3) and `phase-list` (the phase index's list, opened + from its pill below 1000 px). Pick some with `--states`. `index.json` records, per shot, how many connections are lit and moving, whether the flow has settled, the canvas box, the header and status bar heights (`bars`), the chrome above and below the canvas diff --git a/webview/build.mjs b/webview/build.mjs index 4073567c..3b6f754a 100644 --- a/webview/build.mjs +++ b/webview/build.mjs @@ -34,6 +34,8 @@ const CSS_FILES = [ 'edge.css', 'flow.css', 'rail.css', + // Viewer M3: the phase index and the phase overview, overlays inside the canvas. + 'overview.css', // PERF-04. After `edge.css`, because `.mlv-edge--weighted .mlv-edge__path` // has the same specificity as the per-kind stroke rules and has to win on // source order. diff --git a/webview/dist/mlview.css b/webview/dist/mlview.css index 18b4e882..b92b4154 100644 --- a/webview/dist/mlview.css +++ b/webview/dist/mlview.css @@ -1 +1 @@ -:root,.mlv-root[data-theme=light]{--mlv-font: var(--vscode-font-family, ui-sans-serif, -apple-system, "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif);--mlv-font-mono: var(--vscode-editor-font-family, "Cascadia Code", "JetBrains Mono", Consolas, ui-monospace, monospace);--mlv-fs-10: 10.5px;--mlv-fs-11: 11px;--mlv-fs-12: 12px;--mlv-fs-13: 13px;--mlv-fs-14: 14px;--mlv-fs-16: 16px;--mlv-fs-20: 20px;--mlv-compact-title: 11.2px;--mlv-lh-tight: 1.35;--mlv-lh-body: 1.5;--mlv-fw-regular: 450;--mlv-fw-medium: 550;--mlv-fw-semi: 650;--mlv-track-caps: .04em;--mlv-s1: 2px;--mlv-s2: 4px;--mlv-s3: 6px;--mlv-s4: 8px;--mlv-s5: 12px;--mlv-s6: 16px;--mlv-s7: 20px;--mlv-s8: 24px;--mlv-s9: 32px;--mlv-s10: 48px;--mlv-r-chip: 4px;--mlv-r-btn: 6px;--mlv-r-node: 10px;--mlv-r-lane: 14px;--mlv-r-panel: 12px;--mlv-r-pill: 999px;--mlv-sh-1: 0 1px 2px rgba(16, 18, 24, .06), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 4px 12px rgba(16, 18, 24, .1), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 16px 40px rgba(16, 18, 24, .18), 0 0 0 1px var(--mlv-border-strong);--mlv-dur-1: .12s;--mlv-dur-2: .18s;--mlv-dur-3: .26s;--mlv-ease: cubic-bezier(.2, .8, .2, 1);--mlv-flow-speed: 220;--mlv-flow-pulse-speed: 320;--mlv-flow-head: 12px;--mlv-flow-gap: 34px;--mlv-flow-gap-call: 96px;--mlv-flow-gap-back: 24px;--mlv-flow-dur-data: .21s;--mlv-flow-dur-call: .49s;--mlv-flow-dur-back: 165ms;--mlv-flow-dur-min: .38s;--mlv-flow-dur-max: 2.2s;--mlv-flow-hop: 90ms;--mlv-flow-width: 3px;--mlv-flow-port-r: 3.5px;--mlv-flow-color: var(--mlv-accent);--mlv-flow-halo-opacity: .18;--mlv-flow-glow-opacity: .42;--mlv-flow-core-width: 1.6;--mlv-flow-underlay-opacity: .28;--mlv-node-w: 216px;--mlv-node-w-lg: 248px;--mlv-node-w-sm: 176px;--mlv-node-h: 72px;--mlv-rail-w: 4px;--mlv-group-header-h: 34px;--mlv-lane-pad: 20px;--mlv-header-h: 36px;--mlv-status-h: 22px;--mlv-rail-width: 360px;--mlv-bg: var(--vscode-editor-background, #FBFBFD);--mlv-surface: var(--vscode-editorWidget-background, #FFFFFF);--mlv-surface-2: var(--vscode-list-hoverBackground, #F3F4F8);--mlv-border: var(--vscode-widget-border, #E3E5EB);--mlv-border-strong: var(--vscode-widget-border, #C9CDD6);--mlv-text: var(--vscode-editor-foreground, #16181D);--mlv-text-2: var(--vscode-descriptionForeground, #5A6070);--mlv-text-3: var(--vscode-descriptionForeground, #676E81);--mlv-link: var(--vscode-textLink-foreground, #2B57C4);--mlv-accent: var(--vscode-focusBorder, #3B6CF6);--mlv-accent-solid: var(--vscode-button-background, #2B57C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #3B6CF6);--mlv-canvas-dot: var(--vscode-widget-border, #E6E9F0);--mlv-edge: color-mix(in srgb, var(--mlv-text) 64%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 58%, var(--mlv-bg));--mlv-scrim: rgba(16, 18, 24, .36);--mlv-phase-0: #00796B;--mlv-phase-1: #6A4FB3;--mlv-phase-2: #0A693C;--mlv-phase-3: #A0388F;--mlv-phase-4: #558B2F;--mlv-phase-5: #0B7F99;--mlv-phase-6: #546E7A;--mlv-phase-7: #1F6FB2;--mlv-stage-unknown: #7A8090;--mlv-lane-tint: .025;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #D0342C));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #E8A317));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #2F5FD0));--mlv-sev-high-ink: var(--vscode-editor-background, #FFFFFF);--mlv-sev-medium-ink: #3A2500;--mlv-sev-low-ink: var(--vscode-editor-background, #FFFFFF)}@media(prefers-color-scheme:dark){:root:not([data-theme=light]):not([data-theme=hc]),.mlv-root:not([data-theme=light]):not([data-theme=hc]){--mlv-bg: var(--vscode-editor-background, #131417);--mlv-surface: var(--vscode-editorWidget-background, #1A1C21);--mlv-surface-2: var(--vscode-list-hoverBackground, #22252C);--mlv-border: var(--vscode-widget-border, #2C3038);--mlv-border-strong: var(--vscode-widget-border, #3B414C);--mlv-text: var(--vscode-editor-foreground, #E6E8EE);--mlv-text-2: var(--vscode-descriptionForeground, #9AA1B1);--mlv-text-3: var(--vscode-descriptionForeground, #969DAD);--mlv-link: var(--vscode-textLink-foreground, #8FB0FF);--mlv-accent: var(--vscode-focusBorder, #6E96FF);--mlv-accent-solid: var(--vscode-button-background, #3355C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #6E96FF);--mlv-canvas-dot: var(--vscode-widget-border, #23262D);--mlv-edge: color-mix(in srgb, var(--mlv-text) 54%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 46%, var(--mlv-bg));--mlv-scrim: rgba(0, 0, 0, .5);--mlv-sh-1: 0 1px 2px rgba(0, 0, 0, .36), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 6px 16px rgba(0, 0, 0, .44), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 20px 48px rgba(0, 0, 0, .55), 0 0 0 1px var(--mlv-border-strong);--mlv-phase-0: #4DB6AC;--mlv-phase-1: #A48BE0;--mlv-phase-2: #4CA871;--mlv-phase-3: #D684C5;--mlv-phase-4: #9CCC65;--mlv-phase-5: #4FC3D9;--mlv-phase-6: #90A4AE;--mlv-phase-7: #6FA8DC;--mlv-stage-unknown: #8B93A7;--mlv-lane-tint: .035;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #FF6169));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #F2B03C));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #6E96FF));--mlv-sev-high-ink: #1A0405;--mlv-sev-medium-ink: #2A1A00;--mlv-sev-low-ink: #0B1020}}body.vscode-dark,:root[data-theme=dark],.mlv-root[data-theme=dark]{--mlv-bg: var(--vscode-editor-background, #131417);--mlv-surface: var(--vscode-editorWidget-background, #1A1C21);--mlv-surface-2: var(--vscode-list-hoverBackground, #22252C);--mlv-border: var(--vscode-widget-border, #2C3038);--mlv-border-strong: var(--vscode-widget-border, #3B414C);--mlv-text: var(--vscode-editor-foreground, #E6E8EE);--mlv-text-2: var(--vscode-descriptionForeground, #9AA1B1);--mlv-text-3: var(--vscode-descriptionForeground, #969DAD);--mlv-link: var(--vscode-textLink-foreground, #8FB0FF);--mlv-accent: var(--vscode-focusBorder, #6E96FF);--mlv-accent-solid: var(--vscode-button-background, #3355C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #6E96FF);--mlv-canvas-dot: var(--vscode-widget-border, #23262D);--mlv-edge: color-mix(in srgb, var(--mlv-text) 54%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 46%, var(--mlv-bg));--mlv-scrim: rgba(0, 0, 0, .5);--mlv-sh-1: 0 1px 2px rgba(0, 0, 0, .36), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 6px 16px rgba(0, 0, 0, .44), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 20px 48px rgba(0, 0, 0, .55), 0 0 0 1px var(--mlv-border-strong);--mlv-phase-0: #4DB6AC;--mlv-phase-1: #A48BE0;--mlv-phase-2: #4CA871;--mlv-phase-3: #D684C5;--mlv-phase-4: #9CCC65;--mlv-phase-5: #4FC3D9;--mlv-phase-6: #90A4AE;--mlv-phase-7: #6FA8DC;--mlv-stage-unknown: #8B93A7;--mlv-lane-tint: .035;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #FF6169));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #F2B03C));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #6E96FF));--mlv-sev-high-ink: #1A0405;--mlv-sev-medium-ink: #2A1A00;--mlv-sev-low-ink: #0B1020}body.vscode-high-contrast,body.vscode-high-contrast-light,:root[data-theme=hc],.mlv-root[data-theme=hc]{--mlv-surface: var(--vscode-editor-background, #000000);--mlv-surface-2: var(--vscode-editor-background, #000000);--mlv-bg: var(--vscode-editor-background, #000000);--mlv-text: var(--vscode-editor-foreground, #FFFFFF);--mlv-text-2: var(--vscode-editor-foreground, #FFFFFF);--mlv-text-3: var(--vscode-editor-foreground, #FFFFFF);--mlv-link: var(--vscode-textLink-foreground, #6BB7FF);--mlv-border: var(--vscode-contrastBorder, #FFFFFF);--mlv-border-strong: var(--vscode-contrastBorder, #FFFFFF);--mlv-edge: var(--vscode-contrastBorder, #FFFFFF);--mlv-node-edge: var(--vscode-contrastBorder, #FFFFFF);--mlv-canvas-dot: transparent;--mlv-lane-tint: 0;--mlv-group-tint: 0;--mlv-phase-0: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-1: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-2: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-3: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-4: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-5: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-6: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-7: var(--vscode-contrastBorder, #FFFFFF);--mlv-sh-1: 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 0 0 2px var(--mlv-border);--mlv-sh-3: 0 0 0 2px var(--mlv-border);--mlv-flow-color: var(--mlv-text);--mlv-flow-width: 4px;--mlv-flow-core-width: 2.2;--mlv-flow-underlay-opacity: .45}.mlv-root{--mlv-stale-ink: color-mix(in srgb, var(--mlv-sev-medium) 70%, var(--mlv-text))}html.mlv-fills-page,html.mlv-fills-page>body{height:100%;margin:0;padding:0;overflow:hidden}html.mlv-fills-page>body>.mlv-root{height:100%}.mlv-root{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:240px;overflow:hidden;font-family:var(--mlv-font);font-size:var(--mlv-fs-13);line-height:var(--mlv-lh-body);color:var(--mlv-text);background:var(--mlv-bg);-webkit-font-smoothing:antialiased}.mlv-root *,.mlv-root *:before,.mlv-root *:after{box-sizing:border-box}.mlv-root button,.mlv-root input,.mlv-root select{font:inherit;color:inherit}.mlv-body{position:relative;display:flex;flex:1 1 auto;min-height:0}.mlv-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.mlv-root :focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px;border-radius:var(--mlv-r-btn)}.mlv-btn{display:inline-flex;align-items:center;gap:var(--mlv-s3);min-height:24px;padding:3px var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);cursor:pointer;transition:background var(--mlv-dur-1) var(--mlv-ease),border-color var(--mlv-dur-1) var(--mlv-ease)}.mlv-btn:hover{background:var(--mlv-surface-2);border-color:var(--mlv-border-strong)}.mlv-btn--primary{background:var(--mlv-accent-solid);border-color:var(--mlv-accent-solid);color:var(--mlv-on-accent)}.mlv-root .mlv-btn--primary{color:var(--mlv-on-accent)}.mlv-btn--primary:hover{background:var(--mlv-accent-solid);filter:brightness(1.08)}body.vscode-high-contrast .mlv-root .mlv-btn--primary,body.vscode-high-contrast-light .mlv-root .mlv-btn--primary,:root[data-theme=hc] .mlv-root .mlv-btn--primary,.mlv-root[data-theme=hc] .mlv-btn--primary{border-color:var(--vscode-button-border, var(--vscode-contrastBorder, var(--mlv-text)))}.mlv-btn--icon{min-width:26px;justify-content:center;padding:3px var(--mlv-s3)}.mlv-btn[aria-pressed=true]{background:var(--mlv-surface-2);border-color:var(--mlv-border-strong)}.mlv-btn[hidden]{display:none}.mlv-chip{display:inline-flex;align-items:center;gap:var(--mlv-s2);min-height:20px;padding:1px var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-medium);white-space:nowrap}.mlv-chip[hidden]{display:none}.mlv-chip--btn{cursor:pointer;min-height:24px;padding:2px var(--mlv-s4)}.mlv-chip--btn[aria-pressed=false]{border-style:dashed;border-color:var(--mlv-text-3);text-decoration:line-through}.mlv-mono{font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11)}.mlv-link{color:var(--mlv-link);background:none;border:0;padding:0;font:inherit;text-align:left;cursor:pointer;text-decoration:underline;text-underline-offset:2px}.mlv-link--inline{padding:0;border:0;background:none;color:var(--mlv-link);font:inherit;cursor:pointer;text-decoration:underline}.mlv-input{min-width:0;padding:3px var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12)}.mlv-input::placeholder{color:var(--mlv-text-3)}.mlv-uicon,.mlv-icon{flex:none;display:block}@media(prefers-reduced-motion:reduce){.mlv-root *,.mlv-root *:before,.mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}body.vscode-reduce-motion .mlv-root *,body.vscode-reduce-motion .mlv-root *:before,body.vscode-reduce-motion .mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}body.vscode-using-screen-reader .mlv-root *,body.vscode-using-screen-reader .mlv-root *:before,body.vscode-using-screen-reader .mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}.mlv-skiplink{position:absolute;left:-9999px;top:0;z-index:100;padding:var(--mlv-s3) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);text-decoration:none}.mlv-skiplink:focus,.mlv-skiplink:focus-visible{left:var(--mlv-s4);top:var(--mlv-s4)}.mlv-main{position:relative;display:flex;flex:1 1 auto;flex-direction:column;min-width:0;min-height:0}.mlv-main>.mlv-canvas{min-height:0}.mlv-loc{display:inline-flex;align-items:baseline;min-width:0;max-width:100%}.mlv-loc__file{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.mlv-loc__at{flex:0 0 auto;white-space:pre}.mlv-header{position:relative;display:flex;align-items:center;gap:var(--mlv-s4);flex:none;min-width:0;height:var(--mlv-header-h);padding:0 var(--mlv-s4) 0 var(--mlv-s5);border-bottom:1px solid var(--mlv-border);background:var(--mlv-surface);white-space:nowrap}.mlv-header__title{flex:0 1 auto;min-width:48px;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);line-height:1.3}.mlv-header__bar{display:flex;align-items:center;gap:var(--mlv-s3);flex:1 0 auto;min-width:0}.mlv-header__spacer{flex:1 1 0;min-width:var(--mlv-s2)}.mlv-header__prov{flex:0 1 auto;min-width:0;max-width:38ch;height:22px;gap:5px;overflow:hidden;cursor:pointer;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-regular);background:transparent}.mlv-header__prov:hover{color:var(--mlv-text);border-color:var(--mlv-border-strong)}.mlv-header__prov[aria-expanded=true]{color:var(--mlv-text);border-color:var(--mlv-border-strong);background:var(--mlv-surface-2)}.mlv-header__rev{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-header__host{flex:none}.mlv-header__dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--mlv-text-3);opacity:.6}.mlv-header__prov[data-stale=true] .mlv-header__dot{background:var(--mlv-stale-ink);opacity:1}.mlv-header .mlv-chip--btn{height:24px;gap:var(--mlv-s2);padding:0 var(--mlv-s3);color:var(--mlv-text);font-size:var(--mlv-fs-12);font-variant-numeric:tabular-nums;background:transparent}.mlv-header .mlv-chip--btn:hover{background:var(--mlv-surface-2)}.mlv-chip--exceptions{white-space:nowrap}.mlv-chip--exceptions[hidden]{display:none}.mlv-chip--exceptions .mlv-uicon{color:var(--mlv-text-2)}.mlv-chip--exceptions[aria-pressed=false]{border-style:solid;border-color:var(--mlv-border);text-decoration:none}.mlv-chip--exceptions[aria-pressed=true]{border-color:var(--mlv-text-2);color:var(--mlv-text);background:var(--mlv-surface-2)}.mlv-chip--exceptions .mlv-chip__detail{color:var(--mlv-text-2)}.mlv-header__unit{flex:none;margin-left:calc(-1 * var(--mlv-s2));color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-header[data-fit="3"] .mlv-header__title,.mlv-header[data-fit="4"] .mlv-header__title{position:absolute;width:1px;min-width:0;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.mlv-header .mlv-btn--icon{width:26px;height:26px;min-height:0;padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-header .mlv-btn--icon:hover,.mlv-header .mlv-btn--icon[aria-expanded=true]{border-color:transparent;background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-header__refine{display:inline-flex;flex:none}.mlv-header .mlv-workflow__refine{height:24px;min-height:0;padding:0 var(--mlv-s4);font-weight:var(--mlv-fw-medium)}.mlv-header__searchbtn{display:none}.mlv-search{display:flex;align-items:center;flex:0 1 260px;min-width:150px;height:24px}.mlv-header[data-layout=mid] .mlv-header__host,.mlv-header[data-layout=narrow] .mlv-header__host,.mlv-header[data-layout=mid][data-search=closed] .mlv-search{display:none}.mlv-header[data-layout=mid][data-search=closed] .mlv-header__searchbtn{display:inline-flex}.mlv-header[data-layout=narrow] .mlv-header__prov,.mlv-header[data-layout=narrow][data-search=closed] .mlv-search,.mlv-header[data-layout=narrow][data-search=open] .mlv-header__title,.mlv-header[data-layout=narrow][data-search=open] .mlv-header__spacer{display:none}.mlv-header[data-layout=narrow] .mlv-search{flex:1 1 auto;min-width:96px}.mlv-header[data-fit="3"],.mlv-header[data-fit="4"]{gap:var(--mlv-s2);padding:0 var(--mlv-s3)}.mlv-header[data-fit="3"] .mlv-header__bar,.mlv-header[data-fit="4"] .mlv-header__bar{gap:var(--mlv-s2)}.mlv-search__glyph{position:relative;left:22px;margin-left:-16px;color:var(--mlv-text-3);pointer-events:none}.mlv-search .mlv-input{flex:1 1 auto;height:24px;padding-left:26px}.mlv-search__results{position:absolute;top:calc(100% + 2px);right:var(--mlv-s4);z-index:50;width:min(460px,calc(100% - 2 * var(--mlv-s4)));max-height:min(380px,60vh);overflow-y:auto;padding:var(--mlv-s2);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3);list-style:none;margin:0;white-space:normal}.mlv-search__results[hidden]{display:none}.mlv-result__count{padding:var(--mlv-s2) var(--mlv-s4) var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-result{display:grid;grid-template-columns:14px minmax(0,1fr);column-gap:var(--mlv-s3);row-gap:1px;align-items:start;width:100%;padding:var(--mlv-s3) var(--mlv-s4);border:0;border-radius:var(--mlv-r-btn);background:none;cursor:pointer;text-align:left}.mlv-result>.mlv-sev-glyph,.mlv-result>svg{grid-column:1;margin-top:2px}.mlv-result:hover,.mlv-result[aria-selected=true]{background:var(--mlv-surface-2)}.mlv-result__label{grid-column:2;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;overflow-wrap:anywhere;font-size:var(--mlv-fs-12);line-height:1.35}.mlv-result__meta{grid-column:2;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-result__jump{grid-column:2;justify-self:start}.mlv-result__empty{padding:var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-moremenu{position:fixed;z-index:60;display:flex;flex-direction:column;min-width:240px;max-width:calc(100vw - 16px);max-height:calc(100vh - 48px);overflow-y:auto;padding:var(--mlv-s2);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--vscode-menu-background, var(--mlv-surface));box-shadow:var(--mlv-sh-3);color:var(--vscode-menu-foreground, var(--mlv-text))}.mlv-moremenu[hidden],.mlv-moremenu__item[hidden],.mlv-moremenu__sep[hidden]{display:none}.mlv-moremenu__sep{height:1px;margin:var(--mlv-s2) var(--mlv-s3);background:var(--vscode-menu-separatorBackground, var(--mlv-border))}.mlv-moremenu__item{display:flex;align-items:center;gap:var(--mlv-s4);width:100%;min-height:26px;padding:2px var(--mlv-s4);border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer;font-size:var(--mlv-fs-12);text-align:left}.mlv-moremenu__item .mlv-uicon{color:var(--mlv-text-2)}.mlv-moremenu__item:hover,.mlv-moremenu__item:focus-visible{background:var(--vscode-menu-selectionBackground, var(--mlv-surface-2));color:var(--vscode-menu-selectionForeground, var(--mlv-text));outline-offset:-2px}.mlv-moremenu__item[disabled]{cursor:default;opacity:.45}.mlv-moremenu__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-moremenu__item[role=menuitemcheckbox]:after{content:"";flex:none;width:14px;text-align:center}.mlv-moremenu__item[aria-checked=true]:after{content:"\2713"}.mlv-moremenu__keys{flex:none;color:var(--mlv-text-2);font-family:inherit;font-size:var(--mlv-fs-11)}.mlv-moremenu__item[data-more-item=minimap]{flex-wrap:wrap;row-gap:0}.mlv-moremenu__item[data-more-item=minimap]:after{order:2}.mlv-moremenu__item[data-more-item=minimap][disabled]{opacity:1}.mlv-moremenu__item[data-more-item=minimap][disabled] .mlv-moremenu__label,.mlv-moremenu__note{color:var(--mlv-text-2)}.mlv-moremenu__note{order:3;flex:1 0 100%;padding-left:calc(14px + var(--mlv-s4));font-size:var(--mlv-fs-11);white-space:normal}.mlv-moremenu__note[hidden]{display:none}.mlv-banner__detail{margin-top:var(--mlv-s2);color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);white-space:pre-wrap;max-height:120px;overflow:auto}.mlv-status{display:flex;align-items:center;gap:var(--mlv-s5);flex:none;min-width:0;height:var(--mlv-status-h);padding:0 var(--mlv-s3) 0 var(--mlv-s5);border-top:1px solid var(--mlv-border);background:var(--mlv-surface);color:var(--mlv-text-2);font-size:var(--mlv-fs-11);white-space:nowrap;overflow:hidden}.mlv-status__counts{flex:0 0 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-status__coverage{flex:0 1 auto;min-width:0;height:18px;padding:0 var(--mlv-s2);overflow:hidden;text-overflow:ellipsis;border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer;font:inherit}.mlv-status__coverage[hidden]{display:none}.mlv-status__coverage:hover,.mlv-status__coverage[aria-expanded=true]{background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-status__spacer{flex:1 1 0}.mlv-status__freshness{display:inline-flex;align-items:center;gap:var(--mlv-s4);flex:0 100 auto;min-width:0;overflow:hidden}.mlv-status__fresh{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-status__fresh.is-warn,.mlv-status__freshness[data-stale=true]{flex:none}.mlv-status[data-layout=narrow] .mlv-status__coverage{flex-shrink:1000}.mlv-status[data-layout=narrow] .mlv-status__counts{flex-shrink:1}.mlv-status[data-layout=narrow]{gap:var(--mlv-s4)}.mlv-status[data-layout=narrow] .mlv-zoom__btn{display:none}.mlv-minimap{position:absolute;right:var(--mlv-s5);bottom:var(--mlv-s5);z-index:20;width:200px;border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);overflow:hidden}.mlv-minimap[hidden]{display:none}.mlv-minimap.is-collapsed{width:28px;height:28px}.mlv-minimap.is-collapsed .mlv-minimap__svg{display:none}.mlv-minimap__toggle{position:absolute;top:2px;right:2px;z-index:1;width:24px;height:24px;min-height:0;padding:0;border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text-2)}.mlv-minimap__toggle .mlv-uicon{transform:rotate(-90deg)}.mlv-minimap.is-collapsed .mlv-minimap__toggle .mlv-uicon{transform:rotate(90deg)}.mlv-minimap__svg{display:block;width:100%;height:130px;cursor:pointer}.mlv-minimap__node{fill:var(--mlv-stage, var(--mlv-stage-unknown));opacity:.75}.mlv-minimap__node--issue{stroke:var(--mlv-sev, var(--mlv-sev-low));stroke-width:1.5}.mlv-minimap__view{fill:var(--mlv-accent);fill-opacity:.12;stroke:var(--mlv-accent);stroke-width:1}.mlv-toasts{position:absolute;left:50%;bottom:var(--mlv-s7);z-index:60;display:flex;flex-direction:column;gap:var(--mlv-s3);transform:translate(-50%);pointer-events:none}.mlv-toast{min-width:240px;max-width:360px;padding:var(--mlv-s4) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);font-size:var(--mlv-fs-12);text-align:center}.mlv-state{position:absolute;inset:0;z-index:30;display:flex;align-items:center;justify-content:center;padding:var(--mlv-s9);background:var(--mlv-bg)}.mlv-state[hidden]{display:none}.mlv-state__inner{width:min(460px,100%);text-align:center}.mlv-state__title{margin:0 0 var(--mlv-s4);font-size:var(--mlv-fs-16);font-weight:var(--mlv-fw-semi)}.mlv-state__body{margin:0 0 var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-state__list{margin:0 auto var(--mlv-s5);padding:0;list-style:none;text-align:left;max-width:420px;color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-state__actions{display:flex;gap:var(--mlv-s4);justify-content:center}.mlv-btn[disabled]{opacity:.4;cursor:default}.mlv-sheet{position:absolute;inset:0;z-index:60;display:flex;align-items:center;justify-content:center}.mlv-sheet[hidden]{display:none}.mlv-sheet__scrim{position:absolute;inset:0;background:var(--mlv-scrim)}.mlv-sheet__panel{position:relative;width:min(560px,92%);max-height:min(70vh,620px);overflow-y:auto;padding:var(--mlv-s5) var(--mlv-s6) var(--mlv-s6);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3)}.mlv-sheet__head{display:flex;align-items:center;gap:var(--mlv-s4);margin-bottom:var(--mlv-s5)}.mlv-sheet__title{flex:1 1 auto;margin:0;font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi)}.mlv-sheet__list{display:grid;grid-template-columns:minmax(140px,auto) 1fr;gap:var(--mlv-s3) var(--mlv-s5);margin:0}.mlv-sheet__keys{display:flex;flex-wrap:wrap;gap:var(--mlv-s2);margin:0}.mlv-sheet__desc{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);align-self:center}.mlv-kbd{display:inline-block;padding:1px var(--mlv-s3);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);white-space:nowrap}.mlv-result__foot{display:flex;align-items:center;justify-content:space-between;gap:var(--mlv-s4);padding:var(--mlv-s3) var(--mlv-s4);border-top:1px solid var(--mlv-border);color:var(--mlv-text-3);font-size:var(--mlv-fs-10)}.mlv-result__jump{flex:none;padding:1px var(--mlv-s3);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-accent);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-medium)}.mlv-legend{position:absolute;right:var(--mlv-s5);bottom:var(--mlv-s5);z-index:45;display:flex;flex-direction:column;width:min(320px,calc(100% - 2 * var(--mlv-s5)));max-height:min(560px,calc(100% - 2 * var(--mlv-s5)));border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-shadow-panel, 0 8px 24px rgba(0, 0, 0, .18));overflow:hidden}.mlv-legend[hidden]{display:none}.mlv-legend__head{display:flex;align-items:center;justify-content:space-between;padding:var(--mlv-s4) var(--mlv-s5);border-bottom:1px solid var(--mlv-border)}.mlv-legend__title{margin:0;font-size:var(--mlv-fs-12);font-weight:var(--mlv-fw-semi)}.mlv-legend__body{overflow-y:auto;padding:var(--mlv-s4) var(--mlv-s5) var(--mlv-s5)}.mlv-legend__section+.mlv-legend__section{margin-top:var(--mlv-s5)}.mlv-legend__heading{margin:0 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);letter-spacing:var(--mlv-track-caps);text-transform:uppercase}.mlv-legend__list{display:grid;grid-template-columns:minmax(0,auto) minmax(0,1fr);gap:var(--mlv-s2) var(--mlv-s4);margin:0}.mlv-legend__term{display:flex;align-items:center;gap:var(--mlv-s3);color:var(--mlv-text);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-medium)}.mlv-legend__desc{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);line-height:var(--mlv-lh-tight)}.mlv-legend__swatch{flex:none;overflow:visible}.mlv-legend__basis{display:inline-flex;flex-direction:column;align-items:flex-start;gap:var(--mlv-s3)}.mlv-legend__desc strong{color:var(--mlv-text);font-weight:var(--mlv-fw-semi)}.mlv-legend__card{position:relative;display:inline-flex;align-items:center;min-width:44px;height:20px;padding:0 3px;box-sizing:border-box;border:1px solid var(--mlv-node-edge, var(--mlv-border));border-radius:4px;background:var(--mlv-surface)}.mlv-legend__card[data-basis=inferred]{border:1.5px dashed var(--mlv-text-2)}.mlv-legend__card[data-basis=unresolved]{border:1.5px dotted var(--mlv-text-2);background-image:repeating-linear-gradient(135deg,transparent 0 4px,color-mix(in srgb,var(--mlv-text) 8%,transparent) 4px 5px)}.mlv-legend__tag{padding:0 3px;border:1px dashed var(--mlv-text-2);border-radius:3px;background:var(--mlv-bg);color:var(--mlv-text);font-size:9px;line-height:11px;white-space:nowrap}.mlv-legend__tag[data-basis=unresolved]{border-style:dotted}.mlv-legend__phases{display:inline-flex;gap:2px}.mlv-legend__phase{width:3px;height:14px;border-radius:1.5px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-legend__short{color:var(--mlv-text);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi)}@media(max-width:900px){.mlv-legend{right:var(--mlv-s3);bottom:var(--mlv-s3);width:calc(100% - 2 * var(--mlv-s3))}}.mlv-legend__body{background-color:var(--mlv-surface);background-image:linear-gradient(to bottom,var(--mlv-surface) 30%,transparent),linear-gradient(to top,var(--mlv-surface) 30%,transparent),linear-gradient(to bottom,var(--mlv-border-strong),transparent),linear-gradient(to top,var(--mlv-border-strong),transparent);background-repeat:no-repeat;background-size:100% 24px,100% 24px,100% 8px,100% 8px;background-position:top,bottom,top,bottom;background-attachment:local,local,scroll,scroll;scrollbar-gutter:stable}.mlv-canvas{position:relative;flex:1 1 auto;min-width:0;overflow:hidden;background-color:var(--mlv-bg);background-image:radial-gradient(var(--mlv-canvas-dot) 1px,transparent 1px);background-size:20px 20px;cursor:grab;touch-action:none}.mlv-canvas[data-lod=compact]{background-image:none}.mlv-canvas.is-panning{cursor:grabbing}.mlv-world{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}.mlv-layer{position:absolute;top:0;left:0}.mlv-layer--edges{overflow:visible;pointer-events:none}.mlv-layer--nodes{pointer-events:none}.mlv-layer--nodes>*{pointer-events:auto}.mlv-lane{position:absolute;border:1px solid var(--mlv-border);border-radius:var(--mlv-r-lane);background:color-mix(in srgb,var(--mlv-text) calc(var(--mlv-lane-tint) * 100%),transparent)}.mlv-lane:before{content:"";position:absolute;top:6px;bottom:6px;left:0;width:3px;border-radius:1.5px;background:var(--mlv-stage, var(--mlv-stage-unknown));pointer-events:none}.mlv-lane__header{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;gap:var(--mlv-s4);height:var(--mlv-lane-header-h, 28px);padding:0 var(--mlv-s5);border-bottom:1px solid var(--mlv-border);border-radius:calc(var(--mlv-r-lane) - 1px) calc(var(--mlv-r-lane) - 1px) 0 0;background:color-mix(in srgb,var(--mlv-text) 5%,var(--mlv-surface))}.mlv-lane__header:before{content:"";flex:none;width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-lane__num{flex:none;margin-right:calc(-1 * var(--mlv-s2));color:var(--mlv-text-2);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-lane__label{flex:0 1 auto;min-width:48px;overflow:hidden;color:var(--mlv-text);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);text-overflow:ellipsis;white-space:nowrap}.mlv-lane__count{flex:none;color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums;white-space:nowrap}.mlv-lane__spacer{flex:1 1 auto}.mlv-lane__header .mlv-cluster{flex:none}.mlv-lane__unit:before{content:"\b7 "}.mlv-lane__unit{flex:0 100 auto;min-width:0;overflow:hidden;margin-left:calc(-1 * var(--mlv-s2));color:var(--mlv-text-3);font-size:var(--mlv-fs-10);text-overflow:ellipsis;white-space:nowrap}.mlv-group{position:absolute;border:1px solid var(--mlv-border-strong);border-radius:12px;background:color-mix(in srgb,var(--mlv-text) calc(var(--mlv-group-tint) * 100%),transparent)}.mlv-group[data-basis=inferred]{border:calc(1.5px / var(--mlv-z, 1)) dashed var(--mlv-text-2)}.mlv-group[data-basis=unresolved]{border:calc(1.5px / var(--mlv-z, 1)) dotted var(--mlv-text-2)}.mlv-group__header{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;height:var(--mlv-group-header-h);padding:0 var(--mlv-s4);border:0;border-radius:12px 12px 0 0;background:none;color:var(--mlv-text);cursor:pointer;text-align:left}.mlv-group__header:hover{background:var(--mlv-surface-2)}.mlv-group__chevron{transition:transform var(--mlv-dur-2) var(--mlv-ease);color:var(--mlv-text-2)}.mlv-group.is-collapsed .mlv-group__chevron{transform:rotate(0)}.mlv-group__chevron{transform:rotate(90deg)}.mlv-group__name{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-12);font-weight:var(--mlv-fw-semi)}.mlv-group__count{flex:none;padding:0 var(--mlv-s3);border-radius:var(--mlv-r-pill);background:var(--mlv-surface-2);color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums}.mlv-group.is-dim,.mlv-lane.is-dim{opacity:.5}.mlv-canvas.is-focusing .mlv-node:not(.is-lit){opacity:.22;filter:saturate(.3);pointer-events:none}.mlv-canvas.is-focusing .mlv-edge:not(.is-lit){opacity:.16}.mlv-canvas.is-tracing .mlv-node.is-lit{box-shadow:0 0 0 max(1.5px,calc(1px / var(--mlv-z, 1))) var(--mlv-text-2)}.mlv-tooltip{position:absolute;z-index:50;max-width:320px;padding:var(--mlv-s4) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3);font-size:var(--mlv-fs-12);pointer-events:none}.mlv-tooltip[hidden]{display:none}.mlv-tooltip__title{font-weight:var(--mlv-fw-semi)}.mlv-tooltip__row{margin-top:var(--mlv-s2);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-tooltip__loc{font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);color:var(--mlv-text-3)}.mlv-zoom{display:inline-flex;align-items:center;flex:none;gap:1px}.mlv-zoom__level{min-width:38px;text-align:center;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-variant-numeric:tabular-nums}.mlv-zoom .mlv-zoom__btn{width:20px;height:18px;min-width:0;min-height:0;padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-zoom .mlv-zoom__btn:hover{background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-layer--lanes,.mlv-lane,.mlv-group{pointer-events:none}.mlv-lane__header,.mlv-group__header{pointer-events:auto}.mlv-statehost{position:absolute;inset:0;z-index:30;pointer-events:none}.mlv-statehost>*{pointer-events:auto}.mlv-group__chevron-btn{display:inline-flex;align-items:center;justify-content:center;flex:none;width:24px;height:24px;margin-left:-4px;padding:0;border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer}.mlv-group__chevron-btn:hover{background:var(--mlv-surface-2)}.mlv-node{position:absolute;display:flex;align-items:stretch;gap:0;overflow:visible;border:1px solid var(--mlv-node-edge, var(--mlv-border));border-radius:var(--mlv-r-node);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1);cursor:pointer;text-align:left;padding:0;transition:box-shadow var(--mlv-dur-1) var(--mlv-ease),transform var(--mlv-dur-1) var(--mlv-ease)}.mlv-node__rail{flex:none;width:var(--mlv-rail-w);border-radius:var(--mlv-r-node) 0 0 var(--mlv-r-node);background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-node__main{display:flex;align-items:flex-start;gap:var(--mlv-s4);flex:1 1 auto;min-width:0;padding:10px var(--mlv-s5) 10px var(--mlv-s4);overflow:hidden}.mlv-node__iconbox{flex:none;display:flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:6px;background:var(--mlv-surface-2);color:var(--mlv-text-2)}.mlv-node__text{flex:1 1 auto;min-width:0}.mlv-node__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);line-height:var(--mlv-lh-tight)}.mlv-node__sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);line-height:var(--mlv-lh-tight)}.mlv-node__sub[data-lines="2"]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow-wrap:anywhere;white-space:normal}.mlv-node__loc{display:flex;overflow:hidden;white-space:nowrap;color:var(--mlv-text-3);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-node__chips{display:flex;gap:var(--mlv-s2);margin-top:var(--mlv-s2);overflow:hidden}.mlv-node__chips .mlv-chip{display:inline-block;flex:0 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;line-height:18px}.mlv-node:hover{box-shadow:var(--mlv-sh-2);border-color:var(--mlv-border-strong);transform:translateY(-1px);z-index:3}.mlv-node.is-selected{box-shadow:var(--mlv-sh-2),0 0 0 2px var(--mlv-accent),0 0 0 6px color-mix(in srgb,var(--mlv-accent) 14%,transparent);z-index:4}.mlv-node.is-selected .mlv-node__title{font-weight:700}.mlv-node.has-issues{border-color:color-mix(in srgb,var(--mlv-sev) 55%,var(--mlv-node-edge, var(--mlv-border)));--mlv-basis-ink: color-mix(in srgb, var(--mlv-sev) 70%, var(--mlv-text))}.mlv-node.is-stale{border-color:var(--mlv-stale-ink);--mlv-basis-ink: var(--mlv-stale-ink)}.mlv-node[data-basis=inferred],.mlv-node[data-basis=unresolved]{border-color:transparent}.mlv-node[data-basis=inferred]:after,.mlv-node[data-basis=unresolved]:after{content:"";position:absolute;inset:-1px;border:calc(1.5px / var(--mlv-z, 1)) dashed var(--mlv-basis-ink, var(--mlv-text-2));border-radius:inherit;pointer-events:none}.mlv-node[data-basis=unresolved]:after{border-style:dotted}.mlv-node[data-basis=unresolved]{background-image:repeating-linear-gradient(135deg,transparent 0 7px,color-mix(in srgb,var(--mlv-text) 8%,transparent) 7px 8px)}.mlv-basis-tag{display:inline-flex;align-items:center;height:14px;padding:0 4px;border:1px dashed var(--mlv-basis-ink, var(--mlv-text-2));border-radius:3px;background:var(--mlv-bg);color:var(--mlv-text);font-size:10px;font-weight:var(--mlv-fw-regular);line-height:1;white-space:nowrap}.mlv-basis-tag[data-basis=unresolved]{border-style:dotted}.mlv-node>.mlv-basis-tag{position:absolute;top:100%;left:10px;z-index:5;transform-origin:0 50%;transform:translateY(-50%) scale(calc(1 / var(--mlv-z, 1)));pointer-events:none}.mlv-group__header .mlv-basis-tag{flex:none}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed]{border-color:color-mix(in srgb,var(--mlv-node-edge, var(--mlv-border)) 35%,var(--mlv-bg));background:var(--mlv-bg);box-shadow:none}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed] .mlv-node__rail{background:color-mix(in srgb,var(--mlv-stage, var(--mlv-stage-unknown)) 30%,var(--mlv-bg))}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed] .mlv-node__iconbox{background:transparent}.mlv-node__stale{position:absolute;top:-7px;left:-7px;z-index:5;display:flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:var(--mlv-r-pill);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1);color:var(--mlv-stale-ink)}.mlv-group__header .mlv-node__stale{position:static;box-shadow:none;flex:none}.mlv-node.is-pulse{animation:mlv-pulse .3s var(--mlv-ease) 2}@keyframes mlv-pulse{0%,to{box-shadow:var(--mlv-sh-2),0 0 0 2px var(--mlv-accent)}50%{box-shadow:var(--mlv-sh-2),0 0 0 7px color-mix(in srgb,var(--mlv-accent) 30%,transparent)}}.mlv-canvas[data-lod=compact] .mlv-node__sub,.mlv-canvas[data-lod=compact] .mlv-node__loc,.mlv-canvas[data-lod=compact] .mlv-node__chips,.mlv-canvas[data-lod=compact] .mlv-node__iconbox{display:none}.mlv-canvas[data-lod=compact] .mlv-node__main{container-type:size;align-items:center;padding:2px 8px}.mlv-canvas[data-lod=compact] .mlv-node__title{font-size:min(calc(var(--mlv-compact-title) / var(--mlv-z, 1)),calc(100cqh / 2.3));line-height:1.15}.mlv-canvas[data-lod=compact] .mlv-node>.mlv-basis-tag{transform-origin:0 0;transform:translateY(-1px) scale(calc(1 / var(--mlv-z, 1)))}.mlv-canvas[data-lod=compact] .mlv-edge-label{display:none}.mlv-glyph{display:block;overflow:visible}.mlv-glyph__shape{fill:var(--mlv-sev-marker, var(--mlv-sev-low))}.mlv-glyph__ink{fill:var(--mlv-sev-marker-ink, #FFFFFF)}.mlv-glyph--low{--mlv-sev-marker: var(--mlv-sev-low);--mlv-sev-marker-ink: var(--mlv-sev-low-ink)}.mlv-glyph--medium{--mlv-sev-marker: var(--mlv-sev-medium);--mlv-sev-marker-ink: var(--mlv-sev-medium-ink)}.mlv-glyph--high{--mlv-sev-marker: var(--mlv-sev-high);--mlv-sev-marker-ink: var(--mlv-sev-high-ink)}body.vscode-high-contrast .mlv-glyph__shape,body.vscode-high-contrast-light .mlv-glyph__shape,:root[data-theme=hc] .mlv-glyph__shape,.mlv-root[data-theme=hc] .mlv-glyph__shape{fill:none;stroke:var(--mlv-sev-marker, currentColor);stroke-width:2}body.vscode-high-contrast .mlv-glyph__ink,body.vscode-high-contrast-light .mlv-glyph__ink,:root[data-theme=hc] .mlv-glyph__ink,.mlv-root[data-theme=hc] .mlv-glyph__ink{fill:var(--mlv-sev-marker, currentColor)}.mlv-badge{position:absolute;top:-6px;right:-6px;z-index:5;display:flex;align-items:center;gap:var(--mlv-s1);height:20px;padding:0 var(--mlv-s2);border-radius:var(--mlv-r-pill);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1)}.mlv-badge--high{border:1px solid color-mix(in srgb,var(--mlv-sev-high) 45%,transparent)}.mlv-badge--medium{border:1px solid color-mix(in srgb,var(--mlv-sev-medium) 45%,transparent)}.mlv-badge--low{border:1px solid color-mix(in srgb,var(--mlv-sev-low) 45%,transparent)}.mlv-badge__count,.mlv-badge__ids{color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-badge__ids{padding-right:2px;color:var(--mlv-text);white-space:nowrap}.mlv-cluster{display:inline-flex;align-items:center;gap:var(--mlv-s3)}.mlv-cluster__item{display:inline-flex;align-items:center;gap:2px}.mlv-cluster__count{color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums}[data-stage]{--mlv-stage: var(--mlv-stage-unknown)}[data-phase-tone="0"]{--mlv-stage: var(--mlv-phase-0)}[data-phase-tone="1"]{--mlv-stage: var(--mlv-phase-1)}[data-phase-tone="2"]{--mlv-stage: var(--mlv-phase-2)}[data-phase-tone="3"]{--mlv-stage: var(--mlv-phase-3)}[data-phase-tone="4"]{--mlv-stage: var(--mlv-phase-4)}[data-phase-tone="5"]{--mlv-stage: var(--mlv-phase-5)}[data-phase-tone="6"]{--mlv-stage: var(--mlv-phase-6)}[data-phase-tone="7"]{--mlv-stage: var(--mlv-phase-7)}[data-sev=high]{--mlv-sev: var(--mlv-sev-high)}[data-sev=medium]{--mlv-sev: var(--mlv-sev-medium)}[data-sev=low]{--mlv-sev: var(--mlv-sev-low)}.mlv-node:focus-visible,.mlv-group__header:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px;border-radius:var(--mlv-r-node)}.mlv-node__title--wrap{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;line-clamp:1;white-space:normal;overflow-wrap:anywhere;text-overflow:clip}.mlv-node__title--wrap[data-lines="2"]{-webkit-line-clamp:2;line-clamp:2}.mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3;line-clamp:3}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap{-webkit-line-clamp:2;line-clamp:2}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3;line-clamp:3;font-size:min(calc(var(--mlv-compact-title) / var(--mlv-z, 1)),calc(100cqh / 3.45))}.mlv-edge{pointer-events:none}.mlv-edge__hit{fill:none;stroke:transparent;stroke-width:12;pointer-events:stroke;cursor:pointer}.mlv-edge__path,.mlv-legend__edge{fill:none;stroke:var(--mlv-edge);stroke-width:max(1.5px,calc(1px / var(--mlv-z, 1)));stroke-linecap:round;stroke-linejoin:round;transition:stroke var(--mlv-dur-1) var(--mlv-ease)}.mlv-edge[data-basis=inferred] .mlv-edge__path,.mlv-edge[data-basis=inferred] .mlv-legend__edge{stroke-dasharray:calc(6px / var(--mlv-z, 1)) calc(4px / var(--mlv-z, 1));stroke-linecap:butt}.mlv-edge[data-basis=unresolved] .mlv-edge__path,.mlv-edge[data-basis=unresolved] .mlv-legend__edge{stroke-dasharray:calc(1.5px / var(--mlv-z, 1)) calc(3.5px / var(--mlv-z, 1));stroke-linecap:round}.mlv-arrow{fill:var(--mlv-edge);stroke:none}.mlv-edge.is-hover .mlv-edge__path,.mlv-edge.is-lit .mlv-edge__path{stroke:var(--mlv-text-2);stroke-width:max(2px,calc(1.5px / var(--mlv-z, 1)))}.mlv-edge.is-selected .mlv-edge__path{stroke:var(--mlv-accent);stroke-width:max(2.5px,calc(2px / var(--mlv-z, 1)))}.mlv-edge.has-issue .mlv-edge__path{stroke:var(--mlv-sev, var(--mlv-edge));opacity:.85}.mlv-edge-label{font-family:var(--mlv-font-mono);font-size:10px;fill:var(--mlv-text-2);paint-order:stroke;stroke:var(--mlv-bg);stroke-width:3;stroke-linejoin:round;text-anchor:middle;dominant-baseline:middle;opacity:0;transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-edge.is-hover .mlv-edge-label,.mlv-edge.is-selected .mlv-edge-label,.mlv-edge.is-lit .mlv-edge-label,.mlv-edge.has-label .mlv-edge-label,.mlv-edge--merged .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--data .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--control .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--state .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--loop .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--output .mlv-edge-label{opacity:1}.mlv-canvas[data-lod=compact] .mlv-edge-label{opacity:0}.mlv-edge-marker__disc{fill:var(--mlv-surface);stroke:var(--mlv-sev-marker, var(--mlv-sev-low));stroke-width:1}.mlv-edge-marker--high{--mlv-sev-marker: var(--mlv-sev-high)}.mlv-edge-marker--medium{--mlv-sev-marker: var(--mlv-sev-medium)}.mlv-edge-marker--low{--mlv-sev-marker: var(--mlv-sev-low)}.mlv-connector{fill:none;stroke:var(--mlv-sev, var(--mlv-accent));stroke-width:1.5;stroke-dasharray:2 4}.mlv-connector__badge{fill:var(--mlv-surface);stroke:var(--mlv-sev, var(--mlv-accent));stroke-width:1}.mlv-connector__label{font-family:var(--mlv-font-mono);font-size:9px;fill:var(--mlv-text);text-anchor:middle;dominant-baseline:central}.mlv-bundles{pointer-events:none}.mlv-bundle__trunk,.mlv-bundle__spur{fill:none;stroke:var(--mlv-edge);stroke-linecap:round;stroke-linejoin:round;transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-bundle__trunk{stroke-width:3.5;opacity:.72}.mlv-bundle__spur{stroke-width:1.5;opacity:.45}.mlv-bundle.has-issue .mlv-bundle__trunk,.mlv-bundle.has-issue .mlv-bundle__spur{stroke:var(--mlv-sev, var(--mlv-edge))}.mlv-bundle__badge-box{fill:var(--mlv-surface);stroke:var(--mlv-edge);stroke-width:1;opacity:.95}.mlv-bundle.has-issue .mlv-bundle__badge-box{stroke:var(--mlv-sev, var(--mlv-edge))}.mlv-bundle__badge-text{font-family:var(--mlv-font-mono);font-size:10px;fill:var(--mlv-text-2);text-anchor:middle;dominant-baseline:central}.mlv-bundle__badge{transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-bundle.is-expanded .mlv-bundle__trunk,.mlv-bundle.is-expanded .mlv-bundle__spur,.mlv-bundle.is-expanded .mlv-bundle__badge,.mlv-canvas .mlv-edge.is-bundled .mlv-edge__path,.mlv-canvas .mlv-edge.is-bundled .mlv-edge-label{opacity:0}.mlv-canvas .mlv-edge.is-bundled[data-basis=inferred] .mlv-edge__path,.mlv-canvas .mlv-edge.is-bundled[data-basis=unresolved] .mlv-edge__path{opacity:1}.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=observed]:not(.is-hover):not(.is-selected):not(.is-lit) .mlv-edge__path{stroke-opacity:.3}.mlv-canvas[data-exceptions=on] .mlv-bundle__trunk,.mlv-canvas[data-exceptions=on] .mlv-bundle__spur{stroke-opacity:.3}.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=inferred] .mlv-edge__path,.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=unresolved] .mlv-edge__path{stroke:var(--mlv-text)}.mlv-canvas[data-lod=compact] .mlv-bundle__badge{opacity:0}.mlv-edge__stalebg{fill:var(--mlv-surface);stroke:var(--mlv-stale-ink);stroke-width:1}.mlv-edge__staleicon{stroke:var(--mlv-stale-ink);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}.mlv-edge__stale{pointer-events:none}.mlv-edge[data-stage]:not(.has-issue){--mlv-flow-color: var(--mlv-stage)}.mlv-edge.has-issue{--mlv-flow-color: var(--mlv-sev)}body.vscode-high-contrast .mlv-edge[data-stage],body.vscode-high-contrast .mlv-edge.has-issue,body.vscode-high-contrast-light .mlv-edge[data-stage],body.vscode-high-contrast-light .mlv-edge.has-issue,:root[data-theme=hc] .mlv-edge[data-stage],:root[data-theme=hc] .mlv-edge.has-issue,.mlv-root[data-theme=hc] .mlv-edge[data-stage],.mlv-root[data-theme=hc] .mlv-edge.has-issue{--mlv-flow-color: var(--mlv-text)}.mlv-edge__flow{fill:none;stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:var(--mlv-flow-width, 3px);stroke-linecap:round;pointer-events:none}.mlv-edge.is-flowing--pulse .mlv-edge__flow{stroke-dasharray:none;stroke-opacity:var(--mlv-flow-underlay-opacity, .28)}.mlv-edge__charge{pointer-events:none;animation:mlv-charge-ends var(--mlv-flow-dur, .9s) linear infinite;animation-iteration-count:var(--mlv-flow-iter, infinite);animation-fill-mode:forwards}.mlv-edge__charge--twin{animation-delay:calc(var(--mlv-flow-dur, .9s) / -2)}@keyframes mlv-charge-ends{0%{opacity:0}8%{opacity:1}92%{opacity:1}to{opacity:0}}.mlv-edge__charge-halo{fill:var(--mlv-flow-color, var(--mlv-accent));fill-opacity:var(--mlv-flow-halo-opacity, .18);stroke:none}.mlv-edge__charge-glow{fill:var(--mlv-flow-color, var(--mlv-accent));fill-opacity:var(--mlv-flow-glow-opacity, .42);stroke:none}.mlv-edge__charge-core{fill:var(--mlv-surface);stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:var(--mlv-flow-core-width, 1.6)}body.vscode-high-contrast .mlv-edge__charge-halo,body.vscode-high-contrast-light .mlv-edge__charge-halo,:root[data-theme=hc] .mlv-edge__charge-halo,.mlv-root[data-theme=hc] .mlv-edge__charge-halo{fill:none;stroke:var(--mlv-text);stroke-width:1.2}.mlv-edge.is-flowing .mlv-edge__flow{stroke-dasharray:var(--mlv-flow-head) var(--mlv-flow-gap);animation:mlv-flow-stream var(--mlv-flow-dur-data) linear infinite;animation-delay:var(--mlv-flow-delay, 0ms);animation-iteration-count:var(--mlv-flow-iter, infinite)}.mlv-edge--call.is-flowing .mlv-edge__flow,.mlv-edge--control.is-flowing .mlv-edge__flow{animation-duration:var(--mlv-flow-dur-call)}.mlv-edge--control.mlv-edge--back.is-flowing .mlv-edge__flow{animation-duration:var(--mlv-flow-dur-back)}@keyframes mlv-flow-stream{0%{stroke-dashoffset:0}to{stroke-dashoffset:var(--mlv-flow-end, -20)}}.mlv-edge__port{stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:1.4;pointer-events:none}.mlv-edge__port--out{fill:var(--mlv-surface)}.mlv-edge__port--in{fill:var(--mlv-flow-color, var(--mlv-accent));stroke:var(--mlv-surface);stroke-width:1.4}.mlv-canvas[data-flow=motion] .mlv-edge.is-flowing:not(.is-flow-settled) .mlv-edge__path,.mlv-canvas[data-flow=motion] .mlv-edge.is-flowing--pulse:not(.is-flow-settled) .mlv-edge__path{marker-end:none}.mlv-canvas[data-flow=static] .mlv-edge.is-flowing--pulse .mlv-edge__path,.mlv-edge.is-flowing--pulse.is-flow-settled .mlv-edge__path{stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:max(2.5px,calc(2px / var(--mlv-z, 1)))}.mlv-edge__dir{fill:none;stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}.mlv-node.is-flow-source,.mlv-group.is-flow-source{box-shadow:0 0 0 2px var(--mlv-stage, var(--mlv-accent))}.mlv-node.is-flow-target,.mlv-group.is-flow-target{box-shadow:0 0 0 2px var(--mlv-surface),0 0 0 4px var(--mlv-accent)}@media(prefers-reduced-motion:reduce){.mlv-edge__flow,.mlv-edge__charge{display:none!important}}body.vscode-reduce-motion .mlv-edge__flow,body.vscode-reduce-motion .mlv-edge__charge,body.vscode-using-screen-reader .mlv-edge__flow,body.vscode-using-screen-reader .mlv-edge__charge{display:none!important}.mlv-canvas[data-flow=off] .mlv-edge__flow,.mlv-canvas[data-flow=off] .mlv-edge__charge,.mlv-canvas[data-flow=off] .mlv-edge__port,.mlv-canvas[data-flow=off] .mlv-edge__dir{display:none}.mlv-rail{position:relative;display:flex;flex-direction:column;flex:none;width:var(--mlv-rail-width);min-width:280px;max-width:560px;border-left:1px solid var(--mlv-border);background:var(--mlv-surface)}.mlv-rail[hidden]{display:none}.mlv-rail__grip{position:absolute;top:0;left:-2px;width:5px;height:100%;cursor:col-resize;z-index:5}.mlv-rail__grip:after{content:"";position:absolute;left:2px;top:0;width:1px;height:100%;background:transparent}.mlv-rail__grip:hover:after{background:var(--mlv-accent)}.mlv-rail__bar{display:flex;align-items:stretch;flex:none;border-bottom:1px solid var(--mlv-border)}.mlv-rail__tabs{display:flex;flex:1 1 auto;min-width:0;height:36px}.mlv-rail__tab{flex:1 1 auto;min-width:0;padding:0 var(--mlv-s3);white-space:nowrap;border:0;border-bottom:2px solid transparent;background:none;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);cursor:pointer}.mlv-rail__tab[aria-selected=true]{color:var(--mlv-text);border-bottom-color:var(--mlv-accent);font-weight:var(--mlv-fw-semi)}.mlv-rail__tab:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__tabcount{color:var(--mlv-text-2);font-variant-numeric:tabular-nums}.mlv-rail__chevron{flex:none;align-self:center;width:26px;height:26px;min-height:0;margin:0 var(--mlv-s2);padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-rail__chevron[hidden]{display:none}.mlv-rail__chevron .mlv-uicon{transform:rotate(90deg)}.mlv-rail__chevron[aria-expanded=false] .mlv-uicon{transform:rotate(-90deg)}.mlv-rail__panel:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__count{color:var(--mlv-text-3);font-weight:var(--mlv-fw-regular);text-transform:none;letter-spacing:0}.mlv-body[data-rail=sheet]{flex-direction:column}.mlv-body[data-rail=sheet]>.mlv-main{flex:1 1 0;min-height:0}.mlv-rail[data-mode=sheet]{flex:0 0 calc(var(--mlv-sheet-fraction, .47) * 100%);width:auto;min-width:0;max-width:none;min-height:32px;max-height:calc(100% - min(240px,45%));border-left:0;border-top:1px solid var(--mlv-border-strong)}.mlv-rail[data-mode=sheet][data-expanded=false]{flex-basis:32px;height:32px}.mlv-rail[data-mode=sheet] .mlv-rail__tabs{height:31px}.mlv-rail[data-mode=sheet][data-expanded=false] .mlv-rail__bar{border-bottom:0}.mlv-rail[data-mode=sheet]>.mlv-rail__grip{top:-4px;left:0;width:100%;height:9px;cursor:row-resize}.mlv-rail[data-mode=sheet]>.mlv-rail__grip:after{left:50%;top:2px;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--mlv-border-strong)}.mlv-rail[data-mode=sheet]>.mlv-rail__grip:hover:after,.mlv-rail[data-mode=sheet]>.mlv-rail__grip:focus-visible:after{background:var(--mlv-accent)}.mlv-rail__grip:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__panel{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--mlv-s4)}.mlv-rail__panel[hidden]{display:none}.mlv-rail__section{margin:0 0 var(--mlv-s5)}.mlv-rail__heading{display:flex;align-items:center;gap:var(--mlv-s3);margin:0 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);text-transform:uppercase;letter-spacing:var(--mlv-track-caps)}.mlv-issues{list-style:none;margin:0;padding:0}.mlv-issues__item{display:flex;flex-wrap:wrap;align-items:flex-start}.mlv-issue{display:flex;align-items:flex-start;gap:var(--mlv-s4);flex:1 1 0;min-width:0;padding:var(--mlv-s4);border:1px solid transparent;border-radius:var(--mlv-r-btn);background:none;cursor:pointer;text-align:left}.mlv-issue:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-issue:hover{background:var(--mlv-surface-2)}.mlv-issue.is-selected{border-color:var(--mlv-accent);background:var(--mlv-surface-2)}.mlv-issue__text{flex:1 1 auto;min-width:0}.mlv-issue__short,.mlv-insp__short{flex:none;color:var(--mlv-text);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-issue__title{font-size:12.5px;font-weight:var(--mlv-fw-medium);line-height:var(--mlv-lh-tight)}.mlv-issue__meta{display:flex;align-items:center;gap:var(--mlv-s3);margin-top:var(--mlv-s1);color:var(--mlv-text-3);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-issue__open{flex:none;align-self:center}.mlv-issue__detail{flex:1 0 100%}.mlv-empty-note{padding:var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12);text-align:center}.mlv-sel[data-columns="2"]{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);column-gap:var(--mlv-s7, 20px);align-items:start}.mlv-sel__col{min-width:0}.mlv-insp__eyebrow{display:flex;flex-wrap:wrap;align-items:center;gap:0 var(--mlv-s2);margin:0 0 var(--mlv-s2);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__phase{display:inline-flex;align-items:center;gap:var(--mlv-s2);color:var(--mlv-text);font-weight:var(--mlv-fw-medium)}.mlv-insp__phasedot{width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-insp__sep{color:var(--mlv-text-3)}.mlv-insp__severity{color:var(--mlv-text)}.mlv-insp__title{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi);line-height:var(--mlv-lh-tight);overflow-wrap:anywhere}.mlv-insp__ends{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-12);overflow-wrap:anywhere}.mlv-insp__section{margin:var(--mlv-s5) 0 0}.mlv-sel__col--evidence>.mlv-insp__section:first-child{margin-top:0}.mlv-insp__issue-title{flex:0 1 auto;min-width:0;color:var(--mlv-text);text-align:left;overflow-wrap:anywhere}.mlv-insp__issue-head .mlv-mono{color:var(--mlv-text-3)}.mlv-insp__issue-id{margin:calc(-1 * var(--mlv-s2)) 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-insp__note,.mlv-insp__aside{color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__links,.mlv-insp__sentences{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--mlv-s2);font-size:var(--mlv-fs-12);line-height:1.45}.mlv-insp__flow-basis{color:var(--mlv-text-2)}.mlv-quote{padding:var(--mlv-s3) 0 0;border-top:1px solid var(--mlv-border)}.mlv-quote__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--mlv-s1) var(--mlv-s3);font-size:var(--mlv-fs-11)}.mlv-quote__num{flex:none;min-width:1.4em;color:var(--mlv-text-2);font-variant-numeric:tabular-nums;font-weight:var(--mlv-fw-semi)}.mlv-quote__loc{flex:1 0 calc(100% - 1.4em - var(--mlv-s3));min-width:0;color:var(--mlv-text);font-family:var(--mlv-font-mono);overflow-wrap:anywhere}.mlv-quote__loc+*{margin-left:calc(1.4em + var(--mlv-s3))}.mlv-quote__at{color:var(--mlv-text-2)}.mlv-quote__role{color:var(--mlv-text-2);font-style:italic}.mlv-quote__fresh.is-muted{color:var(--mlv-text-2)}.mlv-quote__fresh.mlv-insp__stale{margin-left:0}.mlv-quote__open{flex:none}.mlv-quote__text{margin:var(--mlv-s2) 0 0;padding:var(--mlv-s2) var(--mlv-s3);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);line-height:1.5;overflow-x:auto;white-space:pre;user-select:text}.mlv-quote__line{display:inline}.mlv-quote__ln{display:inline-block;min-width:3ch;margin-right:var(--mlv-s3);color:var(--mlv-text-3);text-align:right;user-select:none}.mlv-insp__evidence-nav{display:flex;gap:var(--mlv-s2);margin:0 0 var(--mlv-s3)}.mlv-about__section{margin:0 0 var(--mlv-s6);font-size:var(--mlv-fs-12);line-height:1.5}.mlv-about__question{margin:0;color:var(--mlv-text);font-size:13px;white-space:pre-wrap;overflow-wrap:anywhere}.mlv-about__question[data-clamped=true]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden}.mlv-about__more{margin-top:var(--mlv-s2);font-size:var(--mlv-fs-11)}.mlv-about__para,.mlv-about__text,.mlv-about__status{margin:0 0 var(--mlv-s3);color:var(--mlv-text);white-space:pre-wrap;overflow-wrap:anywhere}.mlv-about__lead{font-weight:var(--mlv-fw-semi)}.mlv-about__meta,.mlv-about__note{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__kv{padding:0 3px;border-radius:3px;background:var(--mlv-surface-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__limitations{color:var(--mlv-text)}.mlv-about__limitations summary{cursor:pointer;color:var(--mlv-text);font-weight:var(--mlv-fw-medium)}.mlv-about__limitations ul{margin:var(--mlv-s2) 0 0;padding-left:20px}.mlv-about__limitations li{margin-bottom:var(--mlv-s2);overflow-wrap:anywhere}.mlv-about__files{list-style:none;margin:0 0 var(--mlv-s3);padding:0}.mlv-about__file{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 var(--mlv-s3);padding:2px 0}.mlv-about__path{flex:1 1 auto;min-width:0;font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__file .mlv-quote__fresh{flex:none;font-size:var(--mlv-fs-11)}.mlv-about__trust{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__issue{margin-bottom:var(--mlv-s5);padding:var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface-2)}.mlv-insp__issue-head{display:flex;align-items:center;gap:var(--mlv-s3);margin-bottom:var(--mlv-s3);font-weight:var(--mlv-fw-medium)}.mlv-insp__line{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-11);color:var(--mlv-text-2)}.mlv-insp__fix{margin:0 0 var(--mlv-s3);padding-left:var(--mlv-s4);border-left:3px solid var(--mlv-accent);font-size:var(--mlv-fs-11)}.mlv-insp__fix-label{margin:0 0 2px;color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);letter-spacing:.04em;text-transform:uppercase}.mlv-insp__fix-text{margin:0;color:var(--mlv-text);white-space:pre-wrap;overflow-wrap:anywhere}.mlv-insp__detail{margin:0 0 var(--mlv-s4);color:var(--mlv-text);font-size:var(--mlv-fs-12);line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;user-select:text}.mlv-insp__basis{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__basis .mlv-basis-tag{vertical-align:1px}.mlv-insp__caption{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__limits{margin:var(--mlv-s4) 0 0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__related{list-style:none;margin:var(--mlv-s3) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--mlv-s2)}.mlv-outline,.mlv-outline ul{list-style:none;margin:0;padding:0 0 0 var(--mlv-s5)}.mlv-outline{padding-left:0}.mlv-outline__item{outline:none}.mlv-outline__item:focus>.mlv-outline__row{box-shadow:0 0 0 2px var(--mlv-focus)}.mlv-outline__row{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;padding:2px var(--mlv-s3);border:0;border-radius:var(--mlv-r-chip);background:none;cursor:pointer;text-align:left;font-size:var(--mlv-fs-12)}.mlv-outline__row--lane{font-weight:var(--mlv-fw-semi)}.mlv-outline__chevron{flex:none;width:12px;height:12px;color:var(--mlv-text-3);transform:rotate(90deg);transition:transform var(--mlv-dur-1, .12s) ease}.mlv-outline__item[aria-expanded=false]>.mlv-outline__row .mlv-outline__chevron{transform:rotate(0)}.mlv-outline__chevron--none{transform:none}.mlv-outline__row:hover{background:var(--mlv-surface-2)}.mlv-outline__row.is-selected{background:var(--mlv-surface-2);font-weight:var(--mlv-fw-medium)}.mlv-outline__label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-outline__stage{color:var(--mlv-text-3);font-size:var(--mlv-fs-10);text-transform:uppercase;letter-spacing:var(--mlv-track-caps)}.mlv-outline__row--lane{flex-wrap:wrap}.mlv-outline__row--lane .mlv-outline__label{flex:1 1 0;min-width:0}.mlv-outline__phasecount{flex:1 0 100%;font-weight:var(--mlv-fw-regular);text-transform:none;letter-spacing:normal}.mlv-basis-tag--rail{flex:none;vertical-align:middle}.mlv-relations{margin-top:var(--mlv-s6);padding-top:var(--mlv-s4);border-top:1px solid var(--mlv-border)}.mlv-relations__context{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-relations__views{display:flex;gap:var(--mlv-s2);margin-bottom:var(--mlv-s3)}.mlv-relations__views .mlv-btn[aria-pressed=true]{border-color:var(--mlv-accent);color:var(--mlv-text)}.mlv-relations__list{margin:0;padding:0;list-style:none}.mlv-relations__item+.mlv-relations__item{margin-top:var(--mlv-s2)}.mlv-relations__row{display:block;width:100%;min-height:32px;padding:var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface-2);color:var(--mlv-text);text-align:left;cursor:pointer}.mlv-relations__row:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px}.mlv-relations__path,.mlv-relations__detail{display:block}.mlv-relations__detail{margin-top:var(--mlv-s1);color:var(--mlv-text-3);font-size:var(--mlv-fs-10)}.mlv-insp__actions{display:flex;gap:var(--mlv-s3);flex-wrap:wrap;margin-bottom:var(--mlv-s4)}.mlv-clean__title{color:var(--mlv-text);font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi)}.mlv-clean__detail{font-size:var(--mlv-fs-12);max-width:34ch}.mlv-issue__detail{padding:var(--mlv-s3) var(--mlv-s5) var(--mlv-s5) var(--mlv-s8);border-left:2px solid var(--mlv-accent);margin:0 var(--mlv-s3) var(--mlv-s3);background:var(--mlv-surface-2);border-radius:0 var(--mlv-r-btn) var(--mlv-r-btn) 0}.mlv-issue__detail .mlv-insp__line{font-size:var(--mlv-fs-12)}.mlv-issue__goto{display:flex;flex-wrap:wrap;gap:var(--mlv-s2);margin-top:var(--mlv-s4)}.mlv-empty-note .mlv-btn{margin-top:var(--mlv-s4)}.mlv-insp__issue.is-selected{box-shadow:0 0 0 2px var(--mlv-accent)}.mlv-issue__meta{flex-wrap:wrap;row-gap:var(--mlv-s1);min-width:0}.mlv-issue__meta .mlv-chip{max-width:100%}.mlv-edge--weighted .mlv-edge__path{stroke-width:max(var(--mlv-edge-w, 2px),calc(1px / var(--mlv-z, 1)))}.mlv-edge__weight{pointer-events:none}.mlv-edge__weightbg{fill:var(--mlv-surface);stroke:var(--mlv-border-strong);stroke-width:1}.mlv-edge__weighttext{fill:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:9px;font-variant-numeric:tabular-nums;text-anchor:middle;dominant-baseline:middle}.mlv-edge.has-issue .mlv-edge__weightbg{stroke:var(--mlv-sev, var(--mlv-border-strong))}.mlv-canvas[data-lod=compact] .mlv-edge__weight{display:none}:root[data-theme=hc] .mlv-edge__weightbg,.mlv-root[data-theme=hc] .mlv-edge__weightbg{stroke:currentColor}.mlv-workflow{position:relative;z-index:45;flex:none;height:0}.mlv-workflow__composer{position:absolute;top:4px;right:var(--mlv-s4);display:flex;flex-wrap:wrap;align-items:center;gap:8px;width:min(440px,calc(100vw - 2 * var(--mlv-s4)));padding:10px 12px;border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3)}.mlv-workflow__composer[hidden]{display:none}.mlv-workflow__selection{flex:1 0 100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text);font-size:12px;font-weight:var(--mlv-fw-medium)}.mlv-workflow__intent{flex:0 0 auto}.mlv-workflow__custom{flex:1 1 180px;min-width:0}.mlv-workflow__custom[hidden]{display:none}.mlv-workflow__status{flex:1 0 100%;color:var(--mlv-text);font-size:11px}.mlv-workflow__status:empty{display:none}.mlv-workflow__hint{flex:1 0 100%;margin:0;color:var(--mlv-text-2);font-size:11px}#mlview-authored-error{white-space:pre-wrap;margin:0;padding:6px 12px;flex:none;max-height:20vh;overflow-y:auto;overscroll-behavior:contain;font-family:var(--mlv-font, var(--vscode-font-family, system-ui, sans-serif));font-size:12px;line-height:1.45;color:var(--mlv-text, var(--vscode-foreground, inherit));background:var(--mlv-surface-2, var(--vscode-editorWidget-background, transparent));border-bottom:1px solid var(--mlv-sev-medium, var(--vscode-editorWarning-foreground, currentColor))}.mlv-hostnotice{display:flex;align-items:flex-start;gap:var(--mlv-s3);flex:none;padding:6px 12px;font-size:12px;line-height:1.45;color:var(--mlv-text);background:var(--mlv-surface-2);border-bottom:1px solid var(--mlv-border)}.mlv-hostnotice[hidden]{display:none}.mlv-hostnotice--warn{border-bottom-color:var(--mlv-sev-medium)}.mlv-hostnotice--error{border-bottom-color:var(--mlv-sev-high)}.mlv-hostnotice__icon{flex:none;display:flex;padding-top:2px}.mlv-hostnotice--warn .mlv-hostnotice__icon{color:var(--mlv-sev-medium)}.mlv-hostnotice--error .mlv-hostnotice__icon{color:var(--mlv-sev-high)}.mlv-hostnotice__text{flex:1 1 auto;min-width:0;margin:0;white-space:pre-wrap;overflow-wrap:anywhere;max-height:20vh;overflow-y:auto;overscroll-behavior:contain}.mlv-hostnotice__actions{flex:none;display:flex;flex-wrap:wrap;gap:6px}.mlv-hostnotice__actions[hidden]{display:none}.mlv-status__fresh{display:inline-flex;align-items:center;gap:4px}.mlv-status__fresh.is-warn{color:var(--mlv-text)}.mlv-status__fresh.is-warn .mlv-uicon{color:var(--mlv-stale-ink)}.mlv-status__fresh.is-checking{color:var(--mlv-text-2);font-style:italic}.mlv-status__fresh.is-muted{color:var(--mlv-text-2)}.mlv-insp__stale-note{display:flex;align-items:flex-start;gap:6px;margin:4px 0 6px;font-size:11px;color:var(--mlv-text)}.mlv-insp__stale-note .mlv-uicon,.mlv-insp__stale .mlv-uicon,.mlv-chip--stale .mlv-uicon{flex:none;color:var(--mlv-stale-ink)}.mlv-insp__stale{display:inline-flex;align-items:center;gap:4px;margin-left:6px;font-size:11px;color:var(--mlv-text)}.mlv-link.is-stale,.mlv-btn.is-stale{cursor:not-allowed;color:var(--mlv-text-2);text-decoration:line-through}.mlv-chip--stale{display:inline-flex;align-items:center;gap:3px}.mlv-sheet__note{margin:0 0 8px;font-size:12px;color:var(--mlv-text-2)}.mlv-root--workflow>.mlv-body{flex:1 1 0;min-height:min(320px,50vh)}.mlv-minimap.is-short,.mlv-minimap.is-narrow{display:none}@page{margin:12mm}@media print{.mlv-header,.mlv-workflow,.mlv-hostnotice,.mlv-status,.mlv-rail,.mlv-minimap,.mlv-zoom,.mlv-skiplink,.mlv-legend,.mlv-moremenu,.mlv-statehost,.mlv-tooltip,.mlv-toasts,.mlv-shortcuts{display:none!important}html.mlv-fills-page,html.mlv-fills-page>body,.mlv-root,.mlv-body,.mlv-main,.mlv-canvas{display:block!important;height:auto!important;max-height:none!important;min-height:0!important;overflow:visible!important}.mlv-world{position:static!important;transform:none!important;transform-origin:0 0!important}.mlv-canvas{background-image:none!important;cursor:auto!important}.mlv-canvas[data-lod=compact] .mlv-node__sub,.mlv-canvas[data-lod=compact] .mlv-node__loc{display:block!important}.mlv-canvas[data-lod=compact] .mlv-node__sub[data-lines="2"]{display:-webkit-box!important}.mlv-canvas[data-lod=compact] .mlv-node__chips{display:flex!important}.mlv-canvas[data-lod=compact] .mlv-node__title{font-size:var(--mlv-fs-13)!important;line-height:var(--mlv-lh-tight)!important}.mlv-canvas[data-lod=compact] .mlv-node__iconbox{display:flex!important}.mlv-canvas[data-lod=compact] .mlv-node__main{align-items:flex-start!important;padding:10px var(--mlv-s5) 10px var(--mlv-s4)!important}.mlv-canvas[data-lod=compact] .mlv-node>.mlv-basis-tag{transform-origin:0 50%!important;transform:translateY(-50%) scale(calc(1 / var(--mlv-z, 1)))!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap{-webkit-line-clamp:1!important;line-clamp:1!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="2"]{-webkit-line-clamp:2!important;line-clamp:2!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3!important;line-clamp:3!important}.mlv-canvas[data-lod=compact] .mlv-edge-label{display:inline!important;opacity:0!important}.mlv-canvas[data-lod=compact] .mlv-edge--data .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge--control .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge--merged .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge.has-label .mlv-edge-label{opacity:1!important}.mlv-root{-webkit-print-color-adjust:exact;print-color-adjust:exact}.mlv-edge__flow,.mlv-flow-charge{display:none!important}.mlv-node,.mlv-group,.mlv-lane{break-inside:avoid}} +:root,.mlv-root[data-theme=light]{--mlv-font: var(--vscode-font-family, ui-sans-serif, -apple-system, "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif);--mlv-font-mono: var(--vscode-editor-font-family, "Cascadia Code", "JetBrains Mono", Consolas, ui-monospace, monospace);--mlv-fs-10: 10.5px;--mlv-fs-11: 11px;--mlv-fs-12: 12px;--mlv-fs-13: 13px;--mlv-fs-14: 14px;--mlv-fs-16: 16px;--mlv-fs-20: 20px;--mlv-compact-title: 11.2px;--mlv-lh-tight: 1.35;--mlv-lh-body: 1.5;--mlv-fw-regular: 450;--mlv-fw-medium: 550;--mlv-fw-semi: 650;--mlv-track-caps: .04em;--mlv-s1: 2px;--mlv-s2: 4px;--mlv-s3: 6px;--mlv-s4: 8px;--mlv-s5: 12px;--mlv-s6: 16px;--mlv-s7: 20px;--mlv-s8: 24px;--mlv-s9: 32px;--mlv-s10: 48px;--mlv-r-chip: 4px;--mlv-r-btn: 6px;--mlv-r-node: 10px;--mlv-r-lane: 14px;--mlv-r-panel: 12px;--mlv-r-pill: 999px;--mlv-sh-1: 0 1px 2px rgba(16, 18, 24, .06), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 4px 12px rgba(16, 18, 24, .1), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 16px 40px rgba(16, 18, 24, .18), 0 0 0 1px var(--mlv-border-strong);--mlv-dur-1: .12s;--mlv-dur-2: .18s;--mlv-dur-3: .26s;--mlv-ease: cubic-bezier(.2, .8, .2, 1);--mlv-flow-speed: 220;--mlv-flow-pulse-speed: 320;--mlv-flow-head: 12px;--mlv-flow-gap: 34px;--mlv-flow-gap-call: 96px;--mlv-flow-gap-back: 24px;--mlv-flow-dur-data: .21s;--mlv-flow-dur-call: .49s;--mlv-flow-dur-back: 165ms;--mlv-flow-dur-min: .38s;--mlv-flow-dur-max: 2.2s;--mlv-flow-hop: 90ms;--mlv-flow-width: 3px;--mlv-flow-port-r: 3.5px;--mlv-flow-color: var(--mlv-accent);--mlv-flow-halo-opacity: .18;--mlv-flow-glow-opacity: .42;--mlv-flow-core-width: 1.6;--mlv-flow-underlay-opacity: .28;--mlv-node-w: 216px;--mlv-node-w-lg: 248px;--mlv-node-w-sm: 176px;--mlv-node-h: 72px;--mlv-rail-w: 4px;--mlv-group-header-h: 34px;--mlv-lane-pad: 20px;--mlv-header-h: 36px;--mlv-status-h: 22px;--mlv-rail-width: 360px;--mlv-bg: var(--vscode-editor-background, #FBFBFD);--mlv-surface: var(--vscode-editorWidget-background, #FFFFFF);--mlv-surface-2: var(--vscode-list-hoverBackground, #F3F4F8);--mlv-border: var(--vscode-widget-border, #E3E5EB);--mlv-border-strong: var(--vscode-widget-border, #C9CDD6);--mlv-text: var(--vscode-editor-foreground, #16181D);--mlv-text-2: var(--vscode-descriptionForeground, #5A6070);--mlv-text-3: var(--vscode-descriptionForeground, #676E81);--mlv-link: var(--vscode-textLink-foreground, #2B57C4);--mlv-accent: var(--vscode-focusBorder, #3B6CF6);--mlv-accent-solid: var(--vscode-button-background, #2B57C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #3B6CF6);--mlv-canvas-dot: var(--vscode-widget-border, #E6E9F0);--mlv-edge: color-mix(in srgb, var(--mlv-text) 64%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 58%, var(--mlv-bg));--mlv-scrim: rgba(16, 18, 24, .36);--mlv-phase-0: #00796B;--mlv-phase-1: #6A4FB3;--mlv-phase-2: #0A693C;--mlv-phase-3: #A0388F;--mlv-phase-4: #558B2F;--mlv-phase-5: #0B7F99;--mlv-phase-6: #546E7A;--mlv-phase-7: #1F6FB2;--mlv-stage-unknown: #7A8090;--mlv-lane-tint: .025;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #D0342C));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #E8A317));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #2F5FD0));--mlv-sev-high-ink: var(--vscode-editor-background, #FFFFFF);--mlv-sev-medium-ink: #3A2500;--mlv-sev-low-ink: var(--vscode-editor-background, #FFFFFF)}@media(prefers-color-scheme:dark){:root:not([data-theme=light]):not([data-theme=hc]),.mlv-root:not([data-theme=light]):not([data-theme=hc]){--mlv-bg: var(--vscode-editor-background, #131417);--mlv-surface: var(--vscode-editorWidget-background, #1A1C21);--mlv-surface-2: var(--vscode-list-hoverBackground, #22252C);--mlv-border: var(--vscode-widget-border, #2C3038);--mlv-border-strong: var(--vscode-widget-border, #3B414C);--mlv-text: var(--vscode-editor-foreground, #E6E8EE);--mlv-text-2: var(--vscode-descriptionForeground, #9AA1B1);--mlv-text-3: var(--vscode-descriptionForeground, #969DAD);--mlv-link: var(--vscode-textLink-foreground, #8FB0FF);--mlv-accent: var(--vscode-focusBorder, #6E96FF);--mlv-accent-solid: var(--vscode-button-background, #3355C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #6E96FF);--mlv-canvas-dot: var(--vscode-widget-border, #23262D);--mlv-edge: color-mix(in srgb, var(--mlv-text) 54%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 46%, var(--mlv-bg));--mlv-scrim: rgba(0, 0, 0, .5);--mlv-sh-1: 0 1px 2px rgba(0, 0, 0, .36), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 6px 16px rgba(0, 0, 0, .44), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 20px 48px rgba(0, 0, 0, .55), 0 0 0 1px var(--mlv-border-strong);--mlv-phase-0: #4DB6AC;--mlv-phase-1: #A48BE0;--mlv-phase-2: #4CA871;--mlv-phase-3: #D684C5;--mlv-phase-4: #9CCC65;--mlv-phase-5: #4FC3D9;--mlv-phase-6: #90A4AE;--mlv-phase-7: #6FA8DC;--mlv-stage-unknown: #8B93A7;--mlv-lane-tint: .035;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #FF6169));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #F2B03C));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #6E96FF));--mlv-sev-high-ink: #1A0405;--mlv-sev-medium-ink: #2A1A00;--mlv-sev-low-ink: #0B1020}}body.vscode-dark,:root[data-theme=dark],.mlv-root[data-theme=dark]{--mlv-bg: var(--vscode-editor-background, #131417);--mlv-surface: var(--vscode-editorWidget-background, #1A1C21);--mlv-surface-2: var(--vscode-list-hoverBackground, #22252C);--mlv-border: var(--vscode-widget-border, #2C3038);--mlv-border-strong: var(--vscode-widget-border, #3B414C);--mlv-text: var(--vscode-editor-foreground, #E6E8EE);--mlv-text-2: var(--vscode-descriptionForeground, #9AA1B1);--mlv-text-3: var(--vscode-descriptionForeground, #969DAD);--mlv-link: var(--vscode-textLink-foreground, #8FB0FF);--mlv-accent: var(--vscode-focusBorder, #6E96FF);--mlv-accent-solid: var(--vscode-button-background, #3355C4);--mlv-on-accent: var(--vscode-button-foreground, #FFFFFF);--mlv-focus: var(--vscode-focusBorder, #6E96FF);--mlv-canvas-dot: var(--vscode-widget-border, #23262D);--mlv-edge: color-mix(in srgb, var(--mlv-text) 54%, var(--mlv-bg));--mlv-node-edge: color-mix(in srgb, var(--mlv-text) 46%, var(--mlv-bg));--mlv-scrim: rgba(0, 0, 0, .5);--mlv-sh-1: 0 1px 2px rgba(0, 0, 0, .36), 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 6px 16px rgba(0, 0, 0, .44), 0 0 0 1px var(--mlv-border-strong);--mlv-sh-3: 0 20px 48px rgba(0, 0, 0, .55), 0 0 0 1px var(--mlv-border-strong);--mlv-phase-0: #4DB6AC;--mlv-phase-1: #A48BE0;--mlv-phase-2: #4CA871;--mlv-phase-3: #D684C5;--mlv-phase-4: #9CCC65;--mlv-phase-5: #4FC3D9;--mlv-phase-6: #90A4AE;--mlv-phase-7: #6FA8DC;--mlv-stage-unknown: #8B93A7;--mlv-lane-tint: .035;--mlv-group-tint: 0;--mlv-sev-high: var(--vscode-editorError-foreground, var(--vscode-charts-red, #FF6169));--mlv-sev-medium: var(--vscode-editorWarning-foreground, var(--vscode-charts-yellow, #F2B03C));--mlv-sev-low: var(--vscode-editorInfo-foreground, var(--vscode-charts-blue, #6E96FF));--mlv-sev-high-ink: #1A0405;--mlv-sev-medium-ink: #2A1A00;--mlv-sev-low-ink: #0B1020}body.vscode-high-contrast,body.vscode-high-contrast-light,:root[data-theme=hc],.mlv-root[data-theme=hc]{--mlv-surface: var(--vscode-editor-background, #000000);--mlv-surface-2: var(--vscode-editor-background, #000000);--mlv-bg: var(--vscode-editor-background, #000000);--mlv-text: var(--vscode-editor-foreground, #FFFFFF);--mlv-text-2: var(--vscode-editor-foreground, #FFFFFF);--mlv-text-3: var(--vscode-editor-foreground, #FFFFFF);--mlv-link: var(--vscode-textLink-foreground, #6BB7FF);--mlv-border: var(--vscode-contrastBorder, #FFFFFF);--mlv-border-strong: var(--vscode-contrastBorder, #FFFFFF);--mlv-edge: var(--vscode-contrastBorder, #FFFFFF);--mlv-node-edge: var(--vscode-contrastBorder, #FFFFFF);--mlv-canvas-dot: transparent;--mlv-lane-tint: 0;--mlv-group-tint: 0;--mlv-phase-0: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-1: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-2: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-3: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-4: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-5: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-6: var(--vscode-contrastBorder, #FFFFFF);--mlv-phase-7: var(--vscode-contrastBorder, #FFFFFF);--mlv-sh-1: 0 0 0 1px var(--mlv-border);--mlv-sh-2: 0 0 0 2px var(--mlv-border);--mlv-sh-3: 0 0 0 2px var(--mlv-border);--mlv-flow-color: var(--mlv-text);--mlv-flow-width: 4px;--mlv-flow-core-width: 2.2;--mlv-flow-underlay-opacity: .45}.mlv-root{--mlv-stale-ink: color-mix(in srgb, var(--mlv-sev-medium) 70%, var(--mlv-text))}html.mlv-fills-page,html.mlv-fills-page>body{height:100%;margin:0;padding:0;overflow:hidden}html.mlv-fills-page>body>.mlv-root{height:100%}.mlv-root{position:relative;display:flex;flex-direction:column;width:100%;height:100%;min-height:240px;overflow:hidden;font-family:var(--mlv-font);font-size:var(--mlv-fs-13);line-height:var(--mlv-lh-body);color:var(--mlv-text);background:var(--mlv-bg);-webkit-font-smoothing:antialiased}.mlv-root *,.mlv-root *:before,.mlv-root *:after{box-sizing:border-box}.mlv-root button,.mlv-root input,.mlv-root select{font:inherit;color:inherit}.mlv-body{position:relative;display:flex;flex:1 1 auto;min-height:0}.mlv-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.mlv-root :focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px;border-radius:var(--mlv-r-btn)}.mlv-btn{display:inline-flex;align-items:center;gap:var(--mlv-s3);min-height:24px;padding:3px var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);cursor:pointer;transition:background var(--mlv-dur-1) var(--mlv-ease),border-color var(--mlv-dur-1) var(--mlv-ease)}.mlv-btn:hover{background:var(--mlv-surface-2);border-color:var(--mlv-border-strong)}.mlv-btn--primary{background:var(--mlv-accent-solid);border-color:var(--mlv-accent-solid);color:var(--mlv-on-accent)}.mlv-root .mlv-btn--primary{color:var(--mlv-on-accent)}.mlv-btn--primary:hover{background:var(--mlv-accent-solid);filter:brightness(1.08)}body.vscode-high-contrast .mlv-root .mlv-btn--primary,body.vscode-high-contrast-light .mlv-root .mlv-btn--primary,:root[data-theme=hc] .mlv-root .mlv-btn--primary,.mlv-root[data-theme=hc] .mlv-btn--primary{border-color:var(--vscode-button-border, var(--vscode-contrastBorder, var(--mlv-text)))}.mlv-btn--icon{min-width:26px;justify-content:center;padding:3px var(--mlv-s3)}.mlv-btn[aria-pressed=true]{background:var(--mlv-surface-2);border-color:var(--mlv-border-strong)}.mlv-btn[hidden]{display:none}.mlv-chip{display:inline-flex;align-items:center;gap:var(--mlv-s2);min-height:20px;padding:1px var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-medium);white-space:nowrap}.mlv-chip[hidden]{display:none}.mlv-chip--btn{cursor:pointer;min-height:24px;padding:2px var(--mlv-s4)}.mlv-chip--btn[aria-pressed=false]{border-style:dashed;border-color:var(--mlv-text-3);text-decoration:line-through}.mlv-mono{font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11)}.mlv-link{color:var(--mlv-link);background:none;border:0;padding:0;font:inherit;text-align:left;cursor:pointer;text-decoration:underline;text-underline-offset:2px}.mlv-link--inline{padding:0;border:0;background:none;color:var(--mlv-link);font:inherit;cursor:pointer;text-decoration:underline}.mlv-input{min-width:0;padding:3px var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12)}.mlv-input::placeholder{color:var(--mlv-text-3)}.mlv-uicon,.mlv-icon{flex:none;display:block}@media(prefers-reduced-motion:reduce){.mlv-root *,.mlv-root *:before,.mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}body.vscode-reduce-motion .mlv-root *,body.vscode-reduce-motion .mlv-root *:before,body.vscode-reduce-motion .mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}body.vscode-using-screen-reader .mlv-root *,body.vscode-using-screen-reader .mlv-root *:before,body.vscode-using-screen-reader .mlv-root *:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}.mlv-skiplink{position:absolute;left:-9999px;top:0;z-index:100;padding:var(--mlv-s3) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);text-decoration:none}.mlv-skiplink:focus,.mlv-skiplink:focus-visible{left:var(--mlv-s4);top:var(--mlv-s4)}.mlv-main{position:relative;display:flex;flex:1 1 auto;flex-direction:column;min-width:0;min-height:0}.mlv-main>.mlv-canvas{min-height:0}.mlv-loc{display:inline-flex;align-items:baseline;min-width:0;max-width:100%}.mlv-loc__file{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.mlv-loc__at{flex:0 0 auto;white-space:pre}.mlv-header{position:relative;display:flex;align-items:center;gap:var(--mlv-s4);flex:none;min-width:0;height:var(--mlv-header-h);padding:0 var(--mlv-s4) 0 var(--mlv-s5);border-bottom:1px solid var(--mlv-border);background:var(--mlv-surface);white-space:nowrap}.mlv-header__title{flex:0 1 auto;min-width:48px;margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);line-height:1.3}.mlv-header__bar{display:flex;align-items:center;gap:var(--mlv-s3);flex:1 0 auto;min-width:0}.mlv-header__spacer{flex:1 1 0;min-width:var(--mlv-s2)}.mlv-header__prov{flex:0 1 auto;min-width:0;max-width:38ch;height:22px;gap:5px;overflow:hidden;cursor:pointer;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-regular);background:transparent}.mlv-header__prov:hover{color:var(--mlv-text);border-color:var(--mlv-border-strong)}.mlv-header__prov[aria-expanded=true]{color:var(--mlv-text);border-color:var(--mlv-border-strong);background:var(--mlv-surface-2)}.mlv-header__rev{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-header__host{flex:none}.mlv-header__dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--mlv-text-3);opacity:.6}.mlv-header__prov[data-stale=true] .mlv-header__dot{background:var(--mlv-stale-ink);opacity:1}.mlv-header .mlv-chip--btn{height:24px;gap:var(--mlv-s2);padding:0 var(--mlv-s3);color:var(--mlv-text);font-size:var(--mlv-fs-12);font-variant-numeric:tabular-nums;background:transparent}.mlv-header .mlv-chip--btn:hover{background:var(--mlv-surface-2)}.mlv-chip--exceptions{white-space:nowrap}.mlv-chip--exceptions[hidden]{display:none}.mlv-chip--exceptions .mlv-uicon{color:var(--mlv-text-2)}.mlv-chip--exceptions[aria-pressed=false]{border-style:solid;border-color:var(--mlv-border);text-decoration:none}.mlv-chip--exceptions[aria-pressed=true]{border-color:var(--mlv-text-2);color:var(--mlv-text);background:var(--mlv-surface-2)}.mlv-chip--exceptions .mlv-chip__detail{color:var(--mlv-text-2)}.mlv-header__unit{flex:none;margin-left:calc(-1 * var(--mlv-s2));color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-header[data-fit="3"] .mlv-header__title,.mlv-header[data-fit="4"] .mlv-header__title{position:absolute;width:1px;min-width:0;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.mlv-header .mlv-btn--icon{width:26px;height:26px;min-height:0;padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-header .mlv-btn--icon:hover,.mlv-header .mlv-btn--icon[aria-expanded=true]{border-color:transparent;background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-header__refine{display:inline-flex;flex:none}.mlv-header .mlv-workflow__refine{height:24px;min-height:0;padding:0 var(--mlv-s4);font-weight:var(--mlv-fw-medium)}.mlv-header__searchbtn{display:none}.mlv-search{display:flex;align-items:center;flex:0 1 260px;min-width:150px;height:24px}.mlv-header[data-layout=mid] .mlv-header__host,.mlv-header[data-layout=narrow] .mlv-header__host,.mlv-header[data-layout=mid][data-search=closed] .mlv-search{display:none}.mlv-header[data-layout=mid][data-search=closed] .mlv-header__searchbtn{display:inline-flex}.mlv-header[data-layout=narrow] .mlv-header__prov,.mlv-header[data-layout=narrow][data-search=closed] .mlv-search,.mlv-header[data-layout=narrow][data-search=open] .mlv-header__title,.mlv-header[data-layout=narrow][data-search=open] .mlv-header__spacer{display:none}.mlv-header[data-layout=narrow] .mlv-search{flex:1 1 auto;min-width:96px}.mlv-header[data-fit="3"],.mlv-header[data-fit="4"]{gap:var(--mlv-s2);padding:0 var(--mlv-s3)}.mlv-header[data-fit="3"] .mlv-header__bar,.mlv-header[data-fit="4"] .mlv-header__bar{gap:var(--mlv-s2)}.mlv-search__glyph{position:relative;left:22px;margin-left:-16px;color:var(--mlv-text-3);pointer-events:none}.mlv-search .mlv-input{flex:1 1 auto;height:24px;padding-left:26px}.mlv-search__results{position:absolute;top:calc(100% + 2px);right:var(--mlv-s4);z-index:50;width:min(460px,calc(100% - 2 * var(--mlv-s4)));max-height:min(380px,60vh);overflow-y:auto;padding:var(--mlv-s2);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3);list-style:none;margin:0;white-space:normal}.mlv-search__results[hidden]{display:none}.mlv-result__count{padding:var(--mlv-s2) var(--mlv-s4) var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-result{display:grid;grid-template-columns:14px minmax(0,1fr);column-gap:var(--mlv-s3);row-gap:1px;align-items:start;width:100%;padding:var(--mlv-s3) var(--mlv-s4);border:0;border-radius:var(--mlv-r-btn);background:none;cursor:pointer;text-align:left}.mlv-result>.mlv-sev-glyph,.mlv-result>svg{grid-column:1;margin-top:2px}.mlv-result:hover,.mlv-result[aria-selected=true]{background:var(--mlv-surface-2)}.mlv-result__label{grid-column:2;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;overflow-wrap:anywhere;font-size:var(--mlv-fs-12);line-height:1.35}.mlv-result__meta{grid-column:2;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-result__jump{grid-column:2;justify-self:start}.mlv-result__empty{padding:var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-moremenu{position:fixed;z-index:60;display:flex;flex-direction:column;min-width:240px;max-width:calc(100vw - 16px);max-height:calc(100vh - 48px);overflow-y:auto;padding:var(--mlv-s2);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--vscode-menu-background, var(--mlv-surface));box-shadow:var(--mlv-sh-3);color:var(--vscode-menu-foreground, var(--mlv-text))}.mlv-moremenu[hidden],.mlv-moremenu__item[hidden],.mlv-moremenu__sep[hidden]{display:none}.mlv-moremenu__sep{height:1px;margin:var(--mlv-s2) var(--mlv-s3);background:var(--vscode-menu-separatorBackground, var(--mlv-border))}.mlv-moremenu__item{display:flex;align-items:center;gap:var(--mlv-s4);width:100%;min-height:26px;padding:2px var(--mlv-s4);border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer;font-size:var(--mlv-fs-12);text-align:left}.mlv-moremenu__item .mlv-uicon{color:var(--mlv-text-2)}.mlv-moremenu__item:hover,.mlv-moremenu__item:focus-visible{background:var(--vscode-menu-selectionBackground, var(--mlv-surface-2));color:var(--vscode-menu-selectionForeground, var(--mlv-text));outline-offset:-2px}.mlv-moremenu__item[disabled]{cursor:default;opacity:.45}.mlv-moremenu__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-moremenu__item[role=menuitemcheckbox]:after{content:"";flex:none;width:14px;text-align:center}.mlv-moremenu__item[aria-checked=true]:after{content:"\2713"}.mlv-moremenu__keys{flex:none;color:var(--mlv-text-2);font-family:inherit;font-size:var(--mlv-fs-11)}.mlv-moremenu__item[data-more-item=phaseindex]{flex-wrap:wrap;row-gap:0}.mlv-moremenu__item[data-more-item=phaseindex]:after{order:2}.mlv-moremenu__item[data-more-item=phaseindex][disabled]{opacity:1}.mlv-moremenu__item[data-more-item=phaseindex][disabled] .mlv-moremenu__label,.mlv-moremenu__note{color:var(--mlv-text-2)}.mlv-moremenu__note{order:3;flex:1 0 100%;padding-left:calc(14px + var(--mlv-s4));font-size:var(--mlv-fs-11);white-space:normal}.mlv-moremenu__note[hidden]{display:none}.mlv-banner__detail{margin-top:var(--mlv-s2);color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);white-space:pre-wrap;max-height:120px;overflow:auto}.mlv-status{display:flex;align-items:center;gap:var(--mlv-s5);flex:none;min-width:0;height:var(--mlv-status-h);padding:0 var(--mlv-s3) 0 var(--mlv-s5);border-top:1px solid var(--mlv-border);background:var(--mlv-surface);color:var(--mlv-text-2);font-size:var(--mlv-fs-11);white-space:nowrap;overflow:hidden}.mlv-status__counts{flex:0 0 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-status__coverage{flex:0 1 auto;min-width:0;height:18px;padding:0 var(--mlv-s2);overflow:hidden;text-overflow:ellipsis;border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer;font:inherit}.mlv-status__coverage[hidden]{display:none}.mlv-status__coverage:hover,.mlv-status__coverage[aria-expanded=true]{background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-status__spacer{flex:1 1 0}.mlv-status__freshness{display:inline-flex;align-items:center;gap:var(--mlv-s4);flex:0 100 auto;min-width:0;overflow:hidden}.mlv-status__fresh{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-status__fresh.is-warn,.mlv-status__freshness[data-stale=true]{flex:none}.mlv-status[data-layout=narrow] .mlv-status__coverage{flex-shrink:1000}.mlv-status[data-layout=narrow] .mlv-status__counts{flex-shrink:1}.mlv-status[data-layout=narrow]{gap:var(--mlv-s4)}.mlv-status[data-layout=narrow] .mlv-zoom__btn{display:none}.mlv-toasts{position:absolute;left:50%;bottom:var(--mlv-s7);z-index:60;display:flex;flex-direction:column;gap:var(--mlv-s3);transform:translate(-50%);pointer-events:none}.mlv-toast{min-width:240px;max-width:360px;padding:var(--mlv-s4) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);font-size:var(--mlv-fs-12);text-align:center}.mlv-state{position:absolute;inset:0;z-index:30;display:flex;align-items:center;justify-content:center;padding:var(--mlv-s9);background:var(--mlv-bg)}.mlv-state[hidden]{display:none}.mlv-state__inner{width:min(460px,100%);text-align:center}.mlv-state__title{margin:0 0 var(--mlv-s4);font-size:var(--mlv-fs-16);font-weight:var(--mlv-fw-semi)}.mlv-state__body{margin:0 0 var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12)}.mlv-state__list{margin:0 auto var(--mlv-s5);padding:0;list-style:none;text-align:left;max-width:420px;color:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-state__actions{display:flex;gap:var(--mlv-s4);justify-content:center}.mlv-btn[disabled]{opacity:.4;cursor:default}.mlv-sheet{position:absolute;inset:0;z-index:60;display:flex;align-items:center;justify-content:center}.mlv-sheet[hidden]{display:none}.mlv-sheet__scrim{position:absolute;inset:0;background:var(--mlv-scrim)}.mlv-sheet__panel{position:relative;width:min(560px,92%);max-height:min(70vh,620px);overflow-y:auto;padding:var(--mlv-s5) var(--mlv-s6) var(--mlv-s6);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3)}.mlv-sheet__head{display:flex;align-items:center;gap:var(--mlv-s4);margin-bottom:var(--mlv-s5)}.mlv-sheet__title{flex:1 1 auto;margin:0;font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi)}.mlv-sheet__list{display:grid;grid-template-columns:minmax(140px,auto) 1fr;gap:var(--mlv-s3) var(--mlv-s5);margin:0}.mlv-sheet__keys{display:flex;flex-wrap:wrap;gap:var(--mlv-s2);margin:0}.mlv-sheet__desc{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);align-self:center}.mlv-kbd{display:inline-block;padding:1px var(--mlv-s3);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);white-space:nowrap}.mlv-result__foot{display:flex;align-items:center;justify-content:space-between;gap:var(--mlv-s4);padding:var(--mlv-s3) var(--mlv-s4);border-top:1px solid var(--mlv-border);color:var(--mlv-text-3);font-size:var(--mlv-fs-10)}.mlv-result__jump{flex:none;padding:1px var(--mlv-s3);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-accent);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-medium)}.mlv-legend{position:absolute;right:var(--mlv-s5);bottom:var(--mlv-s5);z-index:45;display:flex;flex-direction:column;width:min(320px,calc(100% - 2 * var(--mlv-s5)));max-height:min(560px,calc(100% - 2 * var(--mlv-s5)));border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-shadow-panel, 0 8px 24px rgba(0, 0, 0, .18));overflow:hidden}.mlv-legend[hidden]{display:none}.mlv-legend__head{display:flex;align-items:center;justify-content:space-between;padding:var(--mlv-s4) var(--mlv-s5);border-bottom:1px solid var(--mlv-border)}.mlv-legend__title{margin:0;font-size:var(--mlv-fs-12);font-weight:var(--mlv-fw-semi)}.mlv-legend__body{overflow-y:auto;padding:var(--mlv-s4) var(--mlv-s5) var(--mlv-s5)}.mlv-legend__section+.mlv-legend__section{margin-top:var(--mlv-s5)}.mlv-legend__heading{margin:0 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);letter-spacing:var(--mlv-track-caps);text-transform:uppercase}.mlv-legend__list{display:grid;grid-template-columns:minmax(0,auto) minmax(0,1fr);gap:var(--mlv-s2) var(--mlv-s4);margin:0}.mlv-legend__term{display:flex;align-items:center;gap:var(--mlv-s3);color:var(--mlv-text);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-medium)}.mlv-legend__desc{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);line-height:var(--mlv-lh-tight)}.mlv-legend__swatch{flex:none;overflow:visible}.mlv-legend__basis{display:inline-flex;flex-direction:column;align-items:flex-start;gap:var(--mlv-s3)}.mlv-legend__desc strong{color:var(--mlv-text);font-weight:var(--mlv-fw-semi)}.mlv-legend__card{position:relative;display:inline-flex;align-items:center;min-width:44px;height:20px;padding:0 3px;box-sizing:border-box;border:1px solid var(--mlv-node-edge, var(--mlv-border));border-radius:4px;background:var(--mlv-surface)}.mlv-legend__card[data-basis=inferred]{border:1.5px dashed var(--mlv-text-2)}.mlv-legend__card[data-basis=unresolved]{border:1.5px dotted var(--mlv-text-2);background-image:repeating-linear-gradient(135deg,transparent 0 4px,color-mix(in srgb,var(--mlv-text) 8%,transparent) 4px 5px)}.mlv-legend__tag{padding:0 3px;border:1px dashed var(--mlv-text-2);border-radius:3px;background:var(--mlv-bg);color:var(--mlv-text);font-size:9px;line-height:11px;white-space:nowrap}.mlv-legend__tag[data-basis=unresolved]{border-style:dotted}.mlv-legend__phases{display:inline-flex;gap:2px}.mlv-legend__phase{width:3px;height:14px;border-radius:1.5px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-legend__short{color:var(--mlv-text);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi)}@media(max-width:900px){.mlv-legend{right:var(--mlv-s3);bottom:var(--mlv-s3);width:calc(100% - 2 * var(--mlv-s3))}}.mlv-legend__body{background-color:var(--mlv-surface);background-image:linear-gradient(to bottom,var(--mlv-surface) 30%,transparent),linear-gradient(to top,var(--mlv-surface) 30%,transparent),linear-gradient(to bottom,var(--mlv-border-strong),transparent),linear-gradient(to top,var(--mlv-border-strong),transparent);background-repeat:no-repeat;background-size:100% 24px,100% 24px,100% 8px,100% 8px;background-position:top,bottom,top,bottom;background-attachment:local,local,scroll,scroll;scrollbar-gutter:stable}.mlv-canvas{position:relative;flex:1 1 auto;min-width:0;overflow:hidden;background-color:var(--mlv-bg);background-image:radial-gradient(var(--mlv-canvas-dot) 1px,transparent 1px);background-size:20px 20px;cursor:grab;touch-action:none}.mlv-canvas[data-lod=compact]{background-image:none}.mlv-canvas.is-panning{cursor:grabbing}.mlv-world{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}.mlv-layer{position:absolute;top:0;left:0}.mlv-layer--edges{overflow:visible;pointer-events:none}.mlv-layer--nodes{pointer-events:none}.mlv-layer--nodes>*{pointer-events:auto}.mlv-lane{position:absolute;border:1px solid var(--mlv-border);border-radius:var(--mlv-r-lane);background:color-mix(in srgb,var(--mlv-text) calc(var(--mlv-lane-tint) * 100%),transparent)}.mlv-lane:before{content:"";position:absolute;top:6px;bottom:6px;left:0;width:3px;border-radius:1.5px;background:var(--mlv-stage, var(--mlv-stage-unknown));pointer-events:none}.mlv-lane__header{position:absolute;top:0;left:0;right:0;display:flex;align-items:center;gap:var(--mlv-s4);height:var(--mlv-lane-header-h, 28px);padding:0 var(--mlv-s5);border-bottom:1px solid var(--mlv-border);border-radius:calc(var(--mlv-r-lane) - 1px) calc(var(--mlv-r-lane) - 1px) 0 0;background:color-mix(in srgb,var(--mlv-text) 5%,var(--mlv-surface))}.mlv-lane__header:before{content:"";flex:none;width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-lane__num{flex:none;margin-right:calc(-1 * var(--mlv-s2));color:var(--mlv-text-2);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-lane__label{flex:0 1 auto;min-width:48px;overflow:hidden;color:var(--mlv-text);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);text-overflow:ellipsis;white-space:nowrap}.mlv-lane__count{flex:none;color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums;white-space:nowrap}.mlv-lane__spacer{flex:1 1 auto}.mlv-lane__header .mlv-cluster{flex:none}.mlv-lane__unit:before{content:"\b7 "}.mlv-lane__unit{flex:0 100 auto;min-width:0;overflow:hidden;margin-left:calc(-1 * var(--mlv-s2));color:var(--mlv-text-3);font-size:var(--mlv-fs-10);text-overflow:ellipsis;white-space:nowrap}.mlv-group{position:absolute;border:1px solid var(--mlv-border-strong);border-radius:12px;background:color-mix(in srgb,var(--mlv-text) calc(var(--mlv-group-tint) * 100%),transparent)}.mlv-group[data-basis=inferred]{border:calc(1.5px / var(--mlv-z, 1)) dashed var(--mlv-text-2)}.mlv-group[data-basis=unresolved]{border:calc(1.5px / var(--mlv-z, 1)) dotted var(--mlv-text-2)}.mlv-group__header{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;height:var(--mlv-group-header-h);padding:0 var(--mlv-s4);border:0;border-radius:12px 12px 0 0;background:none;color:var(--mlv-text);cursor:pointer;text-align:left}.mlv-group__header:hover{background:var(--mlv-surface-2)}.mlv-group__chevron{transition:transform var(--mlv-dur-2) var(--mlv-ease);color:var(--mlv-text-2)}.mlv-group.is-collapsed .mlv-group__chevron{transform:rotate(0)}.mlv-group__chevron{transform:rotate(90deg)}.mlv-group__name{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-12);font-weight:var(--mlv-fw-semi)}.mlv-group__count{flex:none;padding:0 var(--mlv-s3);border-radius:var(--mlv-r-pill);background:var(--mlv-surface-2);color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums}.mlv-group.is-dim,.mlv-lane.is-dim{opacity:.5}.mlv-canvas.is-focusing .mlv-node:not(.is-lit){opacity:.22;filter:saturate(.3);pointer-events:none}.mlv-canvas.is-focusing .mlv-edge:not(.is-lit){opacity:.16}.mlv-canvas.is-tracing .mlv-node.is-lit{box-shadow:0 0 0 max(1.5px,calc(1px / var(--mlv-z, 1))) var(--mlv-text-2)}.mlv-tooltip{position:absolute;z-index:50;max-width:320px;padding:var(--mlv-s4) var(--mlv-s5);border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3);font-size:var(--mlv-fs-12);pointer-events:none}.mlv-tooltip[hidden]{display:none}.mlv-tooltip__title{font-weight:var(--mlv-fw-semi)}.mlv-tooltip__row{margin-top:var(--mlv-s2);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-tooltip__loc{font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10);color:var(--mlv-text-3)}.mlv-zoom{display:inline-flex;align-items:center;flex:none;gap:1px}.mlv-zoom__level{min-width:38px;text-align:center;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-variant-numeric:tabular-nums}.mlv-zoom .mlv-zoom__btn{width:20px;height:18px;min-width:0;min-height:0;padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-zoom .mlv-zoom__btn:hover{background:var(--mlv-surface-2);color:var(--mlv-text)}.mlv-layer--lanes,.mlv-lane,.mlv-group{pointer-events:none}.mlv-lane__header,.mlv-group__header{pointer-events:auto}.mlv-statehost{position:absolute;inset:0;z-index:30;pointer-events:none}.mlv-statehost>*{pointer-events:auto}.mlv-group__chevron-btn{display:inline-flex;align-items:center;justify-content:center;flex:none;width:24px;height:24px;margin-left:-4px;padding:0;border:0;border-radius:var(--mlv-r-btn);background:none;color:inherit;cursor:pointer}.mlv-group__chevron-btn:hover{background:var(--mlv-surface-2)}.mlv-node{position:absolute;display:flex;align-items:stretch;gap:0;overflow:visible;border:1px solid var(--mlv-node-edge, var(--mlv-border));border-radius:var(--mlv-r-node);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1);cursor:pointer;text-align:left;padding:0;transition:box-shadow var(--mlv-dur-1) var(--mlv-ease),transform var(--mlv-dur-1) var(--mlv-ease)}.mlv-node__rail{flex:none;width:var(--mlv-rail-w);border-radius:var(--mlv-r-node) 0 0 var(--mlv-r-node);background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-node__main{display:flex;align-items:flex-start;gap:var(--mlv-s4);flex:1 1 auto;min-width:0;padding:10px var(--mlv-s5) 10px var(--mlv-s4);overflow:hidden}.mlv-node__iconbox{flex:none;display:flex;align-items:center;justify-content:center;width:26px;height:26px;border-radius:6px;background:var(--mlv-surface-2);color:var(--mlv-text-2)}.mlv-node__text{flex:1 1 auto;min-width:0}.mlv-node__title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);line-height:var(--mlv-lh-tight)}.mlv-node__sub{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);line-height:var(--mlv-lh-tight)}.mlv-node__sub[data-lines="2"]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow-wrap:anywhere;white-space:normal}.mlv-node__loc{display:flex;overflow:hidden;white-space:nowrap;color:var(--mlv-text-3);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-node__chips{display:flex;gap:var(--mlv-s2);margin-top:var(--mlv-s2);overflow:hidden}.mlv-node__chips .mlv-chip{display:inline-block;flex:0 1 auto;min-width:0;max-width:100%;overflow:hidden;text-overflow:ellipsis;line-height:18px}.mlv-node:hover{box-shadow:var(--mlv-sh-2);border-color:var(--mlv-border-strong);transform:translateY(-1px);z-index:3}.mlv-node.is-selected{box-shadow:var(--mlv-sh-2),0 0 0 2px var(--mlv-accent),0 0 0 6px color-mix(in srgb,var(--mlv-accent) 14%,transparent);z-index:4}.mlv-node.is-selected .mlv-node__title{font-weight:700}.mlv-node.has-issues{border-color:color-mix(in srgb,var(--mlv-sev) 55%,var(--mlv-node-edge, var(--mlv-border)));--mlv-basis-ink: color-mix(in srgb, var(--mlv-sev) 70%, var(--mlv-text))}.mlv-node.is-stale{border-color:var(--mlv-stale-ink);--mlv-basis-ink: var(--mlv-stale-ink)}.mlv-node[data-basis=inferred],.mlv-node[data-basis=unresolved]{border-color:transparent}.mlv-node[data-basis=inferred]:after,.mlv-node[data-basis=unresolved]:after{content:"";position:absolute;inset:-1px;border:calc(1.5px / var(--mlv-z, 1)) dashed var(--mlv-basis-ink, var(--mlv-text-2));border-radius:inherit;pointer-events:none}.mlv-node[data-basis=unresolved]:after{border-style:dotted}.mlv-node[data-basis=unresolved]{background-image:repeating-linear-gradient(135deg,transparent 0 7px,color-mix(in srgb,var(--mlv-text) 8%,transparent) 7px 8px)}.mlv-basis-tag{display:inline-flex;align-items:center;height:14px;padding:0 4px;border:1px dashed var(--mlv-basis-ink, var(--mlv-text-2));border-radius:3px;background:var(--mlv-bg);color:var(--mlv-text);font-size:10px;font-weight:var(--mlv-fw-regular);line-height:1;white-space:nowrap}.mlv-basis-tag[data-basis=unresolved]{border-style:dotted}.mlv-node>.mlv-basis-tag{position:absolute;top:100%;left:10px;z-index:5;transform-origin:0 50%;transform:translateY(-50%) scale(calc(1 / var(--mlv-z, 1)));pointer-events:none}.mlv-group__header .mlv-basis-tag{flex:none}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed]{border-color:color-mix(in srgb,var(--mlv-node-edge, var(--mlv-border)) 35%,var(--mlv-bg));background:var(--mlv-bg);box-shadow:none}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed] .mlv-node__rail{background:color-mix(in srgb,var(--mlv-stage, var(--mlv-stage-unknown)) 30%,var(--mlv-bg))}.mlv-canvas[data-exceptions=on] .mlv-node[data-basis=observed] .mlv-node__iconbox{background:transparent}.mlv-node__stale{position:absolute;top:-7px;left:-7px;z-index:5;display:flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:var(--mlv-r-pill);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1);color:var(--mlv-stale-ink)}.mlv-group__header .mlv-node__stale{position:static;box-shadow:none;flex:none}.mlv-node.is-pulse{animation:mlv-pulse .3s var(--mlv-ease) 2}@keyframes mlv-pulse{0%,to{box-shadow:var(--mlv-sh-2),0 0 0 2px var(--mlv-accent)}50%{box-shadow:var(--mlv-sh-2),0 0 0 7px color-mix(in srgb,var(--mlv-accent) 30%,transparent)}}.mlv-canvas[data-lod=compact] .mlv-node__sub,.mlv-canvas[data-lod=compact] .mlv-node__loc,.mlv-canvas[data-lod=compact] .mlv-node__chips,.mlv-canvas[data-lod=compact] .mlv-node__iconbox{display:none}.mlv-canvas[data-lod=compact] .mlv-node__main{container-type:size;align-items:center;padding:2px 8px}.mlv-canvas[data-lod=compact] .mlv-node__title{font-size:min(calc(var(--mlv-compact-title) / var(--mlv-z, 1)),calc(100cqh / 2.3));line-height:1.15}.mlv-canvas[data-lod=compact] .mlv-node>.mlv-basis-tag{transform-origin:0 0;transform:translateY(-1px) scale(calc(1 / var(--mlv-z, 1)))}.mlv-canvas[data-lod=compact] .mlv-edge-label{display:none}.mlv-glyph{display:block;overflow:visible}.mlv-glyph__shape{fill:var(--mlv-sev-marker, var(--mlv-sev-low))}.mlv-glyph__ink{fill:var(--mlv-sev-marker-ink, #FFFFFF)}.mlv-glyph--low{--mlv-sev-marker: var(--mlv-sev-low);--mlv-sev-marker-ink: var(--mlv-sev-low-ink)}.mlv-glyph--medium{--mlv-sev-marker: var(--mlv-sev-medium);--mlv-sev-marker-ink: var(--mlv-sev-medium-ink)}.mlv-glyph--high{--mlv-sev-marker: var(--mlv-sev-high);--mlv-sev-marker-ink: var(--mlv-sev-high-ink)}body.vscode-high-contrast .mlv-glyph__shape,body.vscode-high-contrast-light .mlv-glyph__shape,:root[data-theme=hc] .mlv-glyph__shape,.mlv-root[data-theme=hc] .mlv-glyph__shape{fill:none;stroke:var(--mlv-sev-marker, currentColor);stroke-width:2}body.vscode-high-contrast .mlv-glyph__ink,body.vscode-high-contrast-light .mlv-glyph__ink,:root[data-theme=hc] .mlv-glyph__ink,.mlv-root[data-theme=hc] .mlv-glyph__ink{fill:var(--mlv-sev-marker, currentColor)}.mlv-badge{position:absolute;top:-6px;right:-6px;z-index:5;display:flex;align-items:center;gap:var(--mlv-s1);height:20px;padding:0 var(--mlv-s2);border-radius:var(--mlv-r-pill);background:var(--mlv-surface);box-shadow:var(--mlv-sh-1)}.mlv-badge--high{border:1px solid color-mix(in srgb,var(--mlv-sev-high) 45%,transparent)}.mlv-badge--medium{border:1px solid color-mix(in srgb,var(--mlv-sev-medium) 45%,transparent)}.mlv-badge--low{border:1px solid color-mix(in srgb,var(--mlv-sev-low) 45%,transparent)}.mlv-badge__count,.mlv-badge__ids{color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-badge__ids{padding-right:2px;color:var(--mlv-text);white-space:nowrap}.mlv-cluster{display:inline-flex;align-items:center;gap:var(--mlv-s3)}.mlv-cluster__item{display:inline-flex;align-items:center;gap:2px}.mlv-cluster__count{color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-variant-numeric:tabular-nums}[data-stage]{--mlv-stage: var(--mlv-stage-unknown)}[data-phase-tone="0"]{--mlv-stage: var(--mlv-phase-0)}[data-phase-tone="1"]{--mlv-stage: var(--mlv-phase-1)}[data-phase-tone="2"]{--mlv-stage: var(--mlv-phase-2)}[data-phase-tone="3"]{--mlv-stage: var(--mlv-phase-3)}[data-phase-tone="4"]{--mlv-stage: var(--mlv-phase-4)}[data-phase-tone="5"]{--mlv-stage: var(--mlv-phase-5)}[data-phase-tone="6"]{--mlv-stage: var(--mlv-phase-6)}[data-phase-tone="7"]{--mlv-stage: var(--mlv-phase-7)}[data-sev=high]{--mlv-sev: var(--mlv-sev-high)}[data-sev=medium]{--mlv-sev: var(--mlv-sev-medium)}[data-sev=low]{--mlv-sev: var(--mlv-sev-low)}.mlv-node:focus-visible,.mlv-group__header:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px;border-radius:var(--mlv-r-node)}.mlv-node__title--wrap{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1;line-clamp:1;white-space:normal;overflow-wrap:anywhere;text-overflow:clip}.mlv-node__title--wrap[data-lines="2"]{-webkit-line-clamp:2;line-clamp:2}.mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3;line-clamp:3}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap{-webkit-line-clamp:2;line-clamp:2}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3;line-clamp:3;font-size:min(calc(var(--mlv-compact-title) / var(--mlv-z, 1)),calc(100cqh / 3.45))}.mlv-edge{pointer-events:none}.mlv-edge__hit{fill:none;stroke:transparent;stroke-width:12;pointer-events:stroke;cursor:pointer}.mlv-edge__path,.mlv-legend__edge{fill:none;stroke:var(--mlv-edge);stroke-width:max(1.5px,calc(1px / var(--mlv-z, 1)));stroke-linecap:round;stroke-linejoin:round;transition:stroke var(--mlv-dur-1) var(--mlv-ease)}.mlv-edge[data-basis=inferred] .mlv-edge__path,.mlv-edge[data-basis=inferred] .mlv-legend__edge{stroke-dasharray:calc(6px / var(--mlv-z, 1)) calc(4px / var(--mlv-z, 1));stroke-linecap:butt}.mlv-edge[data-basis=unresolved] .mlv-edge__path,.mlv-edge[data-basis=unresolved] .mlv-legend__edge{stroke-dasharray:calc(1.5px / var(--mlv-z, 1)) calc(3.5px / var(--mlv-z, 1));stroke-linecap:round}.mlv-arrow{fill:var(--mlv-edge);stroke:none}.mlv-edge.is-hover .mlv-edge__path,.mlv-edge.is-lit .mlv-edge__path{stroke:var(--mlv-text-2);stroke-width:max(2px,calc(1.5px / var(--mlv-z, 1)))}.mlv-edge.is-selected .mlv-edge__path{stroke:var(--mlv-accent);stroke-width:max(2.5px,calc(2px / var(--mlv-z, 1)))}.mlv-edge.has-issue .mlv-edge__path{stroke:var(--mlv-sev, var(--mlv-edge));opacity:.85}.mlv-edge-label{font-family:var(--mlv-font-mono);font-size:10px;fill:var(--mlv-text-2);paint-order:stroke;stroke:var(--mlv-bg);stroke-width:3;stroke-linejoin:round;text-anchor:middle;dominant-baseline:middle;opacity:0;transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-edge.is-hover .mlv-edge-label,.mlv-edge.is-selected .mlv-edge-label,.mlv-edge.is-lit .mlv-edge-label,.mlv-edge.has-label .mlv-edge-label,.mlv-edge--merged .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--data .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--control .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--state .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--loop .mlv-edge-label,.mlv-canvas[data-lod=full] .mlv-edge--output .mlv-edge-label{opacity:1}.mlv-canvas[data-lod=compact] .mlv-edge-label{opacity:0}.mlv-edge-marker__disc{fill:var(--mlv-surface);stroke:var(--mlv-sev-marker, var(--mlv-sev-low));stroke-width:1}.mlv-edge-marker--high{--mlv-sev-marker: var(--mlv-sev-high)}.mlv-edge-marker--medium{--mlv-sev-marker: var(--mlv-sev-medium)}.mlv-edge-marker--low{--mlv-sev-marker: var(--mlv-sev-low)}.mlv-connector{fill:none;stroke:var(--mlv-sev, var(--mlv-accent));stroke-width:1.5;stroke-dasharray:2 4}.mlv-connector__badge{fill:var(--mlv-surface);stroke:var(--mlv-sev, var(--mlv-accent));stroke-width:1}.mlv-connector__label{font-family:var(--mlv-font-mono);font-size:9px;fill:var(--mlv-text);text-anchor:middle;dominant-baseline:central}.mlv-bundles{pointer-events:none}.mlv-bundle__trunk,.mlv-bundle__spur{fill:none;stroke:var(--mlv-edge);stroke-linecap:round;stroke-linejoin:round;transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-bundle__trunk{stroke-width:3.5;opacity:.72}.mlv-bundle__spur{stroke-width:1.5;opacity:.45}.mlv-bundle.has-issue .mlv-bundle__trunk,.mlv-bundle.has-issue .mlv-bundle__spur{stroke:var(--mlv-sev, var(--mlv-edge))}.mlv-bundle__badge-box{fill:var(--mlv-surface);stroke:var(--mlv-edge);stroke-width:1;opacity:.95}.mlv-bundle.has-issue .mlv-bundle__badge-box{stroke:var(--mlv-sev, var(--mlv-edge))}.mlv-bundle__badge-text{font-family:var(--mlv-font-mono);font-size:10px;fill:var(--mlv-text-2);text-anchor:middle;dominant-baseline:central}.mlv-bundle__badge{transition:opacity var(--mlv-dur-1) var(--mlv-ease)}.mlv-bundle.is-expanded .mlv-bundle__trunk,.mlv-bundle.is-expanded .mlv-bundle__spur,.mlv-bundle.is-expanded .mlv-bundle__badge,.mlv-canvas .mlv-edge.is-bundled .mlv-edge__path,.mlv-canvas .mlv-edge.is-bundled .mlv-edge-label{opacity:0}.mlv-canvas .mlv-edge.is-bundled[data-basis=inferred] .mlv-edge__path,.mlv-canvas .mlv-edge.is-bundled[data-basis=unresolved] .mlv-edge__path{opacity:1}.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=observed]:not(.is-hover):not(.is-selected):not(.is-lit) .mlv-edge__path{stroke-opacity:.3}.mlv-canvas[data-exceptions=on] .mlv-bundle__trunk,.mlv-canvas[data-exceptions=on] .mlv-bundle__spur{stroke-opacity:.3}.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=inferred] .mlv-edge__path,.mlv-canvas[data-exceptions=on] .mlv-edge[data-basis=unresolved] .mlv-edge__path{stroke:var(--mlv-text)}.mlv-canvas[data-lod=compact] .mlv-bundle__badge{opacity:0}.mlv-edge__stalebg{fill:var(--mlv-surface);stroke:var(--mlv-stale-ink);stroke-width:1}.mlv-edge__staleicon{stroke:var(--mlv-stale-ink);stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}.mlv-edge__stale{pointer-events:none}.mlv-edge[data-stage]:not(.has-issue){--mlv-flow-color: var(--mlv-stage)}.mlv-edge.has-issue{--mlv-flow-color: var(--mlv-sev)}body.vscode-high-contrast .mlv-edge[data-stage],body.vscode-high-contrast .mlv-edge.has-issue,body.vscode-high-contrast-light .mlv-edge[data-stage],body.vscode-high-contrast-light .mlv-edge.has-issue,:root[data-theme=hc] .mlv-edge[data-stage],:root[data-theme=hc] .mlv-edge.has-issue,.mlv-root[data-theme=hc] .mlv-edge[data-stage],.mlv-root[data-theme=hc] .mlv-edge.has-issue{--mlv-flow-color: var(--mlv-text)}.mlv-edge__flow{fill:none;stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:var(--mlv-flow-width, 3px);stroke-linecap:round;pointer-events:none}.mlv-edge.is-flowing--pulse .mlv-edge__flow{stroke-dasharray:none;stroke-opacity:var(--mlv-flow-underlay-opacity, .28)}.mlv-edge__charge{pointer-events:none;animation:mlv-charge-ends var(--mlv-flow-dur, .9s) linear infinite;animation-iteration-count:var(--mlv-flow-iter, infinite);animation-fill-mode:forwards}.mlv-edge__charge--twin{animation-delay:calc(var(--mlv-flow-dur, .9s) / -2)}@keyframes mlv-charge-ends{0%{opacity:0}8%{opacity:1}92%{opacity:1}to{opacity:0}}.mlv-edge__charge-halo{fill:var(--mlv-flow-color, var(--mlv-accent));fill-opacity:var(--mlv-flow-halo-opacity, .18);stroke:none}.mlv-edge__charge-glow{fill:var(--mlv-flow-color, var(--mlv-accent));fill-opacity:var(--mlv-flow-glow-opacity, .42);stroke:none}.mlv-edge__charge-core{fill:var(--mlv-surface);stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:var(--mlv-flow-core-width, 1.6)}body.vscode-high-contrast .mlv-edge__charge-halo,body.vscode-high-contrast-light .mlv-edge__charge-halo,:root[data-theme=hc] .mlv-edge__charge-halo,.mlv-root[data-theme=hc] .mlv-edge__charge-halo{fill:none;stroke:var(--mlv-text);stroke-width:1.2}.mlv-edge.is-flowing .mlv-edge__flow{stroke-dasharray:var(--mlv-flow-head) var(--mlv-flow-gap);animation:mlv-flow-stream var(--mlv-flow-dur-data) linear infinite;animation-delay:var(--mlv-flow-delay, 0ms);animation-iteration-count:var(--mlv-flow-iter, infinite)}.mlv-edge--call.is-flowing .mlv-edge__flow,.mlv-edge--control.is-flowing .mlv-edge__flow{animation-duration:var(--mlv-flow-dur-call)}.mlv-edge--control.mlv-edge--back.is-flowing .mlv-edge__flow{animation-duration:var(--mlv-flow-dur-back)}@keyframes mlv-flow-stream{0%{stroke-dashoffset:0}to{stroke-dashoffset:var(--mlv-flow-end, -20)}}.mlv-edge__port{stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:1.4;pointer-events:none}.mlv-edge__port--out{fill:var(--mlv-surface)}.mlv-edge__port--in{fill:var(--mlv-flow-color, var(--mlv-accent));stroke:var(--mlv-surface);stroke-width:1.4}.mlv-canvas[data-flow=motion] .mlv-edge.is-flowing:not(.is-flow-settled) .mlv-edge__path,.mlv-canvas[data-flow=motion] .mlv-edge.is-flowing--pulse:not(.is-flow-settled) .mlv-edge__path{marker-end:none}.mlv-canvas[data-flow=static] .mlv-edge.is-flowing--pulse .mlv-edge__path,.mlv-edge.is-flowing--pulse.is-flow-settled .mlv-edge__path{stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:max(2.5px,calc(2px / var(--mlv-z, 1)))}.mlv-edge__dir{fill:none;stroke:var(--mlv-flow-color, var(--mlv-accent));stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round;pointer-events:none}.mlv-node.is-flow-source,.mlv-group.is-flow-source{box-shadow:0 0 0 2px var(--mlv-stage, var(--mlv-accent))}.mlv-node.is-flow-target,.mlv-group.is-flow-target{box-shadow:0 0 0 2px var(--mlv-surface),0 0 0 4px var(--mlv-accent)}@media(prefers-reduced-motion:reduce){.mlv-edge__flow,.mlv-edge__charge{display:none!important}}body.vscode-reduce-motion .mlv-edge__flow,body.vscode-reduce-motion .mlv-edge__charge,body.vscode-using-screen-reader .mlv-edge__flow,body.vscode-using-screen-reader .mlv-edge__charge{display:none!important}.mlv-canvas[data-flow=off] .mlv-edge__flow,.mlv-canvas[data-flow=off] .mlv-edge__charge,.mlv-canvas[data-flow=off] .mlv-edge__port,.mlv-canvas[data-flow=off] .mlv-edge__dir{display:none}.mlv-rail{position:relative;display:flex;flex-direction:column;flex:none;width:var(--mlv-rail-width);min-width:280px;max-width:560px;border-left:1px solid var(--mlv-border);background:var(--mlv-surface)}.mlv-rail[hidden]{display:none}.mlv-rail__grip{position:absolute;top:0;left:-2px;width:5px;height:100%;cursor:col-resize;z-index:5}.mlv-rail__grip:after{content:"";position:absolute;left:2px;top:0;width:1px;height:100%;background:transparent}.mlv-rail__grip:hover:after{background:var(--mlv-accent)}.mlv-rail__bar{display:flex;align-items:stretch;flex:none;border-bottom:1px solid var(--mlv-border)}.mlv-rail__tabs{display:flex;flex:1 1 auto;min-width:0;height:36px}.mlv-rail__tab{flex:1 1 auto;min-width:0;padding:0 var(--mlv-s3);white-space:nowrap;border:0;border-bottom:2px solid transparent;background:none;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);cursor:pointer}.mlv-rail__tab[aria-selected=true]{color:var(--mlv-text);border-bottom-color:var(--mlv-accent);font-weight:var(--mlv-fw-semi)}.mlv-rail__tab:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__tabcount{color:var(--mlv-text-2);font-variant-numeric:tabular-nums}.mlv-rail__chevron{flex:none;align-self:center;width:26px;height:26px;min-height:0;margin:0 var(--mlv-s2);padding:0;border-color:transparent;background:transparent;color:var(--mlv-text-2)}.mlv-rail__chevron[hidden]{display:none}.mlv-rail__chevron .mlv-uicon{transform:rotate(90deg)}.mlv-rail__chevron[aria-expanded=false] .mlv-uicon{transform:rotate(-90deg)}.mlv-rail__panel:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__count{color:var(--mlv-text-3);font-weight:var(--mlv-fw-regular);text-transform:none;letter-spacing:0}.mlv-body[data-rail=sheet]{flex-direction:column}.mlv-body[data-rail=sheet]>.mlv-main{flex:1 1 0;min-height:0}.mlv-rail[data-mode=sheet]{flex:0 0 calc(var(--mlv-sheet-fraction, .47) * 100%);width:auto;min-width:0;max-width:none;min-height:32px;max-height:calc(100% - min(240px,45%));border-left:0;border-top:1px solid var(--mlv-border-strong)}.mlv-rail[data-mode=sheet][data-expanded=false]{flex-basis:32px;height:32px}.mlv-rail[data-mode=sheet] .mlv-rail__tabs{height:31px}.mlv-rail[data-mode=sheet][data-expanded=false] .mlv-rail__bar{border-bottom:0}.mlv-rail[data-mode=sheet]>.mlv-rail__grip{top:-4px;left:0;width:100%;height:9px;cursor:row-resize}.mlv-rail[data-mode=sheet]>.mlv-rail__grip:after{left:50%;top:2px;width:36px;height:4px;margin-left:-18px;border-radius:2px;background:var(--mlv-border-strong)}.mlv-rail[data-mode=sheet]>.mlv-rail__grip:hover:after,.mlv-rail[data-mode=sheet]>.mlv-rail__grip:focus-visible:after{background:var(--mlv-accent)}.mlv-rail__grip:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-rail__panel{flex:1 1 auto;min-height:0;overflow-y:auto;padding:var(--mlv-s4)}.mlv-rail__panel[hidden]{display:none}.mlv-rail__section{margin:0 0 var(--mlv-s5)}.mlv-rail__heading{display:flex;align-items:center;gap:var(--mlv-s3);margin:0 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);text-transform:uppercase;letter-spacing:var(--mlv-track-caps)}.mlv-issues{list-style:none;margin:0;padding:0}.mlv-issues__item{display:flex;flex-wrap:wrap;align-items:flex-start}.mlv-issue{display:flex;align-items:flex-start;gap:var(--mlv-s4);flex:1 1 0;min-width:0;padding:var(--mlv-s4);border:1px solid transparent;border-radius:var(--mlv-r-btn);background:none;cursor:pointer;text-align:left}.mlv-issue:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:-2px}.mlv-issue:hover{background:var(--mlv-surface-2)}.mlv-issue.is-selected{border-color:var(--mlv-accent);background:var(--mlv-surface-2)}.mlv-issue__text{flex:1 1 auto;min-width:0}.mlv-issue__short,.mlv-insp__short{flex:none;color:var(--mlv-text);font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-issue__title{font-size:12.5px;font-weight:var(--mlv-fw-medium);line-height:var(--mlv-lh-tight)}.mlv-issue__meta{display:flex;align-items:center;gap:var(--mlv-s3);margin-top:var(--mlv-s1);color:var(--mlv-text-3);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-10)}.mlv-issue__open{flex:none;align-self:center}.mlv-issue__detail{flex:1 0 100%}.mlv-empty-note{padding:var(--mlv-s5);color:var(--mlv-text-2);font-size:var(--mlv-fs-12);text-align:center}.mlv-sel[data-columns="2"]{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);column-gap:var(--mlv-s7, 20px);align-items:start}.mlv-sel__col{min-width:0}.mlv-insp__eyebrow{display:flex;flex-wrap:wrap;align-items:center;gap:0 var(--mlv-s2);margin:0 0 var(--mlv-s2);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__phase{display:inline-flex;align-items:center;gap:var(--mlv-s2);color:var(--mlv-text);font-weight:var(--mlv-fw-medium)}.mlv-insp__phasedot{width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-insp__sep{color:var(--mlv-text-3)}.mlv-insp__severity{color:var(--mlv-text)}.mlv-insp__title{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi);line-height:var(--mlv-lh-tight);overflow-wrap:anywhere}.mlv-insp__ends{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-12);overflow-wrap:anywhere}.mlv-insp__section{margin:var(--mlv-s5) 0 0}.mlv-sel__col--evidence>.mlv-insp__section:first-child{margin-top:0}.mlv-insp__issue-title{flex:0 1 auto;min-width:0;color:var(--mlv-text);text-align:left;overflow-wrap:anywhere}.mlv-insp__issue-head .mlv-mono{color:var(--mlv-text-3)}.mlv-insp__issue-id{margin:calc(-1 * var(--mlv-s2)) 0 var(--mlv-s3);color:var(--mlv-text-3);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-insp__note,.mlv-insp__aside{color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__links,.mlv-insp__sentences{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--mlv-s2);font-size:var(--mlv-fs-12);line-height:1.45}.mlv-insp__flow-basis{color:var(--mlv-text-2)}.mlv-quote{padding:var(--mlv-s3) 0 0;border-top:1px solid var(--mlv-border)}.mlv-quote__head{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--mlv-s1) var(--mlv-s3);font-size:var(--mlv-fs-11)}.mlv-quote__num{flex:none;min-width:1.4em;color:var(--mlv-text-2);font-variant-numeric:tabular-nums;font-weight:var(--mlv-fw-semi)}.mlv-quote__loc{flex:1 0 calc(100% - 1.4em - var(--mlv-s3));min-width:0;color:var(--mlv-text);font-family:var(--mlv-font-mono);overflow-wrap:anywhere}.mlv-quote__loc+*{margin-left:calc(1.4em + var(--mlv-s3))}.mlv-quote__at{color:var(--mlv-text-2)}.mlv-quote__role{color:var(--mlv-text-2);font-style:italic}.mlv-quote__fresh.is-muted{color:var(--mlv-text-2)}.mlv-quote__fresh.mlv-insp__stale{margin-left:0}.mlv-quote__open{flex:none}.mlv-quote__text{margin:var(--mlv-s2) 0 0;padding:var(--mlv-s2) var(--mlv-s3);border-radius:var(--mlv-r-chip);background:var(--mlv-surface-2);color:var(--mlv-text);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);line-height:1.5;overflow-x:auto;white-space:pre;user-select:text}.mlv-quote__line{display:inline}.mlv-quote__ln{display:inline-block;min-width:3ch;margin-right:var(--mlv-s3);color:var(--mlv-text-3);text-align:right;user-select:none}.mlv-insp__evidence-nav{display:flex;gap:var(--mlv-s2);margin:0 0 var(--mlv-s3)}.mlv-about__section{margin:0 0 var(--mlv-s6);font-size:var(--mlv-fs-12);line-height:1.5}.mlv-about__question{margin:0;color:var(--mlv-text);font-size:13px;white-space:pre-wrap;overflow-wrap:anywhere}.mlv-about__question[data-clamped=true]{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:4;overflow:hidden}.mlv-about__more{margin-top:var(--mlv-s2);font-size:var(--mlv-fs-11)}.mlv-about__para,.mlv-about__text,.mlv-about__status{margin:0 0 var(--mlv-s3);color:var(--mlv-text);white-space:pre-wrap;overflow-wrap:anywhere}.mlv-about__lead{font-weight:var(--mlv-fw-semi)}.mlv-about__meta,.mlv-about__note{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__kv{padding:0 3px;border-radius:3px;background:var(--mlv-surface-2);font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__limitations{color:var(--mlv-text)}.mlv-about__limitations summary{cursor:pointer;color:var(--mlv-text);font-weight:var(--mlv-fw-medium)}.mlv-about__limitations ul{margin:var(--mlv-s2) 0 0;padding-left:20px}.mlv-about__limitations li{margin-bottom:var(--mlv-s2);overflow-wrap:anywhere}.mlv-about__files{list-style:none;margin:0 0 var(--mlv-s3);padding:0}.mlv-about__file{display:flex;flex-wrap:wrap;align-items:baseline;gap:0 var(--mlv-s3);padding:2px 0}.mlv-about__path{flex:1 1 auto;min-width:0;font-family:var(--mlv-font-mono);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-about__file .mlv-quote__fresh{flex:none;font-size:var(--mlv-fs-11)}.mlv-about__trust{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__issue{margin-bottom:var(--mlv-s5);padding:var(--mlv-s4);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface-2)}.mlv-insp__issue-head{display:flex;align-items:center;gap:var(--mlv-s3);margin-bottom:var(--mlv-s3);font-weight:var(--mlv-fw-medium)}.mlv-insp__line{margin:0 0 var(--mlv-s3);font-size:var(--mlv-fs-11);color:var(--mlv-text-2)}.mlv-insp__fix{margin:0 0 var(--mlv-s3);padding-left:var(--mlv-s4);border-left:3px solid var(--mlv-accent);font-size:var(--mlv-fs-11)}.mlv-insp__fix-label{margin:0 0 2px;color:var(--mlv-text-2);font-size:var(--mlv-fs-10);font-weight:var(--mlv-fw-semi);letter-spacing:.04em;text-transform:uppercase}.mlv-insp__fix-text{margin:0;color:var(--mlv-text);white-space:pre-wrap;overflow-wrap:anywhere}.mlv-insp__detail{margin:0 0 var(--mlv-s4);color:var(--mlv-text);font-size:var(--mlv-fs-12);line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere;user-select:text}.mlv-insp__basis{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__basis .mlv-basis-tag{vertical-align:1px}.mlv-insp__caption{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__limits{margin:var(--mlv-s4) 0 0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-insp__related{list-style:none;margin:var(--mlv-s3) 0 0;padding:0;display:flex;flex-direction:column;gap:var(--mlv-s2)}.mlv-outline,.mlv-outline ul{list-style:none;margin:0;padding:0 0 0 var(--mlv-s5)}.mlv-outline{padding-left:0}.mlv-outline__item{outline:none}.mlv-outline__item:focus>.mlv-outline__row{box-shadow:0 0 0 2px var(--mlv-focus)}.mlv-outline__row{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;padding:2px var(--mlv-s3);border:0;border-radius:var(--mlv-r-chip);background:none;cursor:pointer;text-align:left;font-size:var(--mlv-fs-12)}.mlv-outline__row--lane{font-weight:var(--mlv-fw-semi)}.mlv-outline__chevron{flex:none;width:12px;height:12px;color:var(--mlv-text-3);transform:rotate(90deg);transition:transform var(--mlv-dur-1, .12s) ease}.mlv-outline__item[aria-expanded=false]>.mlv-outline__row .mlv-outline__chevron{transform:rotate(0)}.mlv-outline__chevron--none{transform:none}.mlv-outline__row:hover{background:var(--mlv-surface-2)}.mlv-outline__row.is-selected{background:var(--mlv-surface-2);font-weight:var(--mlv-fw-medium)}.mlv-outline__label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-outline__stage{color:var(--mlv-text-3);font-size:var(--mlv-fs-10);text-transform:uppercase;letter-spacing:var(--mlv-track-caps)}.mlv-outline__row--lane{flex-wrap:wrap}.mlv-outline__row--lane .mlv-outline__label{flex:1 1 0;min-width:0}.mlv-outline__phasecount{flex:1 0 100%;font-weight:var(--mlv-fw-regular);text-transform:none;letter-spacing:normal}.mlv-basis-tag--rail{flex:none;vertical-align:middle}.mlv-relations{margin-top:var(--mlv-s6);padding-top:var(--mlv-s4);border-top:1px solid var(--mlv-border)}.mlv-relations__context{margin:0 0 var(--mlv-s3);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-relations__views{display:flex;gap:var(--mlv-s2);margin-bottom:var(--mlv-s3)}.mlv-relations__views .mlv-btn[aria-pressed=true]{border-color:var(--mlv-accent);color:var(--mlv-text)}.mlv-relations__list{margin:0;padding:0;list-style:none}.mlv-relations__item+.mlv-relations__item{margin-top:var(--mlv-s2)}.mlv-relations__row{display:block;width:100%;min-height:32px;padding:var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-btn);background:var(--mlv-surface-2);color:var(--mlv-text);text-align:left;cursor:pointer}.mlv-relations__row:focus-visible{outline:2px solid var(--mlv-focus);outline-offset:2px}.mlv-relations__path,.mlv-relations__detail{display:block}.mlv-relations__detail{margin-top:var(--mlv-s1);color:var(--mlv-text-3);font-size:var(--mlv-fs-10)}.mlv-insp__actions{display:flex;gap:var(--mlv-s3);flex-wrap:wrap;margin-bottom:var(--mlv-s4)}.mlv-clean__title{color:var(--mlv-text);font-size:var(--mlv-fs-14);font-weight:var(--mlv-fw-semi)}.mlv-clean__detail{font-size:var(--mlv-fs-12);max-width:34ch}.mlv-issue__detail{padding:var(--mlv-s3) var(--mlv-s5) var(--mlv-s5) var(--mlv-s8);border-left:2px solid var(--mlv-accent);margin:0 var(--mlv-s3) var(--mlv-s3);background:var(--mlv-surface-2);border-radius:0 var(--mlv-r-btn) var(--mlv-r-btn) 0}.mlv-issue__detail .mlv-insp__line{font-size:var(--mlv-fs-12)}.mlv-issue__goto{display:flex;flex-wrap:wrap;gap:var(--mlv-s2);margin-top:var(--mlv-s4)}.mlv-empty-note .mlv-btn{margin-top:var(--mlv-s4)}.mlv-insp__issue.is-selected{box-shadow:0 0 0 2px var(--mlv-accent)}.mlv-issue__meta{flex-wrap:wrap;row-gap:var(--mlv-s1);min-width:0}.mlv-issue__meta .mlv-chip{max-width:100%}.mlv-phaseindex{position:absolute;right:12px;bottom:12px;z-index:20;display:flex;flex-direction:column;align-items:flex-end;gap:4px;max-width:calc(100% - 24px);max-height:calc(100% - 24px);pointer-events:none}.mlv-phaseindex[hidden]{display:none}.mlv-phaseindex__panel,.mlv-phaseindex__pill{pointer-events:auto}.mlv-phaseindex__panel{display:flex;flex-direction:column;width:max-content;min-width:min(288px,100%);max-width:min(360px,100%);min-height:0;padding:4px 0;border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-btn);background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);overflow:hidden}.mlv-phaseindex__panel[hidden]{display:none}.mlv-phaseindex__head{display:flex;align-items:center;gap:var(--mlv-s3);flex:none;height:26px;padding:0 4px 0 10px;color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-phaseindex__title{color:var(--mlv-text);font-weight:var(--mlv-fw-semi)}.mlv-phaseindex__total{flex:1 1 auto;font-variant-numeric:tabular-nums}.mlv-phaseindex__fold{flex:none;width:22px;height:22px;min-height:0;padding:0;color:var(--mlv-text-2)}.mlv-phaseindex__fold .mlv-uicon{transform:rotate(90deg)}.mlv-phaseindex__rows{flex:1 1 auto;min-height:0;margin:0;padding:0;overflow-y:auto;list-style:none}.mlv-phaseindex__row{display:flex;align-items:center;gap:var(--mlv-s3);width:100%;height:24px;padding:0 10px 0 7px;border:0;border-left:3px solid transparent;background:none;color:var(--mlv-text);font:inherit;font-size:var(--mlv-fs-12);text-align:left;cursor:pointer}.mlv-phaseindex__row:hover{background:var(--mlv-surface-2)}.mlv-phaseindex__row:focus-visible{outline:1px solid var(--mlv-focus);outline-offset:-1px}.mlv-phaseindex__row[data-in-view=true]{border-left-color:var(--mlv-accent);background:color-mix(in srgb,var(--mlv-accent) 12%,transparent)}.mlv-phaseindex__num,.mlv-phaseindex__pillnum{flex:none;color:var(--mlv-text-2);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-phaseindex__num{min-width:14px}.mlv-phaseindex__row:before{content:"";flex:none;width:8px;height:8px;border-radius:2px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-phaseindex__label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-phaseindex__sev{flex:none}.mlv-phaseindex__steps{flex:none;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);font-variant-numeric:tabular-nums}.mlv-phaseindex__pill{display:flex;align-items:center;gap:var(--mlv-s3);max-width:min(360px,100%);height:28px;padding:0 8px 0 10px;border:1px solid var(--mlv-border-strong);border-radius:14px;background:var(--mlv-surface);box-shadow:var(--mlv-sh-2);color:var(--mlv-text);font:inherit;font-size:var(--mlv-fs-12);cursor:pointer}.mlv-phaseindex__pill[hidden]{display:none}.mlv-phaseindex__pill:hover{background:var(--mlv-surface-2)}.mlv-phaseindex__pill:focus-visible{outline:1px solid var(--mlv-focus);outline-offset:1px}.mlv-phaseindex__pilllabel{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mlv-phaseindex__pillchev{flex:none;color:var(--mlv-text-2);transform:rotate(-90deg)}.mlv-phaseindex[data-open=true] .mlv-phaseindex__pillchev{transform:rotate(90deg)}.mlv-phaseindex[data-form=pill] .mlv-phaseindex__panel{max-height:calc(100% - 32px)}.mlv-phaseindex[data-form=pill] .mlv-phaseindex__fold{display:none}.mlv-overview{position:absolute;inset:0;z-index:35;overflow:auto;overscroll-behavior:contain;touch-action:pan-x pan-y;background:var(--mlv-bg);cursor:default}.mlv-overview[hidden]{display:none}.mlv-overview__content{position:relative}.mlv-overview__header{position:relative;z-index:2;box-sizing:border-box;background:var(--mlv-bg)}.mlv-overview__header[data-sticky=true]{position:sticky;top:0;box-shadow:0 1px 0 var(--mlv-border)}.mlv-overview__top{display:flex;align-items:center;gap:var(--mlv-s4);height:24px}.mlv-overview__title{flex:1 1 auto;min-width:0;margin:0;overflow:hidden;color:var(--mlv-text);font-size:var(--mlv-fs-13);font-weight:var(--mlv-fw-semi);text-overflow:ellipsis;white-space:nowrap}.mlv-overview__back{display:inline-flex;align-items:center;gap:var(--mlv-s2);flex:none;height:22px;min-height:0;padding:0 6px;color:var(--mlv-text);font-size:var(--mlv-fs-12)}.mlv-overview__kbd{padding:0 4px;border:1px solid var(--mlv-border-strong);border-radius:3px;color:var(--mlv-text-2);font-family:inherit;font-size:var(--mlv-fs-10)}.mlv-overview__summary,.mlv-overview__key{margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);line-height:17px}.mlv-overview__summary{color:var(--mlv-text)}.mlv-overview__links{position:absolute;top:0;left:0;overflow:visible;pointer-events:none}.mlv-overview__arrow,.mlv-overview__bracket{fill:none;stroke:var(--mlv-edge, var(--mlv-text-2));stroke-width:1.5}.mlv-overview__arrow{stroke-width:2}.mlv-overview__bracket[data-back=true]{stroke-dasharray:5 3}.mlv-overview__head{fill:var(--mlv-edge, var(--mlv-text-2))}.mlv-overview__arrowlabel{position:absolute;transform:translateY(-50%);color:var(--mlv-text-2);font-size:var(--mlv-fs-12);white-space:nowrap;pointer-events:none}.mlv-overview__count{position:absolute;min-width:14px;padding:0 3px;border:1px solid var(--mlv-border-strong);border-radius:7px;background:var(--mlv-bg);color:var(--mlv-text);font-size:var(--mlv-fs-10);line-height:13px;text-align:center;font-variant-numeric:tabular-nums;transform:translate(-50%,-50%)}.mlv-overview__count[data-back=true]{border-style:dashed}.mlv-overview__blocks,.mlv-overview__item{margin:0;padding:0;list-style:none}.mlv-ovblock{position:absolute;box-sizing:border-box;overflow:hidden;border:1px solid color-mix(in srgb,var(--mlv-stage, var(--mlv-text-2)) 45%,var(--mlv-border-strong));border-radius:8px;background:color-mix(in srgb,var(--mlv-stage, var(--mlv-text-2)) 7%,var(--mlv-surface));cursor:pointer}.mlv-ovblock:before{content:"";position:absolute;top:0;bottom:0;left:0;width:4px;background:var(--mlv-stage, var(--mlv-stage-unknown))}.mlv-ovblock:hover{border-color:var(--mlv-stage, var(--mlv-border-strong))}.mlv-ovblock:focus-visible,.mlv-ovblock:focus{outline:2px solid var(--mlv-focus);outline-offset:1px}.mlv-ovblock__head{display:flex;align-items:center;gap:var(--mlv-s5);box-sizing:border-box;padding:0 12px 0 16px}.mlv-ovblock[data-head=two-lines] .mlv-ovblock__head{flex-direction:column;align-items:stretch;justify-content:center;gap:2px}.mlv-ovblock[data-head=two-lines] .mlv-ovblock__name{flex:none}.mlv-ovblock__name{display:flex;align-items:baseline;gap:var(--mlv-s3);flex:1 1 auto;min-width:0}.mlv-ovblock__num{flex:none;color:var(--mlv-text-2);font-size:15px;font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-ovblock__label{min-width:0;overflow:hidden;color:var(--mlv-text);font-size:15px;font-weight:var(--mlv-fw-semi);line-height:20px;text-overflow:ellipsis;white-space:nowrap}.mlv-ovblock__meta{display:flex;align-items:center;gap:var(--mlv-s3);flex:0 1 auto;min-width:0;overflow:hidden;color:var(--mlv-text-2);font-size:var(--mlv-fs-12);line-height:18px;white-space:nowrap}.mlv-ovblock__steps,.mlv-ovblock__meta .mlv-cluster{flex:none}.mlv-ovblock__unit{min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-ovblock__unit:before{content:"\b7 "}.mlv-ovblock__list{margin:0;padding:0;list-style:none}.mlv-ovitem{position:absolute;display:flex;align-items:center;gap:2px;box-sizing:border-box;height:18px;color:var(--mlv-text);font-size:12.5px;line-height:18px;white-space:nowrap}.mlv-ovitem__mark{flex:none;width:14px;color:var(--mlv-text-2);text-align:center}.mlv-ovitem[data-basis] .mlv-ovitem__mark{color:var(--mlv-text);font-weight:var(--mlv-fw-semi)}.mlv-ovmark{display:inline-block;box-sizing:border-box;width:9px;height:9px;border:1.5px dotted currentColor;border-radius:50%;vertical-align:-1px}.mlv-overview__key .mlv-ovmark{margin-right:3px}.mlv-ovitem__title{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis}.mlv-ovitem__tags{display:inline-flex;align-items:center;gap:var(--mlv-s2);flex:none;margin-left:var(--mlv-s3)}.mlv-ovitem__tag{display:inline-flex;align-items:center;gap:1px;font-size:var(--mlv-fs-11);font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums}.mlv-ovitem--more{padding-left:16px;color:var(--mlv-text-2)}.mlv-root[data-theme=hc] .mlv-ovblock{border-color:var(--vscode-contrastBorder, var(--mlv-border-strong));background:var(--mlv-surface)}.mlv-root[data-theme=hc] .mlv-phaseindex__row[data-in-view=true]{background:none;outline:1px dashed var(--mlv-accent);outline-offset:-2px}.mlv-edge--weighted .mlv-edge__path{stroke-width:max(var(--mlv-edge-w, 2px),calc(1px / var(--mlv-z, 1)))}.mlv-edge__weight{pointer-events:none}.mlv-edge__weightbg{fill:var(--mlv-surface);stroke:var(--mlv-border-strong);stroke-width:1}.mlv-edge__weighttext{fill:var(--mlv-text-2);font-family:var(--mlv-font-mono);font-size:9px;font-variant-numeric:tabular-nums;text-anchor:middle;dominant-baseline:middle}.mlv-edge.has-issue .mlv-edge__weightbg{stroke:var(--mlv-sev, var(--mlv-border-strong))}.mlv-canvas[data-lod=compact] .mlv-edge__weight{display:none}:root[data-theme=hc] .mlv-edge__weightbg,.mlv-root[data-theme=hc] .mlv-edge__weightbg{stroke:currentColor}.mlv-workflow{position:relative;z-index:45;flex:none;height:0}.mlv-workflow__composer{position:absolute;top:4px;right:var(--mlv-s4);display:flex;flex-wrap:wrap;align-items:center;gap:8px;width:min(440px,calc(100vw - 2 * var(--mlv-s4)));padding:10px 12px;border:1px solid var(--mlv-border-strong);border-radius:var(--mlv-r-panel);background:var(--mlv-surface);box-shadow:var(--mlv-sh-3)}.mlv-workflow__composer[hidden]{display:none}.mlv-workflow__selection{flex:1 0 100%;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--mlv-text);font-size:12px;font-weight:var(--mlv-fw-medium)}.mlv-workflow__intent{flex:0 0 auto}.mlv-workflow__custom{flex:1 1 180px;min-width:0}.mlv-workflow__custom[hidden]{display:none}.mlv-workflow__status{flex:1 0 100%;color:var(--mlv-text);font-size:11px}.mlv-workflow__status:empty{display:none}.mlv-workflow__hint{flex:1 0 100%;margin:0;color:var(--mlv-text-2);font-size:11px}#mlview-authored-error{white-space:pre-wrap;margin:0;padding:6px 12px;flex:none;max-height:20vh;overflow-y:auto;overscroll-behavior:contain;font-family:var(--mlv-font, var(--vscode-font-family, system-ui, sans-serif));font-size:12px;line-height:1.45;color:var(--mlv-text, var(--vscode-foreground, inherit));background:var(--mlv-surface-2, var(--vscode-editorWidget-background, transparent));border-bottom:1px solid var(--mlv-sev-medium, var(--vscode-editorWarning-foreground, currentColor))}.mlv-hostnotice{display:flex;align-items:flex-start;gap:var(--mlv-s3);flex:none;padding:6px 12px;font-size:12px;line-height:1.45;color:var(--mlv-text);background:var(--mlv-surface-2);border-bottom:1px solid var(--mlv-border)}.mlv-hostnotice[hidden]{display:none}.mlv-hostnotice--warn{border-bottom-color:var(--mlv-sev-medium)}.mlv-hostnotice--error{border-bottom-color:var(--mlv-sev-high)}.mlv-hostnotice__icon{flex:none;display:flex;padding-top:2px}.mlv-hostnotice--warn .mlv-hostnotice__icon{color:var(--mlv-sev-medium)}.mlv-hostnotice--error .mlv-hostnotice__icon{color:var(--mlv-sev-high)}.mlv-hostnotice__text{flex:1 1 auto;min-width:0;margin:0;white-space:pre-wrap;overflow-wrap:anywhere;max-height:20vh;overflow-y:auto;overscroll-behavior:contain}.mlv-hostnotice__actions{flex:none;display:flex;flex-wrap:wrap;gap:6px}.mlv-hostnotice__actions[hidden]{display:none}.mlv-status__fresh{display:inline-flex;align-items:center;gap:4px}.mlv-status__fresh.is-warn{color:var(--mlv-text)}.mlv-status__fresh.is-warn .mlv-uicon{color:var(--mlv-stale-ink)}.mlv-status__fresh.is-checking{color:var(--mlv-text-2);font-style:italic}.mlv-status__fresh.is-muted{color:var(--mlv-text-2)}.mlv-insp__stale-note{display:flex;align-items:flex-start;gap:6px;margin:4px 0 6px;font-size:11px;color:var(--mlv-text)}.mlv-insp__stale-note .mlv-uicon,.mlv-insp__stale .mlv-uicon,.mlv-chip--stale .mlv-uicon{flex:none;color:var(--mlv-stale-ink)}.mlv-insp__stale{display:inline-flex;align-items:center;gap:4px;margin-left:6px;font-size:11px;color:var(--mlv-text)}.mlv-link.is-stale,.mlv-btn.is-stale{cursor:not-allowed;color:var(--mlv-text-2);text-decoration:line-through}.mlv-chip--stale{display:inline-flex;align-items:center;gap:3px}.mlv-sheet__note{margin:0 0 8px;font-size:12px;color:var(--mlv-text-2)}.mlv-root--workflow>.mlv-body{flex:1 1 0;min-height:min(320px,50vh)}.mlv-walkbar{flex:none;display:flex;flex-direction:column;gap:2px;padding:4px var(--mlv-s4) 5px var(--mlv-s5);border-top:1px solid var(--mlv-border);background:var(--mlv-surface-2);color:var(--mlv-text);font-size:var(--mlv-fs-12);line-height:1.4}.mlv-walkbar[hidden]{display:none}.mlv-walkbar__row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--mlv-s2) var(--mlv-s4);min-width:0}.mlv-walkbar__pos{display:inline-flex;flex:none;align-items:center;gap:4px;margin:0;font-weight:var(--mlv-fw-semi);font-variant-numeric:tabular-nums;white-space:nowrap}.mlv-walkbar__mark{display:inline-flex;color:var(--mlv-stale-ink)}.mlv-walkbar__mark[hidden]{display:none}.mlv-walkbar__tools{display:flex;flex:1 1 auto;flex-wrap:wrap;align-items:center;gap:var(--mlv-s2) var(--mlv-s4);min-width:0}.mlv-walkbar__steps{display:inline-flex;flex:none;gap:2px}.mlv-walkbar__step{width:24px;min-width:0;height:22px;min-height:0;padding:0}.mlv-walkbar__step--prev .mlv-uicon{transform:rotate(-90deg)}.mlv-walkbar__step--next .mlv-uicon{transform:rotate(90deg)}.mlv-walkbar__filters{display:inline-flex;flex-wrap:wrap;gap:var(--mlv-s2)}.mlv-walkbar__filter{display:inline-flex;align-items:center;gap:5px;height:22px;padding:0 var(--mlv-s3);border:1px solid var(--mlv-border);border-radius:var(--mlv-r-chip);background:var(--mlv-surface);color:var(--mlv-text);font-size:var(--mlv-fs-12);white-space:nowrap;cursor:pointer}.mlv-walkbar__filter[hidden]{display:none}.mlv-walkbar__filter:hover{border-color:var(--mlv-border-strong)}.mlv-walkbar__filter[aria-pressed=true]{border-color:var(--mlv-text-2);font-weight:var(--mlv-fw-semi)}.mlv-walkbar__fcount{color:var(--mlv-text-2);font-variant-numeric:tabular-nums}.mlv-walkbar__filter[aria-pressed=true] .mlv-walkbar__fcount{color:var(--mlv-text)}.mlv-walkbar__keys{display:inline-flex;flex-wrap:wrap;gap:var(--mlv-s2) var(--mlv-s4);color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-walkbar__keys[hidden]{display:none}.mlv-walkbar__key{display:inline-flex;align-items:center;gap:2px;white-space:nowrap}.mlv-walkbar__exit{margin-left:auto;min-height:22px;padding:0 var(--mlv-s3)}.mlv-walkbar__editor{display:flex;align-items:flex-start;gap:5px;margin:0;color:var(--mlv-text-2);font-size:var(--mlv-fs-11);overflow-wrap:anywhere}.mlv-walkbar__editor[hidden]{display:none}.mlv-walkbar__editor[data-walk-status=blocked],.mlv-walkbar__editor[data-walk-status=failed]{color:var(--mlv-text)}.mlv-walkbar__editoricon{display:inline-flex;flex:none;padding-top:1px;color:var(--mlv-stale-ink)}.mlv-walkbar__editoricon[hidden]{display:none}.mlv-walkbar[data-layout=narrow]{padding:3px var(--mlv-s3)}.mlv-walkbar[data-layout=narrow] .mlv-walkbar__tools{flex:1 1 0}.mlv-walkbar[data-layout=narrow] .mlv-walkbar__filters{flex:1 1 0;min-width:0}.mlv-walkbar[data-layout=narrow] .mlv-walkbar__exitlabel{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.mlv-header__review{flex:none;height:24px;min-height:0;padding:0 var(--mlv-s3);gap:5px}.mlv-header__review[aria-pressed=true]{border-color:var(--mlv-text-2)}.mlv-quote.is-walk{margin-left:calc(-1 * var(--mlv-s3));padding-left:calc(var(--mlv-s3) - 2px);border-left:2px solid var(--mlv-focus)}.mlv-quote__walk{display:flex;align-items:flex-start;gap:5px;margin:3px 0 0 calc(1.4em + var(--mlv-s3));color:var(--mlv-text-2);font-size:var(--mlv-fs-11)}.mlv-quote.is-walk[data-walk-status=blocked] .mlv-quote__walk,.mlv-quote.is-walk[data-walk-status=failed] .mlv-quote__walk{color:var(--mlv-text)}.mlv-quote__walk .mlv-uicon{flex:none;margin-top:1px;color:var(--mlv-stale-ink)}@page{margin:12mm}@media print{.mlv-header,.mlv-workflow,.mlv-hostnotice,.mlv-walkbar,.mlv-status,.mlv-rail,.mlv-phaseindex,.mlv-overview,.mlv-zoom,.mlv-skiplink,.mlv-legend,.mlv-moremenu,.mlv-statehost,.mlv-tooltip,.mlv-toasts,.mlv-shortcuts{display:none!important}html.mlv-fills-page,html.mlv-fills-page>body,.mlv-root,.mlv-body,.mlv-main,.mlv-canvas{display:block!important;height:auto!important;max-height:none!important;min-height:0!important;overflow:visible!important}.mlv-world{position:static!important;transform:none!important;transform-origin:0 0!important}.mlv-canvas{background-image:none!important;cursor:auto!important}.mlv-canvas[data-lod=compact] .mlv-node__sub,.mlv-canvas[data-lod=compact] .mlv-node__loc{display:block!important}.mlv-canvas[data-lod=compact] .mlv-node__sub[data-lines="2"]{display:-webkit-box!important}.mlv-canvas[data-lod=compact] .mlv-node__chips{display:flex!important}.mlv-canvas[data-lod=compact] .mlv-node__title{font-size:var(--mlv-fs-13)!important;line-height:var(--mlv-lh-tight)!important}.mlv-canvas[data-lod=compact] .mlv-node__iconbox{display:flex!important}.mlv-canvas[data-lod=compact] .mlv-node__main{align-items:flex-start!important;padding:10px var(--mlv-s5) 10px var(--mlv-s4)!important}.mlv-canvas[data-lod=compact] .mlv-node>.mlv-basis-tag{transform-origin:0 50%!important;transform:translateY(-50%) scale(calc(1 / var(--mlv-z, 1)))!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap{-webkit-line-clamp:1!important;line-clamp:1!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="2"]{-webkit-line-clamp:2!important;line-clamp:2!important}.mlv-canvas[data-lod=compact] .mlv-node__title--wrap[data-lines="3"]{-webkit-line-clamp:3!important;line-clamp:3!important}.mlv-canvas[data-lod=compact] .mlv-edge-label{display:inline!important;opacity:0!important}.mlv-canvas[data-lod=compact] .mlv-edge--data .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge--control .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge--merged .mlv-edge-label,.mlv-canvas[data-lod=compact] .mlv-edge.has-label .mlv-edge-label{opacity:1!important}.mlv-root{-webkit-print-color-adjust:exact;print-color-adjust:exact}.mlv-edge__flow,.mlv-flow-charge{display:none!important}.mlv-node,.mlv-group,.mlv-lane{break-inside:avoid}} diff --git a/webview/dist/mlview.js b/webview/dist/mlview.js index f79d6be9..abe9e200 100644 --- a/webview/dist/mlview.js +++ b/webview/dist/mlview.js @@ -1,9 +1,9 @@ -"use strict";var MLView=(()=>{var Qn=Object.defineProperty;var Qa=Object.getOwnPropertyDescriptor;var $a=Object.getOwnPropertyNames;var Ja=Object.prototype.hasOwnProperty;var ed=(t,e)=>{for(var n in e)Qn(t,n,{get:e[n],enumerable:!0})},td=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of $a(e))!Ja.call(t,i)&&i!==n&&Qn(t,i,{get:()=>e[i],enumerable:!(o=Qa(e,i))||o.enumerable});return t};var nd=t=>td(Qn({},"__esModule",{value:!0}),t);var fg={};ed(fg,{bridges:()=>hg,mountWorkflow:()=>ug,normalizeWorkflow:()=>Zt,version:()=>cg});function Te(t){if(!t||typeof t.evidenceId!="string")return null;let e=t.cell;return typeof e!="number"||!isFinite(e)||e<0||Math.trunc(e)!==e?null:{cell:e,line:t.line}}function Ke(t){let e=lt(t);return e.head+e.tail}function lt(t){let e=Te(t);return e?{head:t.file,tail:" › cell "+e.cell+", line "+e.line}:{head:t.file,tail:":"+t.line}}function Rt(t){let e=Te(t);return e?t.file+" cell "+e.cell+" line "+e.line:t.file+" line "+t.line}function Re(t){let e=Te(t);return e?"cell "+e.cell+", counted from 0 as the artifact records it (markdown cells count too); line "+e.line+" of that cell":""}var he=["http","//www.w3.org/2000/svg"].join(":"),Bi=["http","//www.w3.org/1999/xlink"].join(":");function f(t,e,n){let o=document.createElement(t);return e&&(o.className=e),n!==void 0&&(o.textContent=n),o}function R(t,e){let n=document.createElementNS(he,t);return e&&fe(n,e),n}function fe(t,e){for(let n of Object.keys(e))t.setAttribute(n,String(e[n]))}function H(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function g(t,e){return t.appendChild(e),e}function W(t,e,n){let o=f("button",t,e);return o.type="button",o.setAttribute("aria-label",n||e),n&&(o.title=n),o}function ne(t,e){let n=f("button",t);return n.type="button",n.title=e,n.setAttribute("aria-label",e),n}function _(t,e,n,o){return t.addEventListener(e,n,o),()=>t.removeEventListener(e,n,o)}function Di(t,e){let n=null,o=((...i)=>{n!==null&&clearTimeout(n),n=setTimeout(()=>{n=null,t(...i)},e)});return o.cancel=()=>{n!==null&&clearTimeout(n),n=null},o}function de(t){return Ke(t)}function Oe(t,e,n="span"){let o=f(n,t+(t?" ":"")+"mlv-loc"),i=lt(e);g(o,f("span","mlv-loc__file",i.head)),g(o,f("span","mlv-loc__at",i.tail));let s=Te(e);return s&&(o.setAttribute("data-cell",String(s.cell)),o.title=Re(e)),o}var Ot={low:"M9 2A7 7 0 1 0 9 16A7 7 0 0 0 9 2Z",medium:"M9.9 2.75a1.04 1.04 0 0 0-1.8 0L1.5 14.6a1.04 1.04 0 0 0 .9 1.56h13.2a1.04 1.04 0 0 0 .9-1.56Z",high:"M6.05 1.4h5.9L16.6 6.05v5.9L11.95 16.6h-5.9L1.4 11.95v-5.9Z"},$n={low:"M9 4.3a1.08 1.08 0 1 0 0 2.16A1.08 1.08 0 0 0 9 4.3ZM7.94 7.65h2.12v5.7H7.94Z",medium:"M7.95 6.45h2.1l-.28 4.62h-1.54ZM9 12.1a1.12 1.12 0 1 0 0 2.24A1.12 1.12 0 0 0 9 12.1Z",high:"M7.94 4.5h2.12l-.3 6.05h-1.52ZM9 11.7a1.15 1.15 0 1 0 0 2.3A1.15 1.15 0 0 0 9 11.7Z"},Z=["high","medium","low"],ye={high:"high",medium:"medium",low:"low"};function od(t){return t==="low"||t==="medium"||t==="high"}function $(t){return od(t)?t:"low"}function pe(){return{low:0,medium:0,high:0}}function J(t){return t.low+t.medium+t.high}function Y(t){return t.high>0?"high":t.medium>0?"medium":t.low>0?"low":null}function U(t,e=18,n){let o=$(t),i=R("svg",{class:"mlv-glyph mlv-glyph--"+o,viewBox:"0 0 18 18",width:e,height:e,role:"img","aria-label":n||ye[o]+" severity"}),s=document.createElementNS(he,"path");fe(s,{class:"mlv-glyph__shape",d:Ot[o],"vector-effect":"non-scaling-stroke"});let r=document.createElementNS(he,"path");return fe(r,{class:"mlv-glyph__ink",d:$n[o]}),i.appendChild(s),i.appendChild(r),i}function Gi(t){let e=J(t),n=Y(t);return e+(e===1?" finding":" findings")+", highest severity "+(n?ye[n]:"none")}function id(t){let e=J(t),n=Z.filter(o=>t[o]>0).map(o=>t[o]+" "+ye[o]);return e+(e===1?" finding":" findings")+": "+n.join(", ")}var ji=2;function Jn(t){let e=t.slice(0,ji).join(" "),n=t.length-ji;return n>0?e+" +"+n:e}function zi(t){return t.length?t.length===1?"finding "+t[0]:"findings "+t.slice(0,-1).join(", ")+" and "+t[t.length-1]:""}function Vi(t,e=18,n=[]){let o=Y(t);if(!o)return null;let i=J(t),s=f("div","mlv-badge mlv-badge--"+o);return s.setAttribute("role","img"),s.setAttribute("aria-label",n.length?zi(n).replace(/^f/,"F")+", highest severity "+ye[o]:Gi(t)),s.appendChild(U(o,e,"")),n.length?s.appendChild(f("span","mlv-badge__ids",Jn(n))):i>1&&s.appendChild(f("span","mlv-badge__count",String(i))),s}function Ht(t,e=14,n){if(J(t)===0)return null;let o=f("div","mlv-cluster");o.setAttribute("role","img"),o.setAttribute("aria-label",n||Gi(t)),o.title=n||id(t);for(let i of Z){let s=t[i];if(s<=0)continue;let r=f("span","mlv-cluster__item mlv-cluster__item--"+i);r.appendChild(U(i,e,"")),r.appendChild(f("span","mlv-cluster__count",String(s))),o.appendChild(r)}return o}function eo(t){return t/2+1.5}function Wi(t,e=16,n=[]){let o=$(t),i=R("g",{class:"mlv-edge-marker mlv-edge-marker--"+o,role:"img"}),s=n.length?zi(n)+" on this connection, highest severity "+ye[o]:ye[o]+" severity finding on this connection";if(i.setAttribute("aria-label",s.charAt(0).toUpperCase()+s.slice(1)),n.length){let a=R("title");a.textContent=i.getAttribute("aria-label")||"",i.appendChild(a)}let r=R("circle",{class:"mlv-edge-marker__disc",r:eo(e),cx:0,cy:0});i.appendChild(r);let l=U(o,e,"");return l.setAttribute("x",String(-e/2)),l.setAttribute("y",String(-e/2)),l.removeAttribute("role"),l.removeAttribute("aria-label"),l.setAttribute("aria-hidden","true"),i.appendChild(l),i}var sd=["changed","missing","unreadable","too-large","elsewhere"];var me={changed:"changed since publishing",missing:"file missing",unreadable:"file unreadable","too-large":"file too large to check",elsewhere:"in another folder; see the notice above"};function ie(t){return t.length>0&&t.every(e=>e==="elsewhere")}function Ki(t){if(!Array.isArray(t))return[];let e=[],n=new Set;for(let o of t){if(e.length>=2e3)break;if(!o||typeof o!="object")continue;let i=o.path,s=o.reason;typeof i!="string"||!i||i.length>500||n.has(i)||typeof s!="string"||sd.indexOf(s)<0||(n.add(i),e.push({path:i,reason:s}))}return e}var Nt=class{constructor(){this.reasons=new Map;this.checking=!1}set(e){let n=new Map(e.map(i=>[i.path,i.reason])),o=n.size===this.reasons.size;if(o)for(let[i,s]of n)this.reasons.get(i)!==s&&(o=!1);return this.reasons=n,!o}reasonOf(e){return e?this.reasons.get(e):void 0}get size(){return this.reasons.size}paths(){return Array.from(this.reasons.keys())}list(){return Array.from(this.reasons,([e,n])=>({path:e,reason:n}))}};function qi(t,e){let n=t||[],o=0;for(let i of n)i.file&&e(i.file)&&o++;return{stale:o,total:n.length}}function rd(t){if(ie(t))return"in another folder";let e=new Set(t);return e.size===1&&e.has("changed")?"changed":e.has("changed")?"changed or missing":"missing"}function ld(t,e){if(!t||e.size===0)return null;let n=Array.from(new Set((t.evidence||[]).map(c=>c.file))),o=n.filter(c=>e.reasonOf(c)),i=e.paths().filter(c=>n.indexOf(c)<0),s=rd(e.list().map(c=>c.reason)),r;o.length?(r=o.length+" of "+n.length+" cited files "+s,i.length&&(r+=" · "+i.length+" inspected")):r=i.length+" inspected "+(i.length===1?"file":"files")+" "+s;let l=e.list().slice(0,20).map(c=>c.path+" — "+me[c.reason]);e.size>20&&l.push("and "+(e.size-20)+" more");let d=(ie(e.list().map(c=>c.reason))?"Source files that are not under the workspace root but are unchanged in another folder; the notice above names it. Jumps into them are blocked.":"Source files that no longer match the published revision. Jumps into them are blocked.")+` -`+l.join(` -`);return{text:r,title:d}}function Ui(t,e){if(!t)return null;let n=ld(t,e);if(n)return{state:"stale",text:n.text,title:n.title};if(!t.verification)return{state:"unverified",text:"Freshness not checked",title:"This revision was published without source hashes, so MLView cannot tell whether the cited files changed since."};let o=new Set((t.evidence||[]).map(i=>i.file)).size;return{state:"unchanged",text:o+(o===1?" cited file unchanged":" cited files unchanged"),title:"The "+(o===1?"file":o+" files")+" the quotes cite still match the hashes published with revision "+t.revision.id+". Unchanged means the quoted lines still exist as published; it does not mean they support the claims."}}function Xi(t,e){if(!(!t||typeof t!="object"||t.v!==1))switch(t.type){case"init":e.init(t.theme,t.capabilities);return;case"workflow":e.workflow(t.document);return;case"workflowError":{let n=Array.isArray(t.codes)?t.codes.filter(o=>typeof o=="string"):void 0;e.workflowStatus(n),e.hostNotice(typeof t.message=="string"?t.message:"",n);return}case"stale":e.stale(Ki(t.files));return;case"actionResult":e.actionResult(t);return;case"theme":e.theme(t.kind);return;case"revealNode":e.revealNode(t.nodeId,t.center!==!1);return;case"revealIssue":e.revealIssue(t.issueId);return;case"restoreState":e.restoreState(t.state);return;default:e.onUnknown(String(t.type))}}function Yi(t,e){return!t||typeof t!="object"?{...e,severities:e.severities.slice()}:{severities:Array.isArray(t.severities)?t.severities.slice():e.severities.slice(),query:typeof t.query=="string"?t.query:""}}var ad=["low","medium","high"];function to(){return{severities:ad.slice(),query:""}}var Pt=class{constructor(){this.current=to();this.keep=e=>this.current.severities.indexOf($(e.severity))>=0}get value(){return this.current}get query(){return this.current.query}patch(e){let n=this.snapshot();e.severities!==void 0&&(n.severities=e.severities.slice()),e.query!==void 0&&(n.query=String(e.query)),this.current=n}reset(){this.current=to()}restore(e){this.current=Yi(e,to())}snapshot(){return{severities:this.current.severities.slice(),query:this.current.query}}toggleSeverity(e){let n=this.current.severities.slice(),o=n.indexOf(e);o>=0?n.splice(o,1):n.push(e),this.patch({severities:n})}};function Zi(t){return t==="light"||t==="dark"||t==="hc"}var Ft=class{constructor(e,n){this.root=e,this.current=Zi(n)?n:"light"}get kind(){return this.current}apply(e){Zi(e)&&(this.current=e,this.root.setAttribute("data-theme",e))}destroy(){this.root.removeAttribute("data-theme")}};var dd='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function cd(t){let e=[];for(let n of Array.from(t.querySelectorAll(dd))){if(n.tabIndex<0)continue;let o=!0;for(let i=n;i&&i!==t.parentElement;i=i.parentElement)if(i.hidden){o=!1;break}o&&e.push(n)}return e}function Bt(t,e){if(e.key!=="Tab"||e.altKey||e.ctrlKey||e.metaKey)return!1;let n=cd(t),o=t.ownerDocument?t.ownerDocument.activeElement:null;if(!n.length)return e.preventDefault(),!0;let i=n[0],s=n[n.length-1],r=!!o&&t.contains(o),l=null;if(e.shiftKey&&(!r||o===i)?l=s:!e.shiftKey&&(!r||o===s)&&(l=i),!l)return!1;e.preventDefault();try{l.focus()}catch{}return!0}function at(t,e){let n=i=>!!i&&i.isConnected&&!i.disabled&&typeof i.focus=="function",o=i=>{if(!n(i)||i===i.ownerDocument.body)return!1;try{i.focus()}catch{return!1}return i.ownerDocument.activeElement===i};o(t)||o(e)}var ut=8;function Qi(t){return(t%ut+ut)%ut}var Dt=class{constructor(e){this.nodeById=new Map;this.edgeById=new Map;this.issueById=new Map;this.issueRank=new Map;this.childrenOf=new Map;this.lexicalParentOf=new Map;this.laneChildrenOf=new Map;this.parentOf=new Map;this.rootsByLane=new Map;this.outEdges=new Map;this.inEdges=new Map;this.lanes=[];this.absentStages=[];this.phaseOrder=new Map;this.graph=e;for(let l of e.nodes||[])this.nodeById.set(l.id,l);for(let l of e.edges||[])this.edgeById.set(l.id,l);for(let l of e.issues||[])this.issueById.set(l.id,l),this.issueRank.has(l.id)||this.issueRank.set(l.id,this.issueRank.size);for(let l of e.nodes||[]){let a=l.parent&&this.nodeById.has(l.parent)?l.parent:null;this.lexicalParentOf.set(l.id,a),a&&ct(this.childrenOf,a,l.id)}for(let l of e.nodes||[])this.hasCycle(l.id)&&this.lexicalParentOf.set(l.id,null);for(let l of e.nodes||[]){let a=this.lexicalParentOf.get(l.id)||null,d=a?this.nodeById.get(a):void 0,c=!!d&&d.stage===l.stage;this.parentOf.set(l.id,c?a:null),c?ct(this.laneChildrenOf,a,l.id):ct(this.rootsByLane,l.stage,l.id)}for(let l of e.edges||[])!this.nodeById.has(l.source)||!this.nodeById.has(l.target)||(ct(this.outEdges,l.source,l),ct(this.inEdges,l.target,l));let n=new Map;for(let l of e.stages||[])n.set(l.id,l);let o=(e.stages||[]).slice().sort((l,a)=>l.order-a.order||ud(l.id,a.id));for(let l of o)(this.rootsByLane.get(l.id)||[]).length>0||l.present?this.lanes.push(l):this.absentStages.push(l);let i=new Set(this.lanes.map(l=>l.id)),s=[];for(let l of e.nodes||[])!n.has(l.stage)&&!i.has(l.stage)&&(i.add(l.stage),s.push(l.stage));s.sort();for(let l of o)this.phaseOrder.has(l.id)||this.phaseOrder.set(l.id,this.phaseOrder.size);for(let l of s)this.phaseOrder.set(l,this.phaseOrder.size);let r=this.lanes.length?this.lanes[this.lanes.length-1].order+1:0;for(let l of s)this.lanes.push({id:l,label:l,order:r++,present:!0,nodeCount:(this.rootsByLane.get(l)||[]).length,issueCounts:pe(),maxSeverity:null})}phaseIndexOf(e){return this.phaseOrder.get(e)??0}hasCycle(e){let n=this.lexicalParentOf.get(e)||null,o=0;for(;n;){if(n===e||++o>4096)return!0;n=this.lexicalParentOf.get(n)||null}return!1}children(e){return this.childrenOf.get(e)||[]}laneChildren(e){return this.laneChildrenOf.get(e)||[]}roots(e){return this.rootsByLane.get(e)||[]}ancestors(e){let n=[],o=this.parentOf.get(e)||null;for(;o;)n.push(o),o=this.parentOf.get(o)||null;return n}isGroup(e){return this.laneChildren(e).length>0}issuesOf(e,n){let o=this.nodeById.get(e);if(!o)return[];let i=[];for(let s of o.issueIds){let r=this.issueById.get(s);r&&n(r)&&i.push(r)}return i}issuesOfEdge(e,n){let o=this.edgeById.get(e);if(!o)return[];let i=[];for(let s of o.issueIds){let r=this.issueById.get(s);r&&n(r)&&i.push(r)}return i}issuesOfEdges(e,n){if(e.length===1)return this.issuesOfEdge(e[0],n);let o=new Set,i=[];for(let s of e)dt(i,o,this.issuesOfEdge(s,n));return this.inDocumentOrder(i)}inDocumentOrder(e){return e.sort((n,o)=>(this.issueRank.get(n.id)??0)-(this.issueRank.get(o.id)??0))}countsFor(e){let n=pe();for(let o of e)n[$(o.severity)]++;return n}ownCounts(e,n){return this.countsFor(this.issuesOf(e,n))}subtreeIssues(e,n){let o=[],i=a=>{o.push(a);for(let d of this.laneChildren(a))i(d)};i(e);let s=new Set(o),r=new Set,l=[];for(let a of o){dt(l,r,this.issuesOf(a,n));for(let d of this.outEdges.get(a)||[])!s.has(d.target)||a===e&&d.target===e||dt(l,r,this.issuesOfEdge(d.id,n))}return this.inDocumentOrder(l)}subtreeCounts(e,n){return this.countsFor(this.subtreeIssues(e,n))}descendantCount(e){let n=0;for(let o of this.laneChildren(e))n+=1+this.descendantCount(o);return n}laneCounts(e,n){let o=new Set,i=[];for(let s of this.graph.nodes||[])s.stage===e&&dt(i,o,this.issuesOf(s.id,n));for(let s of this.graph.edges||[]){let r=this.nodeById.get(s.source);!r||r.stage!==e||dt(i,o,this.issuesOfEdge(s.id,n))}return this.countsFor(i)}isHidden(e,n){let o=this.parentOf.get(e)||null;for(;o;){if(n.has(o))return!0;o=this.parentOf.get(o)||null}return!1}visibleRepresentative(e,n){let o=this.ancestors(e);for(let i=o.length-1;i>=0;i--)if(n.has(o[i]))return o[i];return e}defaultCollapsed(){let e=[],n=(this.graph.nodes||[]).length>120,o=this.findingTargetAncestors();for(let i of this.graph.nodes||[]){if(!this.isGroup(i.id)||o.has(i.id))continue;let s=this.descendantCount(i.id);(s>12||n&&s>4)&&e.push(i.id)}return e.sort()}findingTargetAncestors(){let e=new Set;for(let o of this.graph.issues||[]){for(let i of o.nodeIds||[])e.add(i);for(let i of o.edgeIds||[]){let s=this.edgeById.get(i);s&&(e.add(s.source),e.add(s.target))}}let n=new Set;for(let o of e)for(let i of this.ancestors(o))n.add(i);return n}};function dt(t,e,n){for(let o of n)e.has(o.id)||(e.add(o.id),t.push(o))}function ct(t,e,n){let o=t.get(e);o?o.push(n):t.set(e,[n])}function ud(t,e){return te?1:0}function se(t,e){t.setAttribute("data-phase-index",String(e)),t.setAttribute("data-phase-tone",String(Qi(e)))}function Ji(t){return t.length||1}function jt(t){if(t<2)return 1.5;let e=1.5+Math.log2(t)*.75;return Math.round(Math.min(5,e)*100)/100}function ht(t){return"×"+t}var $i=13;function Gt(t,e,n){if(!n)return{x:t.x,y:t.y};let o=typeof e=="number"&&isFinite(e)?e:0;return{x:t.x+Math.cos(o+Math.PI/2)*$i,y:t.y+Math.sin(o+Math.PI/2)*$i}}function zt(t){return Math.round(ht(t).length*5.6+10)}var xe=13;var es={rankdir:"LR",ranksep:72,nodesep:24,edgesep:12,marginx:16,marginy:16,ranker:"network-simplex"},hd={data:4,call:2,control:2,config:1};function ts(t){let e=hd[t];return typeof e=="number"?e:1}var ns=26,no=264,Vt=24,oo=28,io=2e3,os=20,so=8,fd=14,le=8,Wt=16,ro=12,lo=fd,is=112,ss=4,rs=13,ls=6,ao=28;var pd=6.02,Ut=14,md=1,gd=4,Kt=8,as=2,ds=20,cs=10,vd=3,co=14,bd=.7,qt=7,we=96;function ho(t){return t.label?t.back?"↻ "+t.label:t.label:""}function yd(t){return Ee(t.length*pd+2*gd)}function xd(t){return t.back||t.count>1||wd.indexOf(t.kind)>=0}var wd=["data","control","state","loop","output"];function uo(t,e,n){return t.x-ne.x&&t.y-ne.y}var Xt=class{constructor(){this.cells=new Map}add(e){let n=Math.floor(e.x/we),o=Math.floor((e.x+e.w)/we),i=Math.floor(e.y/we),s=Math.floor((e.y+e.h)/we);for(let r=n;r<=o;r++)for(let l=i;l<=s;l++){let a=r+":"+l,d=this.cells.get(a);d?d.push(e):this.cells.set(a,[e])}}hits(e,n){let o=Math.floor((e.x-n)/we),i=Math.floor((e.x+e.w+n)/we),s=Math.floor((e.y-n)/we),r=Math.floor((e.y+e.h+n)/we);for(let l=o;l<=i;l++)for(let a=s;a<=r;a++){let d=this.cells.get(l+":"+a);if(d){for(let c of d)if(uo(e,c,n))return!0}}return!1}};function Ed(t){let e=[];for(let n of t.boxes.values()){if(!n.isGroup||n.collapsed){e.push({x:n.x,y:n.y,w:n.w,h:n.h});continue}e.push({x:n.x,y:n.y,w:n.w,h:Math.max(n.headerH,1)})}return e}function _d(t){let e=[];for(let n of t){let o=n.y+28,i=n.y+n.h-28;i-o=n.top&&e.y+e.h<=n.bottom)return n;return null}function Md(t,e){return e.x>=0&&e.y>=0&&e.x+e.w<=t.width&&e.y+e.h<=t.height}function kd(t,e){let n=[];for(let o=1;o0?le:0,l=o.51){let c=s.x>i.x,u=Math.min(i.x,s.x)+(c?r:l),h=Math.max(i.x,s.x)-(c?l:r);if(h-um.bottom||n.push({axis:"h",at:p,a:u,b:h,laneId:m.id,score:h-u,order:o})}else if(a<.51&&d>.51){let c=s.y>i.y,u=Math.min(i.y,s.y)+(c?r:l),h=Math.max(i.y,s.y)-(c?l:r);if(h-u.001?e.score-t.score:t.order-e.order}function Ad(t){if(t.length<2)return null;let e=t[0],n=t[1],o=Math.abs(n.x-e.x),i=Math.abs(n.y-e.y);return i<.51&&o>.51?{axis:"h",at:(e.y+n.y)/2,a:Math.min(e.x,n.x),b:Math.max(e.x,n.x),laneId:"",score:o,order:1}:o<.51&&i>.51?{axis:"v",at:(e.x+n.x)/2,a:Math.min(e.y,n.y),b:Math.max(e.y,n.y),laneId:"",score:i,order:1}:null}function Id(t,e){let n=e/2,o=t.axis==="h"?n:Ut/2,i=(t.a+t.b)/2,s=t.a+o,r=t.b-o,l=r>s?fo(i,s,r):i,a=r>s?(r-s)/2:0,d=[0,a*.6,-a*.6,a,-a],c=[];for(let u of d){if(c.length>=cs)break;if(u!==0&&Math.abs(u)<1)continue;let h=l+u;for(let p of[!1,!0]){if(c.length>=cs)break;let m,v;t.axis==="h"?(m=h,v=p?t.at+Kt:t.at-Kt):(m=p?t.at-Kt-n:t.at+Kt+n,v=h),c.push({x:Ee(m),y:Ee(v),rect:{x:Ee(m-n),y:Ee(v-md-Ut/2),w:e,h:Ut},flipped:p,shift:Math.abs(Ee(u))})}}return c}function us(t){return{x:t.x-qt,y:t.y-qt,w:qt*2,h:qt*2}}function Cd(t){let e=0;for(let n=1;n=e||o===t.length-1){let s=i===0?0:fo((e-n)/i,0,1);return{x:Ee(t[o-1].x+(t[o].x-t[o-1].x)*s),y:Ee(t[o-1].y+(t[o].y-t[o-1].y)*s)}}n+=i}return t[t.length-1]}function Rd(t,e,n){if(!n||!uo(us(e),n,1))return{point:e,moved:!1};let o=Cd(t),i=o/2;for(let s of[18,-18,30,-30,44,-44]){let r=fo(i+s,Math.min(8,o),Math.max(o-8,0)),l=Td(t,r);if(!uo(us(l),n,1))return{point:l,moved:!0}}return{point:e,moved:!1}}function hs(t,e){let n=new Map,o={labels:0,always:0,placed:0,hidden:0,flipped:0,shifted:0,fallback:0,nudged:0},i=_d(t.lanes),s=new Xt;for(let a of Ed(t))s.add(a);let r=new Xt,l=[[],[]];for(let a of e){if(!a.label)continue;o.labels++;let d=xd(a);d&&o.always++,l[d?0:1].push(a)}for(let a=0;a({run:C,fallback:!1}));m&&v.push({run:m,fallback:!0});let y=null,S=null,x=!1,w=null,E=null,b=null,M=null,T=0;for(let C of v){if(y||T>=ds)break;for(let N of Id(C.run,h)){if(T>=ds)break;if(T++,!Md(t,N.rect))continue;let O=Sd(i,N.rect);if(O&&(E||(E=N,b=C.run,M=O),!s.hits(N.rect,as)&&!r.hits(N.rect,as))){y=N,S=C.run,x=C.fallback,w=O;break}}}if(!y&&!d&&E&&(y=E,S=b,w=M),!y){o.hidden++,n.set(c.id,{id:c.id,text:u,x:c.mid.x,y:c.mid.y,rect:{x:c.mid.x,y:c.mid.y,w:0,h:0},hidden:!0,flipped:!1,axis:v.length?v[0].run.axis:"h",laneId:null,fallback:x,shifted:0,marker:c.mid,always:d});continue}let L=Rd(c.points,c.mid,y.rect);L.moved&&o.nudged++,o.placed++,y.flipped&&o.flipped++,y.shift>0&&o.shifted++,x&&o.fallback++,r.add(y.rect),n.set(c.id,{id:c.id,text:u,x:y.x,y:y.y,rect:y.rect,hidden:!1,flipped:y.flipped,axis:S?S.axis:"h",laneId:w?w.id:null,fallback:x,shifted:y.shift,marker:L.point,always:d})}}return{placements:n,stats:o}}function fo(t,e,n){return tn?n:t}function Ee(t){return Math.round(t*100)/100}var Od=["data","call","control","config","state","loop","output"],Hd={data:"data",dataflow:"data","data flow":"data",flow:"data",control:"control","control flow":"control",branch:"control","conditional call":"control",decision:"control",state:"state","state update":"state",update:"state","state read":"state","state copy":"state",gradient:"state","gradient flow":"state",optimizes:"state",ownership:"state","state ownership":"state",config:"config",configuration:"config","configuration flow":"config",call:"call",calls:"call",loop:"loop",loops:"loop",output:"output",outputs:"output"};function mo(t){let e=String(t||"").trim();if(!e)return"unknown";let n=e.toLowerCase().replace(/[\s_-]+/g," ");return Hd[n]||e}function ft(t){return t&&t!=="unknown"?t:"kind not specified"}var fs=0;function ps(){return fs++,fs}function Nd(t,e){let n="";for(let o=0;o=0?t:"unknown"}function Fd(t,e,n,o){let i=R("marker",{id:t,viewBox:"0 0 10 10",refX:8.5,refY:5,markerWidth:9,markerHeight:9,markerUnits:"userSpaceOnUse",orient:"auto"}),s=R("path",{class:e+(o?"":" mlv-arrow--open"),d:n});return o||fe(s,{fill:"none"}),i.appendChild(s),i}var pt={d:"M0.5 1 L9 5 L0.5 9 Z"},ms="mlv-arrow";function gs(){let t=R("defs");return t.appendChild(Fd(ms,"mlv-arrow",pt.d,!0)),t}var vs=14,qe=eo(vs);function go(t){return t.placement?t.placement.marker:t.route.mid}function bs(t){let e=t.route,n=Pd(e.kind),o=t.weight&&t.weight>1?Math.floor(t.weight):1,i=o>=2,s=R("g",{class:"mlv-edge mlv-edge--"+n+(e.count>1?" mlv-edge--merged":"")+(i?" mlv-edge--weighted":"")+(e.back?" mlv-edge--back":""),"data-edge-id":e.id,"data-edge-kind":e.kind});s.setAttribute("data-edge-ids",e.ids.join(" ")),i&&(s.setAttribute("data-edge-weight",String(o)),s.style.setProperty("--mlv-edge-w",jt(o)+"px")),t.stage&&s.setAttribute("data-stage",t.stage),t.phase!==void 0&&se(s,t.phase),t.basis&&s.setAttribute("data-basis",t.basis),t.severity&&(s.setAttribute("data-sev",t.severity),s.classList.add("has-issue")),t.stale&&(s.classList.add("is-stale"),s.setAttribute("data-stale","1"));let r=R("path",{class:"mlv-edge__hit",d:e.d});r.setAttribute("tabindex","-1"),r.setAttribute("role","button");let l=t.staleElsewhere?" Its evidence cites a file in another folder.":" Its evidence cites a changed or missing file.";r.setAttribute("aria-label",jd(e,t.sourceLabel,t.targetLabel,t.basis)+(t.stale?l:"")),s.appendChild(r);let a=R("path",{class:"mlv-edge__path",d:e.d});a.setAttribute("marker-end","url(#"+ms+")"),a.setAttribute("id",Nd(t.mountSerial,e.id)),s.appendChild(a);let d=t.placement;if(e.label&&d&&d.hidden)s.setAttribute("data-label-hidden","1");else if(e.label){let u=d?d.x:e.mid.x,h=d?d.y:e.mid.y-8,p=R("text",{class:"mlv-edge-label",x:u,y:h});p.textContent=ho(e),d&&(p.setAttribute("data-label-axis",d.axis),d.flipped&&p.setAttribute("data-label-flipped","1"),d.laneId&&p.setAttribute("data-label-lane",d.laneId)),s.appendChild(p),t.labelVisible&&s.classList.add("has-label")}let c=go(t);if(t.severity){let u=Wi(t.severity,vs,t.findings||[]);u.setAttribute("transform","translate("+c.x+","+c.y+")"),s.appendChild(u)}else if(e.back){let u=R("path",{class:"mlv-edge__loopmark",d:"M4 -4 L0 0 L4 4",fill:"none",stroke:"var(--mlv-edge)","stroke-width":1.3,"stroke-linecap":"round","stroke-linejoin":"round",transform:"translate("+c.x+","+c.y+")"});s.appendChild(u)}return i&&s.appendChild(Dd(o,c,e.midAngle,!!t.severity||e.back)),t.stale&&s.appendChild(Bd(c,!!t.severity||e.back||i)),s}function Bd(t,e){let n=t.x+(e?qe+10:0),o=R("g",{class:"mlv-edge__stale",transform:"translate("+Yt(n)+","+Yt(t.y)+")"});o.setAttribute("data-stale","1"),o.appendChild(R("circle",{class:"mlv-edge__stalebg",r:7.5})),o.appendChild(R("path",{class:"mlv-edge__staleicon",d:"M0 -4.6 4.9 3.9H-4.9ZM0 -1.5v2.6M0 2.7v.1",fill:"none"}));let i=R("title");return i.textContent="This connection cites a file that changed or is missing since publishing. Its jumps are blocked.",o.appendChild(i),o}function Dd(t,e,n,o){let i=ht(t),s=zt(t),r=Gt(e,n,o),l=R("g",{class:"mlv-edge__weight",transform:"translate("+Yt(r.x)+","+Yt(r.y)+")"});l.setAttribute("data-weight",String(t)),l.appendChild(R("rect",{class:"mlv-edge__weightbg",x:-s/2,y:-xe/2,width:s,height:xe,rx:xe/2}));let a=R("text",{class:"mlv-edge__weighttext",x:0,y:0});a.textContent=i,l.appendChild(a);let d=R("title");return d.textContent=t+" connections merged into this edge.",l.appendChild(d),l}function Yt(t){return Math.round(t*100)/100}function jd(t,e,n,o){let i=t.back?"loop-back connection":t.kind&&t.kind!=="unknown"?t.kind+" connection":"connection",s=t.label?" labelled "+t.label:"",r=e&&n?", flows from "+e+" to "+n:"",l=t.count>1?", "+t.count+" merged connections":"";return i+s+r+l+He(o)+". Press Enter to open the cited source."}function He(t){return t==="inferred"?", inferred, not observed":t==="unresolved"?", unresolved":""}function ys(t,e,n,o,i){let s=R("g",{class:"mlv-connector-group","data-sev":o}),r=(t.x+e.x)/2,l=(t.y+e.y)/2,a="M "+t.x+" "+t.y+" Q "+r+" "+(l-28)+" "+e.x+" "+e.y;s.appendChild(R("path",{class:"mlv-connector",d:a}));let d=R("circle",{class:"mlv-connector__badge",cx:r,cy:l-14,r:8});s.appendChild(d);let c=R("text",{class:"mlv-connector__label",x:r,y:l-14});c.textContent=String(n),s.appendChild(c);let u=R("title");return u.textContent=i,s.appendChild(u),s}function Ue(t,e){let n=e.map(o=>t.get(o)).find(Boolean);return n?{file:n.file,absFile:"",line:n.line,col:0,endLine:n.endLine,endCol:0,snippet:n.quote,cell:n.cell,evidenceId:n.id}:{file:"",absFile:"",line:1,col:0,endLine:1,endCol:0}}function Gd(t){return"F"+(t+1)}var zd="unspecified model";function Zt(t){if(!t||t.workflowVersion!=="1.0")throw new Error("MLView.mountWorkflow: workflowVersion must be 1.0");let e=new Map((t.evidence||[]).map(u=>[u.id,u])),n=new Map,o=new Map;for(let u of t.findings||[])for(let h of u.nodeIds||[]){let p=n.get(h)||[];p.push(u.id),n.set(h,p)}for(let u of t.findings||[])for(let h of u.edgeIds||[]){let p=o.get(h)||[];p.push(u.id),o.set(h,p)}let i=new Map((t.phases||[]).map(u=>[u.id,u.label])),s=(t.nodes||[]).map(u=>({...typeof u.detail=="string"&&u.detail.trim()?{detail:u.detail}:{},...i.has(u.phase)?{phaseLabel:i.get(u.phase)}:{},id:u.id,kind:u.kind||"unknown",level:u.parent?"op":"unit",stage:u.phase,label:u.label,sublabel:u.detail||"",qualname:u.label,loc:Ue(e,u.evidence),parent:u.parent||null,basis:u.basis,evidenceLocs:u.evidence.map(h=>Ue(e,[h])).filter(h=>!!h.file),issueIds:n.get(u.id)||[]})),r=(t.edges||[]).map(u=>({id:u.id,kind:mo(u.kind),source:u.source,target:u.target,...u.kind&&mo(u.kind)!==u.kind.trim()?{authoredKind:u.kind}:{},label:u.label,loc:Ue(e,u.evidence),basis:u.basis,evidenceLocs:u.evidence.map(h=>Ue(e,[h])).filter(h=>!!h.file),issueIds:o.get(u.id)||[]})),l=(t.findings||[]).map((u,h)=>{let p=Ue(e,u.evidence),m=(u.evidence||[]).map(v=>({item:e.get(v),role:"Supporting evidence"})).concat((u.counterEvidence||[]).map(v=>({item:e.get(v),role:"Counter-evidence"}))).filter(v=>!!v.item).map(({item:v,role:y})=>({...Ue(e,[v.id]),role:y}));return{id:u.id,code:u.id,short:Gd(h),severity:u.severity,basis:u.basis,title:u.title,message:u.message,fixHint:u.suggestion||"",loc:p,relatedLocs:m,nodeIds:u.nodeIds||[],edgeIds:u.edgeIds||[],stage:s.find(v=>u.nodeIds.includes(v.id))?.stage||""}}),a=new Map,d=new Map;for(let u of s)a.set(u.stage,(a.get(u.stage)||0)+1);for(let u of l){let h=d.get(u.stage)||pe();h[u.severity]++,d.set(u.stage,h)}let c=(t.phases||[]).map((u,h)=>{let p=d.get(u.id)||pe();return{id:u.id,label:u.label,order:h,present:!0,nodeCount:a.get(u.id)||0,issueCounts:p,maxSeverity:["high","medium","low"].find(m=>p[m])||null}});return{schemaVersion:"workflow-view/1",generator:{name:t.producer.host,version:t.producer.model||zd,rendererSha:t.revision.id,generatedAt:t.verification?.publishedAt||""},workspace:{root:t.title,entrypoints:t.request.entrypoints||[],filesAnalyzed:t.coverage.inspectedFiles.length},stages:c,nodes:s,edges:r,issues:l,diagnostics:t.coverage.limitations.map(u=>({kind:"workflow_limitation",message:u})),stats:{nodes:s.length,edges:r.length,issues:l.reduce((u,h)=>(u[h.severity]++,u),pe()),durationMs:0},authoredCoverage:{status:t.coverage.status,limitations:t.coverage.limitations.length}}}var vo=[["explain","Explain"],["expand","Expand"],["challenge","Challenge"],["trace","Trace"],["custom","Custom…"]];function xs(t){let e=_s(t);if(!e)return;let n=ws(e.intent);if(!(!e.open&&n==="explain"&&!e.custom))return{open:e.open,intent:n,custom:e.custom}}var ws=t=>vo.find(([e])=>e===t)?.[0]??"explain";function Es(t){if(!t||typeof t!="object")return null;let e=t;return{open:e.open===!0,intent:ws(e.intent),custom:typeof e.custom=="string"?e.custom.slice(0,500):""}}function _s(t){let e=t.querySelector(".mlv-workflow"),n=t.querySelector(".mlv-workflow__composer"),o=t.querySelector(".mlv-workflow__intent"),i=t.querySelector(".mlv-workflow__custom"),s=t.querySelector(".mlv-workflow__refine");if(!e||!n||!o||!i)return null;let r=t.ownerDocument.activeElement,l=null;r&&r===s?l="refine":r&&n.contains(r)&&(l=r===o?"intent":r===i?"custom":"submit");let a=n.getAttribute("data-selection-kind"),d=n.getAttribute("data-selection-id");return{revision:e.getAttribute("data-revision"),open:!n.hidden,intent:o.value,custom:i.value,focus:l,selection:(a==="node"||a==="edge"||a==="issue")&&d?{kind:a,id:d}:void 0}}var Vd=new Set(["checking","missing","unreadable","parse","invalid"]);function Ss(t,e){if(!e||e.some(o=>Vd.has(o)))return;let n=t.root.querySelector(".mlv-workflow__status");!n||!n.textContent||(n.textContent="",t.saveSoon())}function Wd(t,e){let n=t.root.querySelector(".mlv-workflow__composer"),o=t.root.querySelector(".mlv-workflow__refine"),i=t.root.querySelector(".mlv-workflow__custom"),s=t.root.querySelector(".mlv-workflow__status");if(!(!n||!o||!i||!s)){if(e.outcome==="done"){n.hidden=!0,o.setAttribute("aria-expanded","false"),i.value="",s.textContent="";let r=_e.get(n)||null;_e.delete(n),at(r,o),t.saveSoon();return}n.hidden=!1,o.setAttribute("aria-expanded","true"),s.textContent=e.message||"The refinement prompt was not copied.",t.saveSoon()}}var _e=new WeakMap;function Kd(t,e){if(!e)return"Whole diagram";let n=t.index;if(e.kind==="node"){let i=n?n.nodeById.get(e.id):void 0;return"Step: "+(i?i.label||i.id:e.id)}if(e.kind==="edge"){let i=n?n.edgeById.get(e.id):void 0;if(!i)return"Connection: "+e.id;let s=n.nodeById.get(i.source),r=n.nodeById.get(i.target);return"Connection: "+(i.label||(s?s.label:i.source)+" → "+(r?r.label:i.target))}let o=n?n.issueById.get(e.id):void 0;return"Finding: "+(o?(o.short?o.short+" · ":"")+o.title:e.id)}function Ms(t,e,n){t.root.classList.add("mlv-root--workflow"),t.root.setAttribute("data-workflow-revision",e.revision.id);let o=t.root.querySelector(".mlv-workflow");if(!o){o=f("section","mlv-workflow");let b=t.chrome.header;t.root.insertBefore(o,b.nextSibling)}let i=_s(t.root),s=i??(n?{revision:e.revision.id,open:n.open,intent:n.intent,custom:n.custom,focus:null,selection:void 0}:null),r=!i&&!!n,l=o.querySelector(".mlv-workflow__composer"),a=l&&_e.get(l)||null;H(o),o.setAttribute("data-revision",e.revision.id),o.setAttribute("aria-label","Refinement");let d=f("button","mlv-btn mlv-btn--primary mlv-workflow__refine","Refine…");d.type="button",d.title="Copy a refinement prompt for your assistant about the selection or the whole diagram. MLView calls no model.",d.setAttribute("aria-expanded","false"),d.setAttribute("aria-haspopup","dialog");let c=t.chrome.refineSlot;H(c),c.appendChild(d),t.chrome.syncRoving();let u=g(o,f("form","mlv-workflow__composer"));u.hidden=!0,u.setAttribute("role","dialog"),u.setAttribute("aria-modal","true"),u.setAttribute("aria-label","Refine"),a&&_e.set(u,a);let h=g(u,f("span","mlv-workflow__selection")),p=g(u,f("select","mlv-input mlv-workflow__intent"));p.setAttribute("aria-label","Refinement intent");for(let[b,M]of vo){let T=p.ownerDocument.createElement("option");T.value=b,T.textContent=M,p.appendChild(T)}let m=g(u,f("input","mlv-input mlv-workflow__custom"));m.type="text",m.maxLength=500,m.placeholder="What should the assistant refine?",m.setAttribute("aria-label","Custom refinement intent"),m.hidden=!0;let v=g(u,f("button","mlv-btn","Copy prompt"));v.type="submit";let y=g(u,f("span","mlv-workflow__status"));y.setAttribute("role","status"),g(u,f("p","mlv-workflow__hint","Copies a prompt for your assistant to paste. MLView calls no model."));let S=()=>t.selection?{kind:t.selection.kind,id:t.selection.id}:void 0,x,w=b=>{x=b,h.textContent=Kd(t,b),b?(h.title=b.kind+" id: "+b.id,u.setAttribute("data-selection-kind",b.kind),u.setAttribute("data-selection-id",b.id)):(h.removeAttribute("title"),u.removeAttribute("data-selection-kind"),u.removeAttribute("data-selection-id"))},E=()=>w(S());_(d,"click",()=>{if(u.hidden){let b=d.ownerDocument.activeElement;b&&!u.contains(b)&&b!==d.ownerDocument.body?_e.set(u,b):_e.delete(u)}u.hidden=!u.hidden,d.setAttribute("aria-expanded",u.hidden?"false":"true"),u.hidden||(E(),p.focus()),t.saveSoon()}),_(u,"keydown",b=>{if(Bt(u,b)){b.stopPropagation();return}b.key==="Escape"&&(b.preventDefault(),b.stopPropagation(),yo(t,!0))}),_(p,"change",()=>{m.hidden=p.value!=="custom",m.hidden||m.focus(),t.saveSoon()}),_(m,"input",()=>t.saveSoon()),_(u,"submit",b=>{b.preventDefault();let M=p.value,T=m.value.trim();if(M==="custom"&&!T){m.focus();return}y.textContent="";let L={v:1,type:"refineWorkflow",revisionId:e.revision.id,intent:M};M==="custom"&&(L.customText=T),x&&(L.selection=x),t.postRequest(L,C=>Wd(t,C))}),s&&(vo.some(([b])=>b===s.intent)&&(p.value=s.intent),m.value=s.custom,m.hidden=p.value!=="custom",s.open&&(u.hidden=!1,d.setAttribute("aria-expanded","true"),s.revision===e.revision.id&&!r?w(s.selection):E()),s.focus==="refine"?d.focus():s.focus==="intent"&&!u.hidden?p.focus():s.focus==="custom"&&!u.hidden&&!m.hidden?m.focus():s.focus==="submit"&&!u.hidden&&v.focus())}function bo(t){let e=t.root.querySelector(".mlv-workflow__composer");return!!e&&!e.hidden}function yo(t,e=!1){let n=t.root.querySelector(".mlv-workflow__composer"),o=t.root.querySelector(".mlv-workflow__refine");if(!n||n.hidden)return!1;n.hidden=!0;let i=_e.get(n)||null;return _e.delete(n),o&&o.setAttribute("aria-expanded","false"),e&&at(i,o),t.saveSoon(),!0}var Qt=class{constructor(e){this.armed=null;this.consumed=!1;this.disposers=[];this.disposers.push(_(e,"mousedown",n=>{n.detail>=2&&(this.armed||this.consumed)&&n.preventDefault()},!0),_(e,"click",n=>this.onClick(n),!0),_(e,"dblclick",n=>{this.consumed&&(n.preventDefault(),n.stopPropagation())},!0))}arm(e,n){!e||e.detail!==1||(this.armed=n,this.consumed=!1)}dispose(){for(let e of this.disposers)e();this.disposers=[],this.armed=null}onClick(e){if(e.detail>=2&&(this.armed||this.consumed)){e.preventDefault(),e.stopPropagation();let n=this.armed;this.armed=null,this.consumed=!0,n&&n();return}this.armed=null,this.consumed=!1}};var Ks=Object.defineProperty,qd=(t,e,n)=>e in t?Ks(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,Ud=(t,e)=>{for(var n in e)Ks(t,n,{get:e[n],enumerable:!0})},Xd=(t,e,n)=>qd(t,typeof e!="symbol"?e+"":e,n),qs={};Ud(qs,{Graph:()=>ae,alg:()=>Ao});var Yd=Object.defineProperty,Us=(t,e)=>{for(var n in e)Yd(t,n,{get:e[n],enumerable:!0})},Lo=class{constructor(t){this._isDirected=!0,this._isMultigraph=!1,this._isCompound=!1,this._nodes={},this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={},this._nodeCount=0,this._edgeCount=0,this._defaultNodeLabelFn=()=>{},this._defaultEdgeLabelFn=()=>{},t&&(this._isDirected="directed"in t?t.directed:!0,this._isMultigraph="multigraph"in t?t.multigraph:!1,this._isCompound="compound"in t?t.compound:!1),this._isCompound&&(this._parent={},this._children={},this._children["\0"]={})}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(t){return this._label=t,this}graph(){return this._label}setDefaultNodeLabel(t){return typeof t!="function"?this._defaultNodeLabelFn=()=>t:this._defaultNodeLabelFn=t,this}nodeCount(){return this._nodeCount}nodes(){return Object.keys(this._nodes)}sources(){return this.nodes().filter(t=>Object.keys(this._in[t]).length===0)}sinks(){return this.nodes().filter(t=>Object.keys(this._out[t]).length===0)}setNodes(t,e){return t.forEach(n=>{e!==void 0?this.setNode(n,e):this.setNode(n)}),this}setNode(t,e){return t in this._nodes?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]="\0",this._children[t]={},this._children["\0"][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)}node(t){return this._nodes[t]}hasNode(t){return t in this._nodes}removeNode(t){if(t in this._nodes){let e=n=>this.removeEdge(this._edgeObjs[n]);delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],this.children(t).forEach(n=>{this.setParent(n)}),delete this._children[t]),Object.keys(this._in[t]).forEach(e),delete this._in[t],delete this._preds[t],Object.keys(this._out[t]).forEach(e),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this}setParent(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(e===void 0)e="\0";else{e+="";for(let n=e;n!==void 0;n=this.parent(n))if(n===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this}parent(t){if(this._isCompound){let e=this._parent[t];if(e!=="\0")return e}}children(t="\0"){if(this._isCompound){let e=this._children[t];if(e)return Object.keys(e)}else{if(t==="\0")return this.nodes();if(this.hasNode(t))return[]}return[]}predecessors(t){let e=this._preds[t];if(e)return Object.keys(e)}successors(t){let e=this._sucs[t];if(e)return Object.keys(e)}neighbors(t){let e=this.predecessors(t);if(e){let n=new Set(e),o=this.successors(t);if(o)for(let i of o)n.add(i);return Array.from(n.values())}}isLeaf(t){var e;let n;return this.isDirected()?n=this.successors(t):n=this.neighbors(t),((e=n?.length)!=null?e:0)===0}filterNodes(t){let e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph()),Object.entries(this._nodes).forEach(([i,s])=>{t(i)&&e.setNode(i,s)}),Object.values(this._edgeObjs).forEach(i=>{e.hasNode(i.v)&&e.hasNode(i.w)&&e.setEdge(i,this.edge(i))});let n={},o=i=>{let s=this.parent(i);return!s||e.hasNode(s)?(n[i]=s,s):s in n?n[s]:o(s)};return this._isCompound&&e.nodes().forEach(i=>e.setParent(i,o(i))),e}setDefaultEdgeLabel(t){return typeof t!="function"?this._defaultEdgeLabelFn=()=>t:this._defaultEdgeLabelFn=t,this}edgeCount(){return this._edgeCount}edges(){return Object.values(this._edgeObjs)}setPath(t,e){return t.reduce((n,o)=>(e!==void 0?this.setEdge(n,o,e):this.setEdge(n,o),o)),this}setEdge(t,e,n,o){let i,s,r,l,a=!1;typeof t=="object"&&t!==null&&"v"in t?(i=t.v,s=t.w,r=t.name,arguments.length===2&&(l=e,a=!0)):(i=t,s=e,r=o,arguments.length>2&&(l=n,a=!0)),i=""+i,s=""+s,r!==void 0&&(r=""+r);let d=mt(this._isDirected,i,s,r);if(d in this._edgeLabels)return a&&(this._edgeLabels[d]=l),this;if(r!==void 0&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(i),this.setNode(s),this._edgeLabels[d]=a?l:this._defaultEdgeLabelFn(i,s,r);let c=Zd(this._isDirected,i,s,r);return i=c.v,s=c.w,Object.freeze(c),this._edgeObjs[d]=c,ks(this._preds[s],i),ks(this._sucs[i],s),this._in[s][d]=c,this._out[i][d]=c,this._edgeCount++,this}edge(t,e,n){let o=arguments.length===1?xo(this._isDirected,t):mt(this._isDirected,t,e,n);return this._edgeLabels[o]}edgeAsObj(t,e,n){let o=arguments.length===1?this.edge(t):this.edge(t,e,n);return typeof o!="object"||o===null?{label:o}:o}hasEdge(t,e,n){return(arguments.length===1?xo(this._isDirected,t):mt(this._isDirected,t,e,n))in this._edgeLabels}removeEdge(t,e,n){let o=arguments.length===1?xo(this._isDirected,t):mt(this._isDirected,t,e,n),i=this._edgeObjs[o];if(i){let s=i.v,r=i.w;delete this._edgeLabels[o],delete this._edgeObjs[o],Ls(this._preds[r],s),Ls(this._sucs[s],r),delete this._in[r][o],delete this._out[s][o],this._edgeCount--}return this}inEdges(t,e){return this.isDirected()?this.filterEdges(this._in[t],t,e):this.nodeEdges(t,e)}outEdges(t,e){return this.isDirected()?this.filterEdges(this._out[t],t,e):this.nodeEdges(t,e)}nodeEdges(t,e){if(t in this._nodes)return this.filterEdges({...this._in[t],...this._out[t]},t,e)}_removeFromParentsChildList(t){delete this._children[this._parent[t]][t]}filterEdges(t,e,n){if(!t)return;let o=Object.values(t);return n?o.filter(i=>i.v===e&&i.w===n||i.v===n&&i.w===e):o}};function ks(t,e){t[e]?t[e]++:t[e]=1}function Ls(t,e){t[e]!==void 0&&!--t[e]&&delete t[e]}function mt(t,e,n,o){let i=""+e,s=""+n;if(!t&&i>s){let r=i;i=s,s=r}return i+""+s+""+(o===void 0?"\0":o)}function Zd(t,e,n,o){let i=""+e,s=""+n;if(!t&&i>s){let l=i;i=s,s=l}let r={v:i,w:s};return o&&(r.name=o),r}function xo(t,e){return mt(t,e.v,e.w,e.name)}var Qd={};Us(Qd,{read:()=>tc,write:()=>$d});function $d(t){let e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:Jd(t),edges:ec(t)},n=t.graph();return n!==void 0&&(e.value=structuredClone(n)),e}function Jd(t){return t.nodes().map(e=>{let n=t.node(e),o=t.parent(e),i={v:e};return n!==void 0&&(i.value=n),o!==void 0&&(i.parent=o),i})}function ec(t){return t.edges().map(e=>{let n=t.edge(e),o={v:e.v,w:e.w};return e.name!==void 0&&(o.name=e.name),n!==void 0&&(o.value=n),o})}function tc(t){let e=new Lo(t.options);return t.value!==void 0&&e.setGraph(t.value),t.nodes.forEach(n=>{e.setNode(n.v,n.value),n.parent&&e.setParent(n.v,n.parent)}),t.edges.forEach(n=>{e.setEdge({v:n.v,w:n.w,name:n.name},n.value)}),e}var Ao={};Us(Ao,{CycleException:()=>Jt,bellmanFord:()=>Xs,components:()=>ic,dijkstra:()=>$t,dijkstraAll:()=>lc,findCycles:()=>ac,floydWarshall:()=>cc,isAcyclic:()=>hc,postorder:()=>pc,preorder:()=>mc,prim:()=>gc,shortestPaths:()=>vc,tarjan:()=>Zs,topsort:()=>Qs});var nc=()=>1;function Xs(t,e,n,o){return oc(t,String(e),n||nc,o||function(i){var s;return(s=t.outEdges(i))!=null?s:[]})}function oc(t,e,n,o){let i={},s,r=0,l=t.nodes(),a=function(u){let h=i[u.v],p=i[u.w];if(!h||!p)return;let m=n(u);h.distance+mt.key)}has(t){return t in this._keyIndices}priority(t){let e=this._keyIndices[t];if(e!==void 0)return this._arr[e].priority}min(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key}add(t,e){let n=this._keyIndices,o=String(t);if(!(o in n)){let i=this._arr,s=i.length;return n[o]=s,i.push({key:o,priority:e}),this._decrease(s),!0}return!1}removeMin(){if(this.size()===0)throw new Error("Queue underflow");this._swap(0,this._arr.length-1);let t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key}decrease(t,e){let n=this._keyIndices[t];if(n===void 0)throw new Error(`Key not found: ${t}`);let o=this._arr[n].priority;if(e>o)throw new Error(`New priority is greater than current priority. Key: ${t} Old: ${o} New: ${e}`);this._arr[n].priority=e,this._decrease(n)}_heapify(t){let e=this._arr,n=2*t,o=n+1,i=t;n>1,!(e[o].priority1;function $t(t,e,n,o){let i=function(s){var r;return(r=t.outEdges(s))!=null?r:[]};return rc(t,String(e),n||sc,o||i)}function rc(t,e,n,o){let i={},s=new Ys,r,l,a=function(d){let c=d.v!==r?d.v:d.w,u=i[c];if(!u)return;let h=n(d),p=l.distance+h;if(h<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+d+" Weight: "+h);p0;){r=s.removeMin();let d=i[r];if(!d||d.distance===Number.POSITIVE_INFINITY)break;l=d,o(r).forEach(a)}return i}function lc(t,e,n){return t.nodes().reduce(function(o,i){return o[i]=$t(t,i,e,n),o},{})}function Zs(t){let e=0,n=[],o={},i=[];function s(r){var l;let a=o[r]={onStack:!0,lowlink:e,index:e++};if(n.push(r),(l=t.successors(r))==null||l.forEach(function(d){if(d in o){let c=o[d];c!=null&&c.onStack&&(a.lowlink=Math.min(a.lowlink,c.index))}else{s(d);let c=o[d];c&&(a.lowlink=Math.min(a.lowlink,c.lowlink))}}),a.lowlink===a.index){let d=[],c;do{c=n.pop();let u=o[c];u&&(u.onStack=!1),d.push(c)}while(r!==c);i.push(d)}}return t.nodes().forEach(function(r){r in o||s(r)}),i}function ac(t){return Zs(t).filter(function(e){var n;let o=e[0];return o?e.length>1||e.length===1&&((n=t.outEdges(o,o))!=null?n:[]).length>0:!1})}var dc=()=>1;function cc(t,e,n){return uc(t,e||dc,n||function(o){var i;return(i=t.outEdges(o))!=null?i:[]})}function uc(t,e,n){let o={},i=t.nodes();return i.forEach(function(s){let r={};o[s]=r,r[s]={distance:0,predecessor:""},i.forEach(function(l){s!==l&&(r[l]={distance:Number.POSITIVE_INFINITY,predecessor:""})}),n(s).forEach(function(l){let a=l.v===s?l.w:l.v,d=e(l);r[a]={distance:d,predecessor:s}})}),i.forEach(function(s){let r=o[s];r&&i.forEach(function(l){let a=o[l];a&&i.forEach(function(d){let c=a[s],u=r[d],h=a[d];if(c&&u&&h){let p=c.distance+u.distance;p{var a;return(a=t.isDirected()?t.successors(l):t.neighbors(l))!=null?a:[]}),r={};return e.forEach(function(l){if(!t.hasNode(l))throw new Error("Graph does not have node: "+l);i=$s(t,l,n==="post",r,s,o,i)}),i}function $s(t,e,n,o,i,s,r){return e in o||(o[e]=!0,n||(r=s(r,e)),i(e).forEach(function(l){r=$s(t,l,n,o,i,s,r)}),n&&(r=s(r,e))),r}function Js(t,e,n){return fc(t,e,n,function(o,i){return o.push(i),o},[])}function pc(t,e){return Js(t,e,"post")}function mc(t,e){return Js(t,e,"pre")}function gc(t,e){var n;let o=new Lo,i={},s=new Ys,r;function l(c){let u=c.v===r?c.w:c.v,h=s.priority(u);if(h!==void 0){let p=e(c);p0;){if(r=s.removeMin(),r in i)o.setEdge(r,i[r]);else{if(d)throw new Error("Input graph is not connected: "+t);d=!0}(n=t.nodeEdges(r))==null||n.forEach(l)}return o}function vc(t,e,n,o){return bc(t,e,n,o??(i=>{var s;return(s=t.outEdges(i))!=null?s:[]}))}function bc(t,e,n,o){if(n===void 0)return $t(t,e,n,o);let i=!1,s=t.nodes();for(let r=0;re.setNode(n,t.node(n))),t.edges().forEach(n=>{let o=e.edge(n.v,n.w)||{weight:0,minlen:1},i=t.edge(n);e.setEdge(n.v,n.w,{weight:o.weight+i.weight,minlen:Math.max(o.minlen,i.minlen)})}),e}function er(t){let e=new ae({multigraph:t.isMultigraph()}).setGraph(t.graph());return t.nodes().forEach(n=>{t.children(n).length||e.setNode(n,t.node(n))}),t.edges().forEach(n=>{e.setEdge(n,t.edge(n))}),e}function As(t,e){let n=t.x,o=t.y,i=e.x-n,s=e.y-o,r=t.width/2,l=t.height/2;if(!i&&!s)throw new Error("Not possible to find intersection inside of the rectangle");let a,d;return Math.abs(s)*r>Math.abs(i)*l?(s<0&&(l=-l),a=l*i/s,d=l):(i<0&&(r=-r),a=r,d=r*s/i),{x:n+a,y:o+d}}function vt(t){let e=gt(nr(t)+1).map(()=>[]);return t.nodes().forEach(n=>{let o=t.node(n),i=o.rank;i!==void 0&&(e[i]||(e[i]=[]),e[i][o.order]=n)}),e}function xc(t){let e=t.nodes().map(o=>{let i=t.node(o).rank;return i===void 0?Number.MAX_VALUE:i}),n=ce(Math.min,e);t.nodes().forEach(o=>{let i=t.node(o);Object.hasOwn(i,"rank")&&(i.rank-=n)})}function wc(t){let e=t.nodes().map(r=>t.node(r).rank).filter(r=>r!==void 0),n=ce(Math.min,e),o=[];t.nodes().forEach(r=>{let l=t.node(r).rank-n;o[l]||(o[l]=[]),o[l].push(r)});let i=0,s=t.graph().nodeRankFactor;Array.from(o).forEach((r,l)=>{r===void 0&&l%s!==0?--i:r!==void 0&&i&&r.forEach(a=>t.node(a).rank+=i)})}function Is(t,e,n,o){let i={width:0,height:0};return arguments.length>=4&&(i.rank=n,i.order=o),Ye(t,"border",i,e)}function Ec(t,e=tr){let n=[];for(let o=0;otr){let n=Ec(e);return t(...n.map(o=>t(...o)))}else return t(...e)}function nr(t){let e=t.nodes().map(n=>{let o=t.node(n).rank;return o===void 0?Number.MIN_VALUE:o});return ce(Math.max,e)}function _c(t,e){let n={lhs:[],rhs:[]};return t.forEach(o=>{e(o)?n.lhs.push(o):n.rhs.push(o)}),n}function Sc(t,e){let n=Date.now();try{return e()}finally{console.log(t+" time: "+(Date.now()-n)+"ms")}}function or(t,e){return e()}var Mc=0;function Io(t){let e=++Mc;return t+(""+e)}function gt(t,e,n=1){e==null&&(e=t,t=0);let o=s=>seo[e]:n=e,Object.entries(t).reduce((o,[i,s])=>(o[i]=n(s,i),o),{})}function kc(t,e){return t.reduce((n,o,i)=>(n[o]=e[i],n),{})}var nn="\0";function ir(t,e,n){var o,i,s,r,l,a;if(!(t&&e&&n&&e.dummy==="edge"&&n.dummy==="edge"&&e.edgeObj&&n.edgeObj&&t[e.edgeObj.v]&&t[n.edgeObj.v]&&t[e.edgeObj.w]&&t[n.edgeObj.w]))return 0;let d=!0;e.edgeObj.w===n.edgeObj.w&&(d=!1);let c=d?(i=(o=t[e.edgeObj.v])==null?void 0:o.rank)!=null?i:NaN+1:(r=(s=t[e.edgeObj.w])==null?void 0:s.rank)!=null?r:NaN-1,u=Object.entries(t).find(v=>{var y,S;return((y=v[1].edgeObj)==null?void 0:y.v)===e.edgeObj.v&&((S=v[1].edgeObj)==null?void 0:S.w)===e.edgeObj.w&&v[1].rank===c}),h=Object.entries(t).find(v=>{var y,S;return((y=v[1].edgeObj)==null?void 0:y.v)===n.edgeObj.v&&((S=v[1].edgeObj)==null?void 0:S.w)===n.edgeObj.w&&v[1].rank===c});if(!u||!h)return 0;let p=(l=u[1].order)!=null?l:NaN,m=(a=h[1].order)!=null?a:NaN;return isNaN(p-m)?0:p-m}var Lc="3.1.1",Ac=class{constructor(){Xd(this,"_sentinel");let t={};t._next=t._prev=t,this._sentinel=t}dequeue(){let t=this._sentinel,e=t._prev;if(e!==t)return Cs(e),e}enqueue(t){let e=this._sentinel;t._prev&&t._next&&Cs(t),t._next=e._next,e._next._prev=t,e._next=t,t._prev=e}toString(){let t=[],e=this._sentinel,n=e._prev;for(;n!==e;)t.push(JSON.stringify(n,Ic)),n=n._prev;return"["+t.join(", ")+"]"}};function Cs(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function Ic(t,e){if(t!=="_next"&&t!=="_prev")return e}var Cc=Ac,Tc=()=>1;function Rc(t,e){if(t.nodeCount()<=1)return[];let n=Hc(t,e||Tc);return Oc(n.graph,n.buckets,n.zeroIdx).flatMap(o=>t.outEdges(o.v,o.w)||[])}function Oc(t,e,n){var o;let i=[],s=e[e.length-1],r=e[0],l;for(;t.nodeCount();){for(;l=r.dequeue();)wo(t,e,n,l);for(;l=s.dequeue();)wo(t,e,n,l);if(t.nodeCount()){for(let a=e.length-2;a>0;--a)if(l=(o=e[a])==null?void 0:o.dequeue(),l){i=i.concat(wo(t,e,n,l,!0)||[]);break}}}return i}function wo(t,e,n,o,i){let s=[],r=i?s:void 0;return(t.inEdges(o.v)||[]).forEach(l=>{let a=t.edge(l),d=t.node(l.v);i&&s.push({v:l.v,w:l.w}),d.out-=a,ko(e,n,d)}),(t.outEdges(o.v)||[]).forEach(l=>{let a=t.edge(l),d=l.w,c=t.node(d);c.in-=a,ko(e,n,c)}),t.removeNode(o.v),r}function Hc(t,e){let n=new ae,o=0,i=0;t.nodes().forEach(l=>{n.setNode(l,{v:l,in:0,out:0})}),t.edges().forEach(l=>{let a=n.edge(l.v,l.w)||0,d=e(l),c=a+d;n.setEdge(l.v,l.w,c);let u=n.node(l.v),h=n.node(l.w);i=Math.max(i,u.out+=d),o=Math.max(o,h.in+=d)});let s=Nc(i+o+3).map(()=>new Cc),r=o+1;return n.nodes().forEach(l=>{ko(s,r,n.node(l))}),{graph:n,buckets:s,zeroIdx:r}}function ko(t,e,n){var o,i,s;n.out?n.in?(s=t[n.out-n.in+e])==null||s.enqueue(n):(i=t[t.length-1])==null||i.enqueue(n):(o=t[0])==null||o.enqueue(n)}function Nc(t){let e=[];for(let n=0;n{let i=t.edge(o);t.removeEdge(o),i.forwardName=o.name,i.reversed=!0,t.setEdge(o.w,o.v,i,Io("rev"))});function n(o){return i=>o.edge(i).weight}}function Fc(t,e){let n=[],o={},i={};function s(a){Object.hasOwn(i,a)||(i[a]=!0,o[a]=!0,t.outEdges(a).forEach(d=>{Object.hasOwn(o,d.w)?n.push(d):s(d.w)}),delete o[a])}function r(a){var d;Object.hasOwn(i,a)||(i[a]=!0,o[a]=!0,(d=t.outEdges(a))==null||d.forEach(c=>{var u,h;Object.hasOwn(o,c.w)||((u=e.node(a))==null?void 0:u.rank)>((h=e.node(c.w))==null?void 0:h.rank)&&Dc(t,c.w,c)?n.push(c):r(c.w)}),delete o[a])}let l=s;return e&&typeof e.node=="function"&&(l=r),t.sources().forEach(l),t.nodes().forEach(l),n}function Bc(t){t.edges().forEach(e=>{let n=t.edge(e);if(n.reversed){t.removeEdge(e);let o=n.forwardName;delete n.reversed,delete n.forwardName,t.setEdge(e.w,e.v,n,o)}})}function Dc(t,e,n){let o=new Set;function i(s){var r;if(t.sources().includes(s))return!0;o.add(s);for(let l of(r=t.inEdges(s))!=null?r:[])if(!(l.v===n.v&&l.w===n.w)&&!o.has(l.v)&&i(l.v))return!0;return!1}return i(e)}function jc(t){t.graph().dummyChains=[],t.edges().forEach(e=>Gc(t,e))}function Gc(t,e){let n=e.v,o=t.node(n).rank,i=e.w,s=t.node(i).rank,r=e.name,l=t.edge(e),a=l.labelRank;if(s===o+1)return;t.removeEdge(e);let d,c,u;for(u=0,++o;o{let n=t.node(e),o=n.edgeLabel,i;for(t.setEdge(n.edgeObj,o);n.dummy;)i=t.successors(e)[0],t.removeNode(e),o.points.push({x:n.x,y:n.y}),n.dummy==="edge-label"&&(o.x=n.x,o.y=n.y,o.width=n.width,o.height=n.height),e=i,n=t.node(e)})}function Co(t){let e={};function n(o){let i=t.node(o);if(Object.hasOwn(e,o))return i.rank;e[o]=!0;let s=t.outEdges(o),r=s?s.map(a=>a==null?Number.POSITIVE_INFINITY:n(a.w)-t.edge(a).minlen):[],l=ce(Math.min,r);return l===Number.POSITIVE_INFINITY&&(l=0),i.rank=l}t.sources().forEach(n)}function Xe(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}var sr=Vc;function Vc(t){let e=new ae({directed:!1}),n=t.nodes();if(n.length===0)throw new Error("Graph must have at least one node");let o=n[0],i=t.nodeCount();e.setNode(o,{});let s,r;for(;Wc(e,t){let r=s.v,l=o===r?s.w:r;!t.hasNode(l)&&!Xe(e,s)&&(t.setNode(l,{}),t.setEdge(o,l,{}),n(l))})}return t.nodes().forEach(n),t.nodeCount()}function Kc(t,e){return e.edges().reduce((n,o)=>{let i=Number.POSITIVE_INFINITY;return t.hasNode(o.v)!==t.hasNode(o.w)&&(i=Xe(e,o)),ie.node(o).rank+=n)}var{preorder:Uc,postorder:Xc}=Ao,Yc=Ne;Ne.initLowLimValues=Ro;Ne.initCutValues=To;Ne.calcCutValue=rr;Ne.leaveEdge=ar;Ne.enterEdge=dr;Ne.exchangeEdges=cr;function Ne(t){t=yc(t),Co(t);let e=sr(t);Ro(e),To(e,t);let n,o;for(;n=ar(e);)o=dr(e,t,n),cr(e,t,n,o)}function To(t,e){let n=Xc(t,t.nodes());n=n.slice(0,n.length-1),n.forEach(o=>Zc(t,e,o))}function Zc(t,e,n){let o=t.node(n).parent,i=t.edge(n,o);i.cutvalue=rr(t,e,n)}function rr(t,e,n){let o=t.node(n).parent,i=!0,s=e.edge(n,o),r=0;s||(i=!1,s=e.edge(o,n)),r=s.weight;let l=e.nodeEdges(n);return l&&l.forEach(a=>{let d=a.v===n,c=d?a.w:a.v;if(c!==o){let u=d===i,h=e.edge(a).weight;if(r+=u?h:-h,$c(t,n,c)){let p=t.edge(n,c).cutvalue;r+=u?-p:p}}}),r}function Ro(t,e){arguments.length<2&&(e=t.nodes()[0]),lr(t,{},1,e)}function lr(t,e,n,o,i){let s=n,r=t.node(o);e[o]=!0;let l=t.neighbors(o);return l&&l.forEach(a=>{Object.hasOwn(e,a)||(n=lr(t,e,n,a,o))}),r.low=s,r.lim=n++,i?r.parent=i:delete r.parent,n}function ar(t){return t.edges().find(e=>t.edge(e).cutvalue<0)}function dr(t,e,n){let o=n.v,i=n.w;e.hasEdge(o,i)||(o=n.w,i=n.v);let s=t.node(o),r=t.node(i),l=s,a=!1;return s.lim>r.lim&&(l=r,a=!0),e.edges().filter(d=>a===Ts(t,t.node(d.v),l)&&a!==Ts(t,t.node(d.w),l)).reduce((d,c)=>Xe(e,c)!t.node(i).parent);if(!n)return;let o=Uc(t,[n]);o=o.slice(1),o.forEach(i=>{let s=t.node(i).parent,r=e.edge(i,s),l=!1;r||(r=e.edge(s,i),l=!0),e.node(i).rank=e.node(s).rank+(l?r.minlen:-r.minlen)})}function $c(t,e,n){return t.hasEdge(e,n)}function Ts(t,e,n){return n.low<=e.lim&&e.lim<=n.lim}var Jc=eu;function eu(t){let e=t.graph().ranker;if(typeof e=="function")return e(t);switch(e){case"network-simplex":Rs(t);break;case"tight-tree":nu(t);break;case"longest-path":tu(t);break;case"none":break;default:Rs(t)}}var tu=Co;function nu(t){Co(t),sr(t)}function Rs(t){Yc(t)}var ou=iu;function iu(t){let e=ru(t),n=t.graph();Array.isArray(n.dummyChains)&&n.dummyChains.forEach(o=>{let i=t.node(o),s=i.edgeObj,r=su(t,e,s.v,s.w),l=r.path,a=r.lca,d=0,c=l[d],u=!0;for(;o!==s.w;){if(i=t.node(o),u){for(;(c=l[d])!==a&&t.node(c).maxRankr||l>e[a].lim));let d=a,c=o;for(;(c=t.parent(c))!==d;)s.push(c);return{path:i.concat(s.reverse()),lca:d}}function ru(t){let e={},n=0;function o(i){let s=n;t.children(i).forEach(o),e[i]={low:s,lim:n++}}return t.children(nn).forEach(o),e}function lu(t){let e=Ye(t,"root",{},"_root"),n=au(t),o=Object.values(n),i=ce(Math.max,o)-1,s=2*i+1;t.graph().nestingRoot=e,t.edges().forEach(l=>t.edge(l).minlen*=s);let r=du(t)+1;t.children(nn).forEach(l=>{ur(t,e,s,r,i,n,l)}),t.graph().nodeRankFactor=s}function ur(t,e,n,o,i,s,r){var l;let a=t.children(r);if(!a.length){r!==e&&t.setEdge(e,r,{weight:0,minlen:n});return}let d=Is(t,"_bt"),c=Is(t,"_bb"),u=t.node(r);t.setParent(d,r),u.borderTop=d,t.setParent(c,r),u.borderBottom=c,a.forEach(h=>{var p;ur(t,e,n,o,i,s,h);let m=t.node(h),v=m.borderTop?m.borderTop:h,y=m.borderBottom?m.borderBottom:h,S=m.borderTop?o:2*o,x=v!==y?1:i-((p=s[r])!=null?p:0)+1;t.setEdge(d,v,{weight:S,minlen:x,nestingEdge:!0}),t.setEdge(y,c,{weight:S,minlen:x,nestingEdge:!0})}),t.parent(r)||t.setEdge(e,d,{weight:0,minlen:i+((l=s[r])!=null?l:0)})}function au(t){let e={};function n(o,i){let s=t.children(o);s&&s.length&&s.forEach(r=>n(r,i+1)),e[o]=i}return t.children(nn).forEach(o=>n(o,1)),e}function du(t){return t.edges().reduce((e,n)=>e+t.edge(n).weight,0)}function cu(t){let e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,t.edges().forEach(n=>{t.edge(n).nestingEdge&&t.removeEdge(n)})}var uu=hu;function hu(t){function e(n){let o=t.children(n),i=t.node(n);if(o.length&&o.forEach(e),i&&Object.hasOwn(i,"minRank")){i.borderLeft=[],i.borderRight=[];for(let s=i.minRank,r=i.maxRank+1;sHs(t.node(e))),t.edges().forEach(e=>Hs(t.edge(e)))}function Hs(t){let e=t.width;t.width=t.height,t.height=e}function mu(t){t.nodes().forEach(e=>Eo(t.node(e))),t.edges().forEach(e=>{var n;let o=t.edge(e);(n=o.points)==null||n.forEach(Eo),Object.hasOwn(o,"y")&&Eo(o)})}function Eo(t){t.y=-t.y}function gu(t){t.nodes().forEach(e=>_o(t.node(e))),t.edges().forEach(e=>{var n;let o=t.edge(e);(n=o.points)==null||n.forEach(_o),Object.hasOwn(o,"x")&&_o(o)})}function _o(t){let e=t.x;t.x=t.y,t.y=e}function vu(t,e=null){let n={},o=t.nodes().filter(d=>!t.children(d).length),i=o.map(d=>t.node(d).rank),s=ce(Math.max,i),r=gt(s+1).map(()=>[]);function l(d){if(n[d])return;n[d]=!0;let c=t.node(d);r[c.rank].push(d);let u=t.successors(d);u&&[...u].sort((h,p)=>a(h,p)).forEach(l)}o.sort((d,c)=>t.node(d).rank-t.node(c).rank).forEach(l);function a(d,c){let u=t.node(d),h=t.node(c);return ir(e,u,h)}return r}function bu(t,e){let n=0;for(let o=1;oc)),i=e.flatMap(d=>{let c=t.outEdges(d);return c?c.map(u=>({pos:o[u.w],weight:t.edge(u).weight})).sort((u,h)=>u.pos-h.pos):[]}),s=1;for(;s{let c=d.pos+s;l[c]+=d.weight;let u=0;for(;c>0;)c%2&&(u+=l[c+1]),c=c-1>>1,l[c]+=d.weight;a+=d.weight*u}),a}function xu(t,e=[]){return e.map(n=>{let o=t.inEdges(n);if(!o||!o.length)return{v:n};{let i=o.reduce((s,r)=>{let l=t.edge(r),a=t.node(r.v);return{sum:s.sum+l.weight*a.order,weight:s.weight+l.weight}},{sum:0,weight:0});return{v:n,barycenter:i.sum/i.weight,weight:i.weight}}})}function wu(t,e){let n={};t.forEach((i,s)=>{let r={indegree:0,in:[],out:[],vs:[i.v],i:s};i.barycenter!==void 0&&(r.barycenter=i.barycenter,r.weight=i.weight),n[i.v]=r}),e.edges().forEach(i=>{let s=n[i.v],r=n[i.w];s!==void 0&&r!==void 0&&(r.indegree++,s.out.push(r))});let o=Object.values(n).filter(i=>!i.indegree);return Eu(o)}function Eu(t){let e=[];function n(i){return s=>{s.merged||(s.barycenter===void 0||i.barycenter===void 0||s.barycenter>=i.barycenter)&&_u(i,s)}}function o(i){return s=>{s.in.push(i),--s.indegree===0&&t.push(s)}}for(;t.length;){let i=t.pop();e.push(i),i.in.reverse().forEach(n(i)),i.out.forEach(o(i))}return e.filter(i=>!i.merged).map(i=>en(i,["vs","i","barycenter","weight"]))}function _u(t,e){let n=0,o=0;t.weight&&(n+=t.barycenter*t.weight,o+=t.weight),e.weight&&(n+=e.barycenter*e.weight,o+=e.weight),t.vs=e.vs.concat(t.vs),t.barycenter=n/o,t.weight=o,t.i=Math.min(e.i,t.i),e.merged=!0}function Su(t,e,n,o,i){let s={},r=null,l=null,a=i;typeof e=="boolean"?(a=e,s={}):e&&(s=e,r=n??null,l=o??null);let d=_c(t,S=>Object.hasOwn(S,"barycenter")),c=d.lhs,u=d.rhs.sort((S,x)=>x.i-S.i),h=[],p=0,m=0,v=0;c.sort(Mu(l,r,!!a));for(let[S,x]of Object.entries(s)){let w=c.findIndex(E=>E.vs[0]===S);c.splice(w+1,0,x)}v=Ns(h,u,v),c.forEach(S=>{v+=S.vs.length,h.push(S.vs),p+=S.barycenter*S.weight,m+=S.weight,v=Ns(h,u,v)});let y={vs:h.flat(1)};return m&&(y.barycenter=p/m,y.weight=m),y}function Ns(t,e,n){let o;for(;e.length&&(o=e[e.length-1]).i<=n;)e.pop(),t.push(o.vs),n++;return n}function Mu(t,e,n){return(o,i)=>{if(o.barycenteri.barycenter)return 1;if(t&&(typeof o.vs[0]=="string"||typeof i.vs[0]=="string")){let s=t.node(o.vs[0]),r=t.node(i.vs[0]),l=ir(e,s,r);if(l!==0)return l}return n?i.i-o.i:o.i-i.i}}function fr(t,e,n,o,i){var s,r,l,a,d,c,u,h,p,m,v;let y=null,S=i;typeof o=="boolean"?(S=o,y=null):o!==void 0&&(y=o);let x=t.children(e),w=t.node(e),E=w?w.borderLeft:void 0,b=w?w.borderRight:void 0,M={};E&&(x=x.filter(B=>B!==E&&B!==b));let T=xu(t,x);T.forEach(B=>{if(t.children(B.v).length){let{result:V}=fr(t,B.v,n,y,S);M[B.v]=V,Object.hasOwn(V,"barycenter")&&Lu(B,V)}});let L=wu(T,n);ku(L,M);let C={},N=!1;for(let B=0;B{n.vs=n.vs.flatMap(o=>e[o]?e[o].vs:o)})}function Lu(t,e){t.barycenter!==void 0?(t.barycenter=(t.barycenter*t.weight+e.barycenter*e.weight)/(t.weight+e.weight),t.weight+=e.weight):(t.barycenter=e.barycenter,t.weight=e.weight)}function Au(t,e,n,o){o||(o=t.nodes());let i=Iu(t),s=new ae({compound:!0}).setGraph({root:i}).setDefaultNodeLabel(r=>t.node(r));return o.forEach(r=>{let l=t.node(r),a=t.parent(r);if(l.rank===e||l.minRank<=e&&e<=l.maxRank){s.setNode(r),s.setParent(r,a||i);let d=t[n](r);d&&d.forEach(c=>{let u=c.v===r?c.w:c.v,h=s.edge(u,r),p=h!==void 0?h.weight:0;s.setEdge(u,r,{weight:t.edge(c).weight+p})}),Object.hasOwn(l,"minRank")&&s.setNode(r,{borderLeft:l.borderLeft[e],borderRight:l.borderRight[e]})}}),s}function Iu(t){let e;for(;t.hasNode(e=Io("_root")););return e}function Cu(t,e,n){let o={},i;n.forEach(s=>{let r=t.parent(s),l,a;for(;r;){if(l=t.parent(r),l?(a=o[l],o[l]=r):(a=i,i=r),a&&a!==r){e.setEdge(a,r);return}r=l}})}function pr(t,e={},n=null){if(typeof e.customOrder=="function"){e.customOrder(t,pr);return}let o=nr(t),i=Ps(t,gt(1,o+1),"inEdges"),s=Ps(t,gt(o-1,-1,-1),"outEdges"),r=vu(t,n);if(Fs(t,r),e.disableOptimalOrderHeuristic)return;let l=Number.POSITIVE_INFINITY,a,d=e.constraints||[];for(let c=0,u=0;u<4;++c,++u){Tu(c%2?i:s,c%4>=2,d,n),r=vt(t);let h=bu(t,r);h{o.has(s)||o.set(s,[]),o.get(s).push(r)};for(let s of t.nodes()){let r=t.node(s);if(typeof r.rank=="number"&&i(r.rank,s),typeof r.minRank=="number"&&typeof r.maxRank=="number")for(let l=r.minRank;l<=r.maxRank;l++)l!==r.rank&&i(l,s)}return e.map(function(s){return Au(t,s,n,o.get(s)||[])})}function Tu(t,e,n,o){let i=!0,s=new ae;t.forEach(function(r){n.forEach(c=>s.setEdge(c.left,c.right));let l=r.graph().root,{result:a,usedBias:d}=fr(r,l,s,o,i);e&&d&&(i=!i),a.vs.forEach((c,u)=>r.node(c).order=u),Cu(r,s,a.vs)})}function Fs(t,e){Object.values(e).forEach(n=>n.forEach((o,i)=>t.node(o).order=i))}function Ru(t,e){let n={};function o(i,s){let r=0,l=0,a=i.length,d=s[s.length-1];return s.forEach((c,u)=>{let h=Hu(t,c),p=h?t.node(h).order:a;(h||c===d)&&(s.slice(l,u+1).forEach(m=>{let v=t.predecessors(m);v&&v.forEach(y=>{let S=t.node(y),x=S.order;(x{let u=s[c];if(u!==void 0&&t.node(u).dummy){let h=t.predecessors(u);h&&h.forEach(p=>{if(p===void 0)return;let m=t.node(p);m.dummy&&(m.orderd)&&mr(n,p,u)})}})}function i(s,r){let l=-1,a=-1,d=0;return r.forEach((c,u)=>{if(t.node(c).dummy==="border"){let h=t.predecessors(c);if(h&&h.length){let p=h[0];if(p===void 0)return;a=t.node(p).order,o(r,d,u,l,a),d=u,l=a}}o(r,d,r.length,a,s.length)}),r}return e.length&&e.reduce(i),n}function Hu(t,e){if(t.node(e).dummy){let n=t.predecessors(e);if(n)return n.find(o=>t.node(o).dummy)}}function mr(t,e,n){if(e>n){let i=e;e=n,n=i}let o=t[e];o||(t[e]=o={}),o[n]=!0}function Nu(t,e,n){if(e>n){let i=e;e=n,n=i}let o=t[e];return o!==void 0&&Object.hasOwn(o,n)}function Pu(t,e,n,o,i){let s={},r={},l={};return e.forEach(a=>{a.forEach((d,c)=>{s[d]=d,r[d]=d,l[d]=c})}),e.forEach(a=>{let d=-1,c=-1,u=!1,h=a,p=a.findIndex(m=>i?.includes(m)||Bs(m,t,i));p>0&&(h=[a[p],...a.slice(0,p),...a.slice(p+1)],u=!0),h.forEach(m=>{var v;let y=o(m);if(y&&y.length){i!=null&&i.includes(m)&&(y=y.filter(w=>Bs(w,t,i)));let S=y.sort((w,E)=>{let b=l[w],M=l[E];return(b!==void 0?b:0)-(M!==void 0?M:0)}),x=(S.length-1)/2;for(let w=Math.floor(x),E=Math.ceil(x);w<=E;++w){let b=S[w];if(b===void 0)continue;let M=l[b];if(M!==void 0&&r[m]===m&&d{var S;let x=(S=s[y.v])!=null?S:0,w=r.edge(y);return Math.max(v,x+(w!==void 0?w:0))},0):s[p]=0}function c(p){let m=r.outEdges(p),v=Number.POSITIVE_INFINITY;m&&(v=m.reduce((S,x)=>{let w=s[x.w],E=r.edge(x);return Math.min(S,(w!==void 0?w:0)-(E!==void 0?E:0))},Number.POSITIVE_INFINITY));let y=t.node(p);v!==Number.POSITIVE_INFINITY&&y.borderType!==l&&(s[p]=Math.max(s[p]!==void 0?s[p]:0,v))}function u(p){return r.predecessors(p)||[]}function h(p){return r.successors(p)||[]}return a(d,u),a(c,h),Object.keys(o).forEach(p=>{var m;let v=n[p];v!==void 0&&(s[p]=(m=s[v])!=null?m:0)}),s}function Bu(t,e,n,o){let i=new ae,s=t.graph(),r=Vu(s.nodesep,s.edgesep,o);return e.forEach(l=>{let a;l.forEach(d=>{let c=n[d];if(c!==void 0){if(i.setNode(c),a!==void 0){let u=n[a];if(u!==void 0){let h=i.edge(u,c);i.setEdge(u,c,Math.max(r(t,d,a),h||0))}}a=d}})}),i}function Du(t,e){return Object.values(e).reduce((n,o)=>{let i=Number.NEGATIVE_INFINITY,s=Number.POSITIVE_INFINITY;Object.entries(o).forEach(([l,a])=>{let d=Wu(t,l)/2;i=Math.max(a+d,i),s=Math.min(a-d,s)});let r=i-s;return r{["l","r"].forEach(r=>{let l=s+r,a=t[l];if(!a||a===e)return;let d=Object.values(a),c=o-ce(Math.min,d);r!=="l"&&(c=i-ce(Math.max,d)),c&&(t[l]=tn(a,u=>u+c))})})}function Gu(t,e=void 0){let n=t.ul;return n?tn(n,(o,i)=>{var s,r;if(e){let a=e.toLowerCase(),d=t[a];if(d&&d[i]!==void 0)return d[i]}let l=Object.values(t).map(a=>{let d=a[i];return d!==void 0?d:0}).sort((a,d)=>a-d);return(((s=l[1])!=null?s:0)+((r=l[2])!=null?r:0))/2}):{}}function zu(t,e){let n=vt(t),o=Object.assign(Ru(t,n),Ou(t,n)),i={},s;["u","d"].forEach(l=>{s=l==="u"?n:Object.values(n).reverse(),["l","r"].forEach(a=>{a==="r"&&(s=s.map(u=>Object.values(u).reverse()));let d=Pu(t,s,o,u=>(l==="u"?t.predecessors(u):t.successors(u))||[],e),c=Fu(t,s,d.root,d.align,a==="r");a==="r"&&(c=tn(c,u=>-u)),i[l+a]=c})});let r=Du(t,i);return ju(i,r),Gu(i,t.graph().align)}function Vu(t,e,n){return(o,i,s)=>{let r=o.node(i),l=o.node(s),a=0,d;if(a+=r.width/2,Object.hasOwn(r,"labelpos"))switch(r.labelpos.toLowerCase()){case"l":d=-r.width/2;break;case"r":d=r.width/2;break}if(d&&(a+=n?d:-d),d=void 0,a+=(r.dummy?e:t)/2,a+=(l.dummy?e:t)/2,a+=l.width/2,Object.hasOwn(l,"labelpos"))switch(l.labelpos.toLowerCase()){case"l":d=l.width/2;break;case"r":d=-l.width/2;break}return d&&(a+=n?d:-d),a}}function Wu(t,e){return t.node(e).width}function Bs(t,e,n){var o;if(!n)return!1;let i=(o=e.node(t))==null?void 0:o.edgeObj;if(!i||e.node(t).edgeLabel.reversed)return!1;let s=n.indexOf(i?.v),r=n.indexOf(i?.w);return s!==-1&&r!==-1&&s===(r+1)%n.length||s===(r-1)%n.length}function Ku(t,e){t=er(t),qu(t),Object.entries(zu(t,e)).forEach(([n,o])=>t.node(n).x=o)}function qu(t){let e=vt(t),n=t.graph(),o=n.ranksep,i=n.rankalign,s=0;e.forEach(r=>{let l=r.reduce((a,d)=>{var c;let u=(c=t.node(d).height)!=null?c:0;return a>u?a:u},0);r.forEach(a=>{let d=t.node(a);i==="top"?d.y=s+d.height/2:i==="bottom"?d.y=s+l-d.height/2:d.y=s+l/2}),s+=l+o})}var Ds=new WeakMap;function Uu(t,e={}){return gr(t,or,e),t}function js(t,e,n){let o=e;for(;o!==void 0;){let i=t.parent(o);if(i===n)return o;o=i}}function gr(t,e,n){var o;let i=t.nodes().filter(x=>t.children(x).length),s={};i.forEach(x=>{let w=t.node(x);if(w&&w.rankdir){let E=new ae({multigraph:!0,compound:!0});E.setGraph({rankdir:w.rankdir});let b=t.children(x);b.forEach(z=>{let P={...t.node(z)};E.setNode(z,P);let K=t.parent(z);K&&K!==x&&b.includes(K)&&E.setParent(z,K)});let M=new Set;t.edges().forEach(z=>{let P=js(t,z.v,x),K=js(t,z.w,x);if(P&&K&&P!==K){let We=`${P}\0${K}`;M.has(We)||(M.add(We),E.setEdge(P,K,{...t.edge(z)}))}}),gr(E,e,n);let T=Ws(E);Gs(T,e,n,null),zs(E,T);let L=1/0,C=1/0,N=-1/0,O=-1/0;E.nodes().forEach(z=>{if(z===x)return;let P=E.node(z);P&&typeof P.x=="number"&&typeof P.y=="number"&&typeof P.width=="number"&&typeof P.height=="number"&&(L=Math.min(L,P.x-P.width/2),N=Math.max(N,P.x+P.width/2),C=Math.min(C,P.y-P.height/2),O=Math.max(O,P.y+P.height/2))}),(!isFinite(L)||!isFinite(C)||!isFinite(N)||!isFinite(O))&&(L=C=0,N=O=0);let B=N-L,V=O-C;s[x]={minX:L,minY:C,maxX:N,maxY:O,width:B,height:V,offsetX:L,offsetY:C},w._dagreClusterSubgraph=E}});let r=[],l=x=>{let w=[],E=(t.children(x)||[]).filter(b=>b!==x);for(;E.length>0;){let b=E.shift();w.push(b),(t.children(b)||[]).filter(M=>M!==b).forEach(M=>E.push(M))}return w},a=new Map;i.forEach(x=>{let w=t.node(x);w&&w.rankdir&&s[x]&&a.set(x,(t.children(x)||[]).filter(E=>E!==x))});let d=new Set([...a.values()].flat()),c=new Map;a.forEach((x,w)=>{d.has(w)||c.set(w,l(w))});let u=new Set([...c.values()].flat()),h=x=>{for(let[w,E]of c)if(E.includes(x))return w;return x},p=[];t.edges().forEach(x=>{(u.has(x.v)||u.has(x.w))&&p.push({edge:x,label:t.edge(x)})});let m=new Map;u.forEach(x=>{let w=t.parent(x);m.set(x,typeof w=="string"?w:void 0)}),c.forEach((x,w)=>{let E=t.node(w),b=[];x.forEach(L=>{let C=t.node(L);C&&(b.push({id:L,node:C,parent:m.get(L)}),t.removeNode(L))});let M=p.filter(({edge:L})=>x.includes(L.v)||x.includes(L.w)),T=s[w];E&&(r.push({clusterId:w,subgraph:E._dagreClusterSubgraph,bounds:T,children:x,removedNodes:b,removedEdges:M}),E.width=T.width,E.height=T.height)});let v=new Set;p.forEach(({edge:x,label:w})=>{let E=h(x.v),b=h(x.w);if(E!==b&&t.hasNode(E)&&t.hasNode(b)){let M=`${E}\0${b}`;v.has(M)||(v.add(M),t.setEdge(E,b,{...w,width:0,height:0}))}});let y=Ws(t),S=Gs(y,e,n,(o=Ds.get(t))!=null?o:null);Ds.set(t,S),zs(t,y),v.forEach(x=>{let w=x.indexOf("\0"),E=x.slice(0,w),b=x.slice(w+1);t.hasEdge(E,b)&&t.removeEdge(E,b)}),r.forEach(({clusterId:x,subgraph:w,bounds:E,removedNodes:b,removedEdges:M})=>{var T,L;let C=t.node(x),N=(T=C?.x)!=null?T:0,O=(L=C?.y)!=null?L:0,B=(E.minX+E.maxX)/2,V=(E.minY+E.maxY)/2;b.forEach(({id:z,node:P,parent:K})=>{t.setNode(z,P),K!==void 0&&t.setParent(z,K)}),M.forEach(({edge:z,label:P})=>{t.setEdge(z,P)}),w.nodes().forEach(z=>{if(z===x)return;let P=w.node(z),K=t.node(z);K&&P&&typeof P.x=="number"&&typeof P.y=="number"&&(K.x=N+(P.x-B),K.y=O+(P.y-V))}),delete C._dagreClusterSubgraph}),i.forEach(x=>{var w,E;let b=t.node(x),M=s[x];if(b&&b.rankdir&&b._dagreClusterSubgraph&&M){let T=b._dagreClusterSubgraph,L=(w=b.x)!=null?w:0,C=(E=b.y)!=null?E:0,N=(M.minX+M.maxX)/2,O=(M.minY+M.maxY)/2;T.nodes().forEach(B=>{if(B===x)return;let V=T.node(B),z=t.node(B);if(z&&V&&typeof V.x=="number"&&typeof V.y=="number"){let P=V.x-N,K=V.y-O;z.x=L+P,z.y=C+K}}),delete b._dagreClusterSubgraph}})}function Gs(t,e,n,o=null){var i,s;let r=n?.useDynamic!==!1,l=r&&(i=o?.graph)!=null?i:null,a=r&&(s=o?.rawNodes)!=null?s:null;e(" makeSpaceForEdgeLabels",()=>th(t)),e(" removeSelfEdges",()=>ch(t)),e(" acyclic",()=>Pc(t,l)),e(" nestingGraph.run",()=>lu(t)),e(" rank",()=>Jc(er(t))),e(" injectEdgeLabelProxies",()=>nh(t)),e(" removeEmptyRanks",()=>wc(t)),e(" nestingGraph.cleanup",()=>cu(t)),e(" normalizeRanks",()=>xc(t)),e(" assignRankMinMax",()=>oh(t)),e(" removeEdgeLabelProxies",()=>ih(t)),e(" normalize.run",()=>jc(t)),e(" parentDummyChains",()=>ou(t)),e(" addBorderSegments",()=>uu(t)),e(" order",()=>pr(t,n,a)),e(" insertSelfEdges",()=>uh(t)),e(" adjustCoordinateSystem",()=>fu(t)),e(" position",()=>Ku(t,n.corePath)),e(" positionSelfEdges",()=>hh(t));let d=JSON.parse(JSON.stringify(t._nodes));return e(" removeBorderNodes",()=>dh(t)),e(" normalize.undo",()=>zc(t)),e(" fixupEdgeLabelCoords",()=>lh(t)),e(" undoCoordinateSystem",()=>pu(t)),e(" translateGraph",()=>sh(t)),e(" assignNodeIntersects",()=>rh(t)),e(" reversePoints",()=>ah(t)),e(" acyclic.undo",()=>Bc(t)),{graph:t,rawNodes:d}}function zs(t,e){t.nodes().forEach(n=>{let o=t.node(n),i=e.node(n);o&&(o.x=i.x,o.y=i.y,o.order=i.order,o.rank=i.rank,e.children(n).length&&(o.width=i.width,o.height=i.height))}),t.edges().forEach(n=>{let o=t.edge(n),i=e.edge(n);o.points=i.points,Object.hasOwn(i,"x")&&(o.x=i.x,o.y=i.y)}),t.graph().width=e.graph().width,t.graph().height=e.graph().height}var Xu=["nodesep","edgesep","ranksep","marginx","marginy"],Yu={ranksep:50,edgesep:20,nodesep:50,rankdir:"TB",rankalign:"center"},Zu=["acyclicer","ranker","rankdir","align","rankalign"],Qu=["width","height","rank"],Vs={width:0,height:0},$u=["minlen","weight","width","height","labeloffset"],Ju={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},eh=["labelpos"];function Ws(t){let e=new ae({multigraph:!0,compound:!0}),n=Mo(t.graph());return e.setGraph(Object.assign({},Yu,So(n,Xu),en(n,Zu))),t.nodes().forEach(o=>{let i=Mo(t.node(o)),s=So(i,Qu);Object.keys(Vs).forEach(l=>{s[l]===void 0&&(s[l]=Vs[l])}),e.setNode(o,s);let r=t.parent(o);r!==void 0&&e.setParent(o,r)}),t.edges().forEach(o=>{let i=Mo(t.edge(o));e.setEdge(o,Object.assign({},Ju,So(i,$u),en(i,eh)))}),e}function th(t){let e=t.graph();e.ranksep/=2,t.edges().forEach(n=>{var o;let i=t.edge(n);i.minlen*=2,((o=i.labelpos)!=null?o:"r").toLowerCase()!=="c"&&(e.rankdir==="TB"||e.rankdir==="BT"?i.width+=i.labeloffset:i.height+=i.labeloffset)})}function nh(t){t.edges().forEach(e=>{let n=t.edge(e);if(n.width&&n.height){let o=t.node(e.v),i={rank:(t.node(e.w).rank-o.rank)/2+o.rank,e};Ye(t,"edge-proxy",i,"_ep")}})}function oh(t){let e=0;t.nodes().forEach(n=>{let o=t.node(n);o.borderTop&&(o.minRank=t.node(o.borderTop).rank,o.maxRank=t.node(o.borderBottom).rank,e=Math.max(e,o.maxRank))}),t.graph().maxRank=e}function ih(t){t.nodes().forEach(e=>{let n=t.node(e);if(n.dummy==="edge-proxy"){let o=n;t.edge(o.e).labelRank=n.rank,t.removeNode(e)}})}function sh(t){let e=Number.POSITIVE_INFINITY,n=0,o=Number.POSITIVE_INFINITY,i=0,s=t.graph(),r=s.marginx||0,l=s.marginy||0;function a(d){let c=d.x,u=d.y,h=d.width,p=d.height;e=Math.min(e,c-h/2),n=Math.max(n,c+h/2),o=Math.min(o,u-p/2),i=Math.max(i,u+p/2)}t.nodes().forEach(d=>a(t.node(d))),t.edges().forEach(d=>{let c=t.edge(d);Object.hasOwn(c,"x")&&a(c)}),e-=r,o-=l,t.nodes().forEach(d=>{let c=t.node(d);c.x-=e,c.y-=o}),t.edges().forEach(d=>{let c=t.edge(d);c.points.forEach(u=>{u.x-=e,u.y-=o}),Object.hasOwn(c,"x")&&(c.x-=e),Object.hasOwn(c,"y")&&(c.y-=o)}),s.width=n-e+r,s.height=i-o+l}function rh(t){t.edges().forEach(e=>{if(e.v===e.w)return;let n=t.edge(e),o=t.node(e.v),i=t.node(e.w),s,r;n.points?(s=n.points[0],r=n.points[n.points.length-1]):(n.points=[],s=i,r=o),n.points.unshift(As(o,s)),n.points.push(As(i,r))})}function lh(t){t.edges().forEach(e=>{let n=t.edge(e);if(Object.hasOwn(n,"x"))switch((n.labelpos==="l"||n.labelpos==="r")&&(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset;break}})}function ah(t){t.edges().forEach(e=>{let n=t.edge(e);n.reversed&&n.points.reverse()})}function dh(t){t.nodes().forEach(e=>{if(t.children(e).length){let n=t.node(e),o=t.node(n.borderTop),i=t.node(n.borderBottom),s=t.node(n.borderLeft[n.borderLeft.length-1]),r=t.node(n.borderRight[n.borderRight.length-1]);n.width=Math.abs(r.x-s.x),n.height=Math.abs(i.y-o.y),n.x=s.x+n.width/2,n.y=o.y+n.height/2}}),t.nodes().forEach(e=>{t.node(e).dummy==="border"&&t.removeNode(e)})}function ch(t){t.edges().forEach(e=>{if(e.v===e.w){let n=t.node(e.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e,label:t.edge(e)}),t.removeEdge(e)}})}function uh(t){vt(t).forEach(e=>{let n=0;e.forEach((o,i)=>{let s=t.node(o);typeof s.rank!="number"&&(s.rank=0),s.order=i+n,(s.selfEdges||[]).forEach(r=>{Ye(t,"selfedge",{width:r.label.width,height:r.label.height,rank:s.rank,order:i+ ++n,e:r.e,edgeLabel:r.label},"_se"),(!Array.isArray(r.label.points)||r.label.points.length!==7)&&(r.label.points=[{x:0,y:-10},{x:0,y:-10},{x:0,y:0},{x:0,y:10},{x:0,y:10},{x:0,y:0},{x:0,y:0}])}),delete s.selfEdges})})}function hh(t){t.nodes().forEach(e=>{let n=t.node(e),o=i=>typeof i=="number"&&isFinite(i);if(n.dummy==="selfedge"){let i=n,s=t.node(i.e.v),r=o(s?.x)?s.x:0,l=o(s?.y)?s.y:0,a=o(s?.width)?s.width:0,d=o(s?.height)?s.height:0,c=o(n.x)?n.x:r,u=o(n.y)?n.y:l,h=a/2,p=d/2;i.edgeLabel.points=[{x:c+h,y:u-p},{x:c+h,y:u-p},{x:c,y:u},{x:c-h,y:u+p},{x:c-h,y:u+p},{x:c,y:u},{x:c,y:u}],i.edgeLabel.x=c,i.edgeLabel.y=u,t.setEdge(i.e,i.edgeLabel),t.removeNode(e)}else n&&Array.isArray(n.selfEdges)&&n.selfEdges.forEach(i=>{if(!Array.isArray(i.label.points)||i.label.points.length!==7){let s=o(n.x)?n.x:0,r=o(n.y)?n.y:0,l=o(n.width)?n.width:0,a=o(n.height)?n.height:0,d=l/2,c=a/2;i.label.points=[{x:s+d,y:r-c},{x:s+d,y:r-c},{x:s,y:r},{x:s-d,y:r+c},{x:s-d,y:r+c},{x:s,y:r},{x:s,y:r}]}})})}function So(t,e){return tn(en(t,e),Number)}function Mo(t){let e={};return t&&Object.entries(t).forEach(([n,o])=>{typeof n=="string"&&(n=n.toLowerCase()),e[n]=o}),e}function fh(t){let e=vt(t),n=new ae({compound:!0,multigraph:!0}).setGraph({});return t.nodes().forEach(o=>{n.setNode(o,{label:o}),n.setParent(o,"layer"+t.node(o).rank)}),t.edges().forEach(o=>n.setEdge(o.v,o.w,{},o.name)),e.forEach((o,i)=>{let s="layer"+i;n.setNode(s,{rank:"same"}),o.reduce((r,l)=>(n.setEdge(r,l,{style:"invis"}),l))}),n}var ph={graphlib:qs,version:Lc,layout:Uu,debug:fh,util:{time:Sc,notime:or}},Oo=ph;var yh=58,xh=13*.55;function Po(t,e,n=3){let o=e+"|"+n+"|"+t,i=on.get(o);if(i)return i;let s=Eh(t,e,n);return on.size>=wh&&on.clear(),on.set(o,s),s}var wh=8192,on=new Map;function Eh(t,e,n){let o=Math.max(4,Math.floor((e-yh)/xh)),i=String(t||"").trim().split(/\s+/).filter(Boolean),s=[],r="",l=!1;for(let a=0;ao;){let u=r?o-r.length-1:o;if(u>=4&&(r=r?r+" "+d.slice(0,u):d.slice(0,u),d=d.slice(u)),s.push(r),r="",s.length===n)break}if(s.length===n){l=!0;break}let c=r?r+" "+d:d;if(c.length<=o){r=c;continue}if(s.push(r),r=d,s.length===n){l=!0;break}}if(!l&&r)s.push(r);else if(l&&s.length){let a=s[s.length-1];s[s.length-1]=(a.length>=o?a.slice(0,o-1):a)+"…"}return s.length>n&&(s.length=n),s.length?s:[""]}function Fo(t,e){return Po(t.label||t.qualname||t.id,e).length}function _h(t=!1){return t}function Bo(t){let e=t.loc;return!!(e&&(e.file||e.line))}function Sh(t,e=!1){let n=Bo(t)||e?72:60,o=(Fo(t,br(t,e))-1)*18;return n+o+(_h(e)?26:0)}function br(t,e=!1){return e?248:t.kind==="model"||t.kind==="entrypoint"||t.level==="stage"?248:t.kind==="layer"?176:216}function Do(t,e=!1){return{w:br(t,e),h:Sh(t,e)}}function yr(t,e){let n=[];for(let o of e){let i=t.node(o);i&&isFinite(i.x)&&isFinite(i.y)&&n.push({id:o,n:i})}return n}function xr(t){let e=new Map;for(let n of t){let o=Math.round(n.n.x*100)/100,i=e.get(o);i?i.push(n):e.set(o,[n])}return{keys:Array.from(e.keys()).sort((n,o)=>n-o),columns:e}}function wr(t,e){let n=yr(t,e);if(n.length<2)return;let{keys:o,columns:i}=xr(n),s=0;for(let r of o){let l=i.get(r);for(let v of l)v.n.x+=s;if(l.length<2)continue;l.sort((v,y)=>v.n.y-y.n.y||(v.idy.id?1:0));let a=0,d=0;for(let v of l)a+=v.n.height,d=Math.max(d,v.n.width);if(a+=(l.length-1)*Vt,a<=no)continue;let c=Math.ceil(a/no),u=Math.max(1,Math.ceil(l.length/c)),h=Math.min.apply(null,l.map(v=>v.n.y-v.n.height/2)),p=0,m=h;for(let v=0;v0&&v%u===0&&(p++,m=h);let y=l[v];y.n.x=r+s+p*(d+oo),y.n.y=m+y.n.height/2,m+=y.n.height+Vt}s+=p*(d+oo)}}function Er(t,e){let n=yr(t,e);if(n.length<2)return!1;let{keys:o,columns:i}=xr(n);if(o.length<2)return!1;let s=[];for(let h of o){let p=i.get(h),m=1/0,v=-1/0;for(let y of p)m=Math.min(m,y.n.x-y.n.width/2),v=Math.max(v,y.n.x+y.n.width/2);s.push({members:p,left:m,right:v})}let r=s[0].left;if(s[s.length-1].right-r<=io)return!1;let l=[],a=[],d=r;for(let h of s)a.length>0&&h.right-d>io&&(l.push(a),a=[],d=h.left),a.push(h);if(a.length>0&&l.push(a),l.length<2)return!1;let c=1/0;for(let h of n)c=Math.min(c,h.n.y-h.n.height/2);let u=c;for(let h of l){let p=1/0;for(let S of h)for(let x of S.members)p=Math.min(p,x.n.y-x.n.height/2);let m=r-h[0].left,v=u-p,y=u;for(let S of h)for(let x of S.members)x.n.x+=m,x.n.y+=v,y=Math.max(y,x.n.y+x.n.height/2);u=y+Vt}return!0}function Qe(t){return t.kind==="control"&&t.subkind==="back"}function kr(t,e){let n=new Map,o=[],i=new Map,s=new Map;for(let b of t.graph.nodes||[])s.set(b.id,b.stage);let r=(t.graph.edges||[]).filter(b=>!Qe(b)),l=new Set;for(let b of t.graph.edges||[]){if(!Qe(b))continue;let M=t.nodeById.get(b.source);M&&l.add(M.stage);let T=t.nodeById.get(b.target);T&&l.add(T.stage)}let a=new Set;{let b=new Map;t.lanes.forEach((M,T)=>b.set(M.id,T));for(let M of t.graph.edges||[]){if(Qe(M))continue;let T=t.nodeById.get(M.source),L=t.nodeById.get(M.target);if(!T||!L)continue;let C=b.get(T.stage),N=b.get(L.stage);C===void 0||N===void 0||Math.abs(C-N)>1&&a.add(T.stage+">"+L.stage)}}let d=a.size,c=d>0,u=Wt+d*lo+ro,h=c?Math.min(u,is):0,p=Math.max(0,h-Wt-ro),m=c?Math.max(ss,Math.min(lo,Ih(p/Math.max(1,d)))):0,v=c?Math.max(1,Math.min(d,Math.floor(p/m+1e-6))):0,y=32+h,S=32,x=320;t.lanes.forEach((b,M)=>{i.set(b.id,M);let T=t.roots(b.id).filter(V=>!t.isHidden(V,e)),L=Lr(t,e,T,0,b.id,r,n),C=Math.max(L.w,264),N=(l.has(b.id)?ns:0)+os,O=Math.max(L.h,40)+N,B=28+O+28;L.place(y+28,S+28),o.push({id:b.id,label:b.label||b.id,order:b.order,index:M,x:y,y:S,w:C+56,h:B,headerH:28,nodeCount:Ah(t,b.id)}),x=Math.max(x,C+56),S+=B+40});let w=y+x+32,E=Math.max(S-40+32,64);return{lanes:o,laneIndex:i,boxes:n,width:w,height:E,channelX:32+Wt,hasChannel:c,channelPairs:d,channelSlots:v,channelStep:m,channelW:h}}function Ah(t,e){let n=0;for(let o of t.graph.nodes||[])o.stage===e&&n++;return n}function Lr(t,e,n,o,i,s,r){if(n.length===0)return{w:0,h:0,place:()=>{}};let l=new Set(n),a=new Map,d=new Map,c=new Oo.graphlib.Graph;c.setGraph({...es}),c.setDefaultEdgeLabel(()=>({}));for(let w of n){let E=t.nodeById.get(w);if(!E)continue;let b=t.isGroup(w),M=e.has(w);if(b&&!M){let L=t.laneChildren(w),C=Lr(t,e,L,o+1,i,s,r);a.set(w,C);let N=Math.max(200,C.w+32),O=Math.max(56,34+C.h+16);d.set(w,{w:N,h:O})}else b&&M?d.set(w,Do(E,!0)):d.set(w,Do(E,!1));let T=d.get(w);c.setNode(w,{width:T.w,height:T.h})}let u=new Map;for(let w of s){let E=Mr(t,w.source,l),b=Mr(t,w.target,l);if(!E||!b||E===b)continue;let M=E+"\0"+b,L=(u.get(M)||0)+ts(w.kind);u.set(M,L),c.setEdge(E,b,{weight:L,minlen:1})}Oo.layout(c),wr(c,n),Er(c,n);let h=1/0,p=1/0,m=-1/0,v=-1/0,y=new Map;for(let w of n){let E=c.node(w);if(!E)continue;let b=E.width,M=E.height,T=E.x-b/2,L=E.y-M/2;y.set(w,{x:T,y:L,w:b,h:M}),h=Math.min(h,T),p=Math.min(p,L),m=Math.max(m,T+b),v=Math.max(v,L+M)}isFinite(h)||(h=0,p=0,m=0,v=0);let S=Math.max(0,m-h),x=Math.max(0,v-p);return{w:S,h:x,place(w,E){for(let b of n){let M=y.get(b);if(!M)continue;let T=ln(w+(M.x-h)),L=ln(E+(M.y-p)),C=t.isGroup(b),N=e.has(b);r.set(b,{id:b,x:T,y:L,w:M.w,h:M.h,laneId:i,depth:o,isGroup:C,collapsed:C&&N,headerH:C&&!N?34:0});let O=a.get(b);if(O){let B=T+Math.max(16,(M.w-O.w)/2);O.place(ln(B),ln(L+34))}}}}}function Mr(t,e,n){if(n.has(e))return e;let o=t.parentOf.get(e)||null,i=0;for(;o;){if(n.has(o))return o;if(++i>4096)return null;o=t.parentOf.get(o)||null}return null}function ln(t){return Math.round(t*100)/100}function Ih(t){return Math.floor(t*100)/100}function bt(t,e){let n=t.lanes[e],o=t.lanes[e+1];return n&&o?(n.y+n.h+o.y)/2:n?n.y+n.h+12:o?o.y-12:0}function Ir(t,e,n=rs,o=ls){if(e<=1)return 0;let i=Math.min(o,2*n/(e-1));return an((t-(e-1)/2)*i)}function Ch(t,e,n){let o=Math.abs(t.exitY-t.entryY),i=Math.abs(e.exitY-e.entryY);if(Math.abs(o-i)>.5)return i-o;let s=n.get(t.key)??0,r=n.get(e.key)??0;return Math.abs(s-r)>.5?s-r:t.keye.key?1:0}function Cr(t,e){let n=new Map,o=new Map,i=new Map,s=new Map,r=[];for(let c of e){let u=c.sBox.laneId+">"+c.tBox.laneId,h=s.get(u);h?h.push(c):(s.set(u,[c]),r.push(u))}let l=new Map,a=[];for(let c of r){let u=s.get(c);u.sort((b,M)=>zo(b.tBox)-zo(M.tBox)||Ar(b.tBox)-Ar(M.tBox)||Th(b.key,M.key));let h=u[0],p=t.laneIndex.get(h.sBox.laneId)??0,m=t.laneIndex.get(h.tBox.laneId)??0,v=m>p,y=Math.abs(m-p)===1,S=an(y?bt(t,Math.min(p,m)):v?bt(t,p):bt(t,p-1)),x=an(y?S:v?bt(t,m-1):bt(t,m)),w={key:c,sourceLane:h.sBox.laneId,targetLane:h.tBox.laneId,adjacent:y,down:v,members:u.map(b=>b.key),trunkX:t.channelX,entryY:S,exitY:x,slot:0};n.set(c,w);let E=0;for(let b of u)E+=zo(b.tBox);l.set(c,u.length?E/u.length:0),u.forEach((b,M)=>{o.set(b.key,w),i.set(b.key,M)}),y||a.push(w)}let d=Math.max(1,t.channelSlots);return a.sort((c,u)=>Ch(c,u,l)),a.forEach((c,u)=>{c.slot=u,c.trunkX=an(t.channelX+Math.min(u,d-1)*t.channelStep)}),{pairs:n,pairOf:o,indexOf:i}}function Ar(t){return t.x+t.w/2}function zo(t){return t.y+t.h/2}function Th(t,e){return te?1:0}function an(t){return Math.round(t*100)/100}function ve(t,e){if(t.length<2)return"";let n=["M "+t[0].x+" "+t[0].y];for(let i=1;i=o||i===t.length-1){let r=s===0?0:(o-n)/s,l=Vo(t[i-1],t[i],Math.max(0,Math.min(1,r))),a=Math.atan2(t[i].y-t[i-1].y,t[i].x-t[i-1].x)*180/Math.PI;return{point:{x:$e(l.x),y:$e(l.y)},angle:a}}n+=s}return{point:t[t.length-1],angle:0}}function $e(t){return Math.round(t*100)/100}function dn(t,e){return Math.hypot(e.x-t.x,e.y-t.y)}function Vo(t,e,n){return{x:t.x+(e.x-t.x)*n,y:t.y+(e.y-t.y)*n}}function Rh(t,e){let n=new Map,o=[],i=new Map;for(let r of e.boxes.values()){o.push(r);let l=n.get(r.laneId);l?l.push(r):n.set(r.laneId,[r]);let a=new Set([r.id]);for(let d of t.ancestors(r.id))a.add(d);i.set(r.id,a)}for(let r of n.values())r.sort((l,a)=>l.x-a.x||zh(l.id,a.id));let s=new Map;for(let r of e.lanes){let l=r.y+r.headerH;for(let a of n.get(r.id)||[])l=Math.max(l,a.y+a.h);s.set(r.id,ee(Math.min(l+9,r.y+r.h-5)))}return{index:t,frame:e,laneBoxes:n,ancestry:i,band:s,all:o}}function Rr(t,e,n,o){let i=t.ancestry.get(e.id);if(!i||i.has(n)||i.has(o))return!1;let s=t.ancestry.get(n);if(s&&s.has(e.id))return!1;let r=t.ancestry.get(o);return!(r&&r.has(e.id))}function Oh(t,e){return{x0:Math.min(t.x,e.x),x1:Math.max(t.x,e.x),y0:Math.min(t.y,e.y),y1:Math.max(t.y,e.y)}}function Hh(t,e){return t.x1>e.x+1&&t.x0e.y+1&&t.y0=d||Rr(t,v,i,s)&&c.push({a:v.x-so,b:v.x+v.w+so});c.sort((v,y)=>v.a-y.a);let u=l?l.x+4:0,h=l?l.x+l.w-4:u,p=[],m=u;for(let v of c)if(!(v.b<=m)&&(v.a>m&&p.push({a:m,b:Math.min(v.a,h)}),m=Math.max(m,v.b),m>=h))break;return mv.b-v.a>=10&&v.b>u&&v.a=10&&a.push(ee((h+Math.min(u.b,h+40))/2))}else{let h=Math.min(u.b,e.x-4);h-u.a>=10&&a.push(ee((Math.max(u.a,h-40)+h)/2))}a.sort((u,h)=>n?u-h:h-u);let d=F(e),c=n?e.x+e.w:e.x;for(let u of a)if(!be(t,[A(c,d),A(u,d)],s,r))return u;return null}function Je(t,e,n,o,i,s,r){let l=n>=j(e),a=qo(t,e,l,o,i,s,r);return a!==null?a:qo(t,e,!l,o,i,s,r)}function Or(t,e,n){let o=Rh(t,e),i=new Map,s=[];for(let h of t.graph.edges||[]){let p=t.visibleRepresentative(h.source,n),m=t.visibleRepresentative(h.target,n);if(p===m&&!(h.source===h.target&&p===h.source)||!e.boxes.has(p)||!e.boxes.has(m))continue;let v=p+"\0"+m+"\0"+h.kind+"\0"+(h.subkind||""),y=i.get(v);y||(y={edges:[],s:p,t:m},i.set(v,y),s.push(v)),y.edges.push(h)}let r=[];for(let h of s){let p=i.get(h);if(Qe(p.edges[0]))continue;let m=e.boxes.get(p.s),v=e.boxes.get(p.t);m.laneId!==v.laneId&&(t.ancestors(p.s).indexOf(p.t)>=0||t.ancestors(p.t).indexOf(p.s)>=0||r.push({key:h,sBox:m,tBox:v}))}let l=Cr(e,r),a=new Map,d=new Map,c=new Map,u=[];for(let h of s){let p=i.get(h),m=p.edges[0],v=e.boxes.get(p.s),y=e.boxes.get(p.t),S=Qe(m),x=v.laneId!==y.laneId,w=t.ancestors(p.s).indexOf(p.t)>=0,E=t.ancestors(p.t).indexOf(p.s)>=0,b,M;if(p.s===p.t)b=Gh(v,Ko(c,p.s));else if(S){let C=Ko(a,v.laneId);b=jh(o,t,e,v,y,C)}else if(E||w)b=Ph(o,v,y,E);else if(x){let C=l.pairOf.get(h),N=l.indexOf.get(h)||0,O=Dh(o,v,y,C,N);b=O.points,O.joinFrom>=0&&O.joinTo>O.joinFrom&&(M={key:C.key,sourceLane:C.sourceLane,targetLane:C.targetLane,index:N,size:C.members.length,axis:C.adjacent?"x":"y",channelX:C.trunkX,entryY:C.entryY,exitY:C.adjacent?C.entryY:C.exitY,joinFrom:O.joinFrom,joinTo:O.joinTo})}else{let C=Ko(d,"in:"+v.laneId);b=Fh(o,e,v,y,C)}let T=p.edges.length>1?"×"+p.edges.length:m.label||"",L=Wo(b);u.push({id:m.id,ids:p.edges.map(C=>C.id),kind:m.kind,subkind:m.subkind,source:p.s,target:p.t,label:T,points:b,d:ve(b,le),mid:L.point,midAngle:L.angle,crossLane:x,back:S,count:p.edges.length,trunk:M})}return u}function Ko(t,e){let n=(t.get(e)||0)+1;return t.set(e,n),n-1}function j(t){return t.x+t.w/2}function F(t){return t.y+t.h/2}function Ph(t,e,n,o){let i=o?e:n,s=o?n:e,r=ee(i.x+12),l=i.y+Math.min(28,i.h/2),a=o?[A(r,l),A(r,F(s)),A(s.x,F(s))]:[A(s.x,F(s)),A(r,F(s)),A(r,l)];if(!be(t,a,e.id,n.id))return a;let d=qo(t,s,!1,F(s),F(s),e.id,n.id),c=d===null?r:d,u=d===null||d<=s.x?s.x:s.x+s.w,h=o?[A(r,l),A(r,F(s)),A(c,F(s)),A(u,F(s))]:[A(u,F(s)),A(c,F(s)),A(r,F(s)),A(r,l)];return be(t,h,e.id,n.id)?a:un(h)}function Fh(t,e,n,o,i){let s=n.x+n.w,r=Tr(n,i),l=o.x,a=Tr(o,i),d;if(l>=s+16){let u=ee((s+l)/2);d=Math.abs(r-a)<.5?[A(s,r),A(l,a)]:[A(s,r),A(u,r),A(u,a),A(l,a)]}else{let u=e.lanes[e.laneIndex.get(n.laneId)??0],h=Math.max(n.y+n.h,o.y+o.h)+14+i*6,p=u?u.y+u.h-6:h,m=ee(Math.min(h,p));d=[A(j(n),n.y+n.h),A(j(n),m),A(j(o),m),A(j(o),o.y+o.h)]}if(!be(t,d,n.id,o.id))return d;let c=Bh(t,n,o,i);return c&&!be(t,c,n.id,o.id)?c:d}function Bh(t,e,n,o){let i=ee((t.band.get(e.laneId)??Math.max(e.y+e.h,n.y+n.h)+12)+o*5),s=Je(t,e,j(n),F(e),i,e.id,n.id),r=Je(t,n,j(e),i,F(n),e.id,n.id);if(s===null||r===null)return null;let l=s>=j(e)?e.x+e.w:e.x,a=r>=j(n)?n.x+n.w:n.x;return un([A(l,F(e)),A(s,F(e)),A(s,i),A(r,i),A(r,F(n)),A(a,F(n))])}function Dh(t,e,n,o,i){let s=o.down,r=Ir(i,o.members.length),l=s?e.y+e.h:e.y,a=s?n.y:n.y+n.h,d=ee(o.entryY+r),c=d,u=ee(o.exitY+r),h=o.trunkX,p=o.adjacent?[A(j(e),l),A(j(e),d),A(j(n),d),A(j(n),a)]:[A(j(e),l),A(j(e),c),A(h,c),A(h,u),A(j(n),u),A(j(n),a)],m=o.adjacent?[p[1],p[2]]:[p[2],p[3]];if(!be(t,p,e.id,n.id))return cn(p,m);let v=o.adjacent?d:c,y=o.adjacent?d:u,S=Je(t,e,j(n),F(e),v,e.id,n.id),x=Je(t,n,j(e),y,F(n),e.id,n.id);if(S===null||x===null)return cn(p,m);let w=S>=j(e)?e.x+e.w:e.x,E=x>=j(n)?n.x+n.w:n.x,b=o.adjacent?[A(w,F(e)),A(S,F(e)),A(S,d),A(x,d),A(x,F(n)),A(E,F(n))]:[A(w,F(e)),A(S,F(e)),A(S,c),A(h,c),A(h,u),A(x,u),A(x,F(n)),A(E,F(n))],M=o.adjacent?[b[2],b[3]]:[b[3],b[4]];return be(t,b,e.id,n.id)?cn(p,m):cn(un(b),M)}function cn(t,e){return{points:t,joinFrom:t.indexOf(e[0]),joinTo:t.indexOf(e[1])}}function jh(t,e,n,o,i,s){let r=n.lanes[n.laneIndex.get(o.laneId)??0],l=Math.max(o.y+o.h,i.y+i.h),a=new Set(e.ancestors(o.id)),d=new Set(e.ancestors(i.id));for(let x of n.boxes.values()){if(x.laneId!==o.laneId||x.id!==o.id&&x.id!==i.id&&(a.has(x.id)||d.has(x.id)))continue;let w=Math.min(j(o),j(i)),E=Math.max(j(o),j(i));x.x+x.wE||(l=Math.max(l,x.y+x.h))}let c=ee(Math.min(l+12+s*7,r?r.y+r.h-6:l+12)),u=d.has(o.id)||a.has(i.id)?ee(i.x+Math.min(28,i.w/3)):j(i),h=[A(j(o),o.y+o.h),A(j(o),c),A(u,c),A(u,i.y+i.h)];if(!be(t,h,o.id,i.id))return h;let p=Je(t,o,j(i),F(o),c,o.id,i.id),m=Je(t,i,j(o),c,F(i),o.id,i.id);if(p===null||m===null)return h;let v=p>=j(o)?o.x+o.w:o.x,y=m>=j(i)?i.x+i.w:i.x,S=un([A(v,F(o)),A(p,F(o)),A(p,c),A(m,c),A(m,F(i)),A(y,F(i))]);return be(t,S,o.id,i.id)?h:S}function Gh(t,e){let n=14+e*6,o=t.x+t.w,i=t.y+t.h,s=i-Math.min(14+e*6,t.h/2),r=o-Math.min(28+e*6,t.w/2);return[A(o,s),A(o+n,s),A(o+n,i+n),A(r,i+n),A(r,i)]}function Tr(t,e){if(e===0)return ee(F(t));let n=Math.min(t.h-16,40),o=n/4,i=(e+1>>1)*(e%2===1?1:-1);return ee(F(t)+Math.max(-n/2,Math.min(n/2,i*o)))}function un(t){let e=[];for(let n of t){let o=e[e.length-1];o&&Math.abs(o.x-n.x)<.01&&Math.abs(o.y-n.y)<.01||e.push(n)}return e.length>=2?e:t}function zh(t,e){return te?1:0}function A(t,e){return{x:ee(t),y:ee(e)}}function ee(t){return Math.round(t*100)/100}function Uo(t){let e=Array.from(t.boxes.values());return e.length?e.slice().sort((n,o)=>n.y-o.y||n.x-o.x)[0]:null}function Hr(t,e,n,o){let i=Array.from(e.boxes.values()),s=e.boxes.get(n);if(!s||!i.length)return Uo(e);let r=t.parentOf.get(n)||null,l=i.filter(u=>(t.parentOf.get(u.id)||null)===r&&u.laneId===s.laneId);l.length<2&&(l=i.filter(u=>u.laneId===s.laneId));let a=o==="ArrowRight"||o==="ArrowLeft",d=o==="ArrowRight"||o==="ArrowDown",c=u=>{let h=u.slice().sort((m,v)=>a?m.x-v.x||m.y-v.y:m.y-v.y||m.x-v.x),p=h.findIndex(m=>m.id===n);return p<0?null:h[p+(d?1:-1)]||null};return c(l)||c(i)}var Vh=["is-hover","is-selected","is-lit","is-flowing","is-traced"];function Wh(t){let e=t.bundle,n=t.sourceLabel||e.sourceLane,o=t.targetLabel||e.targetLane;return(e.edgeCount===1?"1 connection":e.edgeCount+" connections")+" from "+n+" to "+o+". Hover to separate them."}function Nr(t){let e=t.bundle,n=e.kinds.length===1?e.kinds[0]:"mixed",o=R("g",{class:"mlv-bundle mlv-bundle--"+n,"data-bundle-id":e.id,"data-count":String(e.edgeCount),"data-members":String(e.count),"data-axis":e.axis});o.setAttribute("data-lane-pair",e.sourceLane+">"+e.targetLane),t.stage&&o.setAttribute("data-stage",t.stage),t.phase!==void 0&&se(o,t.phase),t.severity&&(o.setAttribute("data-sev",t.severity),o.classList.add("has-issue"));let i=R("title");i.textContent=Wh(t),o.appendChild(i);for(let d of e.spurs)o.appendChild(R("path",{class:"mlv-bundle__spur",d:d.d}));o.appendChild(R("path",{class:"mlv-bundle__trunk",d:e.d}));let s=String(e.edgeCount),r=15+6*s.length,l=R("g",{class:"mlv-bundle__badge",transform:"translate("+e.badge.x+","+e.badge.y+")"});l.appendChild(R("rect",{class:"mlv-bundle__badge-box",x:-r/2,y:-8,width:r,height:16,rx:8}));let a=R("text",{class:"mlv-bundle__badge-text",x:0,y:0});return a.textContent=s,l.appendChild(a),o.appendChild(l),o}var hn=class{constructor(){this.els=new Map;this.memberOf=new Map}adopt(e,n){this.els=e,this.memberOf=new Map;for(let o of n)for(let i of o.memberIds)this.memberOf.set(i,o.id)}sync(e){if(this.els.size===0)return;let n=new Set;for(let[o,i]of this.memberOf){let s=e.get(o);s&&Kh(s)&&n.add(i)}for(let[o,i]of this.els)i.classList.toggle("is-expanded",n.has(o));for(let[o,i]of this.memberOf){let s=e.get(o);s&&s.classList.toggle("is-bundled",!n.has(i))}}};function Kh(t){let e=t.classList;for(let n of Vh)if(e.contains(n))return!0;return!1}var Xo={entrypoint:"M5 3.2 12.4 8 5 12.8Z",config:"M2.2 4.5h11.6M2.2 8h11.6M2.2 11.5h11.6M5.6 3.1v2.8M10.4 6.6v2.8M7 10.1v2.8",dataset:"M3 4.3c0-1 2.24-1.8 5-1.8s5 .8 5 1.8-2.24 1.8-5 1.8-5-.8-5-1.8ZM3 4.3v3.4c0 1 2.24 1.8 5 1.8s5-.8 5-1.8V4.3M3 7.7v3.4c0 1 2.24 1.8 5 1.8s5-.8 5-1.8V7.7",dataloader:"M2 4.2h2.6l6.8 7.6H14M2 11.8h2.6l6.8-7.6H14M11.6 2.4 14 4.2l-2.4 1.8M11.6 10 14 11.8l-2.4 1.8",split:"M2 8h3.6l3.4-4.2H14M9 12.2 5.6 8H2M11.8 2 14 3.8l-2.2 1.8M11.8 10.4 14 12.2l-2.2 1.8",transform:"M2.4 13.6 10 6M8.4 4.4l3.2 3.2M12 1.8v2.8M10.6 3.2h2.8M4 2v1.8M3.1 2.9h1.8",augment:"M5.2 2.2 6.1 5 8.9 5.9 6.1 6.8 5.2 9.6 4.3 6.8 1.5 5.9 4.3 5ZM11.4 8.2l.7 1.9 1.9.7-1.9.7-.7 1.9-.7-1.9-1.9-.7 1.9-.7Z",model:"M8 2 14 5 8 8 2 5ZM2 8.2 8 11.2 14 8.2M2 11.2 8 14.2 14 11.2",layer:"M2.4 4.6h11.2v2.4H2.4ZM2.4 9h11.2v2.4H2.4Z",loss:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM8 5.4A2.6 2.6 0 1 0 8 10.6 2.6 2.6 0 0 0 8 5.4ZM8 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2Z",optimizer:"M2.6 12.2a6.4 6.4 0 1 1 10.8 0M8 8.4 11.2 5.2M7.2 12.2h1.6",scheduler:"M2 12.4 5.6 8.6l2.4 2.2L13.4 4.6M9.6 4.6h3.8v3.8",scaler:"M2.4 4h11.2v8H2.4ZM5.4 4v2.4M8 4v3.6M10.6 4v2.4",train_loop:"M13.2 8A5.2 5.2 0 1 1 11.5 4.1M13.6 2.6v3.2h-3.2",eval_loop:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM5.2 8.1 7.2 10.2 11 6",metric:"M3 13V8.4M6.4 13V4.4M9.8 13v-3.4M13.2 13V6.4M2 13.8h12",checkpoint:"M2.6 3h7.6l3.2 3.2v7.4H2.6ZM5.6 3v3.4h4.6V3M5.6 13.6v-3.6h4.6v3.6",tracker:"M8 6.8A1.2 1.2 0 1 0 8 9.2 1.2 1.2 0 0 0 8 6.8ZM5.2 5.2a4 4 0 0 0 0 5.6M10.8 5.2a4 4 0 0 1 0 5.6M3.1 3.1a7 7 0 0 0 0 9.8M12.9 3.1a7 7 0 0 1 0 9.8",predict:"M9.2 1.6 4 8.6h3.3l-.7 5.8L12 7.2H8.5Z",function:"M6.6 2.8c-1.7 0-2.1.9-2.1 2.3v1.1c0 1-.5 1.6-1.5 1.6 1 0 1.5.6 1.5 1.6v1.1c0 1.4.4 2.3 2.1 2.3M9.4 2.8c1.7 0 2.1.9 2.1 2.3v1.1c0 1 .5 1.6 1.5 1.6-1 0-1.5.6-1.5 1.6v1.1c0 1.4-.4 2.3-2.1 2.3",class:"M3 2.6h10v10.8H3ZM3 6.2h10M5.4 8.8h5.2M5.4 11h3.2",artifact:"M8 2 14 5v6l-6 3-6-3V5ZM2 5l6 3 6-3M8 8v6",external:"M9 2.8h4.2V7M13.2 2.8 7.4 8.6M11.6 9.4v3.8H2.8V4.4h3.8",unknown:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM6.2 6.3a1.85 1.85 0 1 1 2.7 1.7c-.6.3-.9.8-.9 1.5M8 11.2v.9"},qh={step:"M8 5.6A2.4 2.4 0 1 0 8 10.4 2.4 2.4 0 0 0 8 5.6Z",group:"M2.6 2.6h4.4V7H2.6ZM9 2.6h4.4V7H9ZM2.6 9h4.4v4.4H2.6ZM9 9h4.4v4.4H9Z"},et={operation:"function",data:"dataset",model:"model",state:"layer",objective:"loss",optimizer:"optimizer",evaluation:"eval_loop",metric:"metric",output:"external",config:"config",loop:"train_loop",branch:"split",group:"group",entrypoint:"entrypoint",artifact:"artifact",op:"function",step:"function",function:"function",method:"function",compute:"function",computation:"function",process:"function",construction:"function",initialization:"function",hook:"function",callback:"function",forward:"function",class:"class",component:"class",dataset:"dataset","data source":"dataset",datasource:"dataset",input:"dataset",inputs:"dataset",batch:"dataset",dataloader:"dataloader","data loader":"dataloader",loader:"dataloader",batching:"dataloader",sampler:"dataloader","data pipeline":"dataloader",split:"split","data split":"split",decision:"split",conditional:"split",condition:"split",selection:"split",control:"split","control flow":"split",preprocessing:"transform",preprocess:"transform",postprocessing:"transform",transform:"transform",transformation:"transform",augmentation:"augment",augment:"augment",network:"model",module:"model",architecture:"model",layer:"layer","state update":"layer",update:"layer","state init":"layer",variable:"layer",buffer:"layer",parameters:"layer",weights:"layer",loss:"loss",criterion:"loss",optimiser:"optimizer",optimization:"optimizer",optimisation:"optimizer","optimizer step":"optimizer","optimization step":"optimizer",scheduler:"scheduler",schedule:"scheduler","learning rate":"scheduler",scaler:"scaler","training loop":"train_loop","train loop":"train_loop","optimization loop":"train_loop",epoch:"train_loop","training run":"train_loop",eval:"eval_loop",validation:"eval_loop",test:"eval_loop","evaluation loop":"eval_loop",metrics:"metric",score:"metric",scoring:"metric",log:"tracker",logging:"tracker",logger:"tracker",tracker:"tracker",history:"tracker",checkpoint:"checkpoint",checkpointing:"checkpoint",persistence:"checkpoint",save:"checkpoint",outputs:"external",result:"external",return:"external",display:"external",external:"external",configuration:"config",settings:"config",hyperparameters:"config",arguments:"config",args:"config",environment:"config","entry point":"entrypoint",entry:"entrypoint",launcher:"entrypoint","api entry":"entrypoint",main:"entrypoint",script:"entrypoint",file:"artifact",inference:"predict",predict:"predict",prediction:"predict",sampling:"predict",generation:"predict",phase:"group",stage:"group"};function Uh(t){return String(t||"").trim().toLowerCase().replace(/[\s_-]+/g," ")}function Xh(t){let e=Uh(t||"");if(!e||e==="unknown")return"step";if(et[e])return et[e];let n=e.split(/[^a-z0-9]+/).filter(Boolean),o=n[n.length-1];if(o&&et[o])return et[o];let i=n[0];return i&&et[i]?et[i]:"step"}function tt(t,e){return e?"group":Xh(t.kind)}function fn(t){return Xo[t]||qh[t]||Xo.unknown}function Yo(t){return Object.prototype.hasOwnProperty.call(Xo,t)}function Zo(t,e=16){let n=R("svg",{class:"mlv-icon",viewBox:"0 0 16 16",width:e,height:e,"aria-hidden":"true",focusable:"false"}),o=document.createElementNS(he,"path");return fe(o,{d:fn(t),fill:"none",stroke:"currentColor","stroke-width":"1.3","stroke-linecap":"round","stroke-linejoin":"round","vector-effect":"non-scaling-stroke"}),n.appendChild(o),n}var Pr={search:"M7 2.6a4.4 4.4 0 1 0 0 8.8 4.4 4.4 0 0 0 0-8.8ZM10.2 10.2 13.6 13.6",more:"M3.4 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2ZM8 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2ZM12.6 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2Z",close:"M3.6 3.6 12.4 12.4M12.4 3.6 3.6 12.4",chevron:"M6 3.6 10.4 8 6 12.4",plus:"M8 3.2v9.6M3.2 8h9.6",minus:"M3.2 8h9.6",fit:"M2.6 6V2.6H6M10 2.6h3.4V6M13.4 10v3.4H10M6 13.4H2.6V10",rail:"M2.4 3h11.2v10H2.4ZM9.6 3v10",open:"M9 2.8h4.2V7M13.2 2.8 7.4 8.6M11.6 9.4v3.8H2.8V4.4h3.8",target:"M8 2.6v2.2M8 11.2v2.2M2.6 8h2.2M11.2 8h2.2M8 5.2A2.8 2.8 0 1 0 8 10.8 2.8 2.8 0 0 0 8 5.2Z",warning:"M8 2.2 14.4 13.4H1.6ZM8 6.4v3.4M8 11.6v.1",info:"M8 2.2a5.8 5.8 0 1 0 0 11.6A5.8 5.8 0 0 0 8 2.2ZM8 7.2v4M8 5v.1",notobserved:"M2.6 5V2.6H5M7 2.6h2M11 2.6h2.4V5M13.4 7v2M13.4 11v2.4H11M9 13.4H7M5 13.4H2.6V11M2.6 9V7",flow:"M2.4 8h8.4M8.4 5.2 11.6 8l-3.2 2.8M13.2 6.4v3.2",legend:"M2.4 3.6h2.4v2.4H2.4ZM2.4 10h2.4v2.4H2.4ZM6.8 4.8h6.8M6.8 11.2h6.8",copy:"M5.8 5.8h7.6v7.6H5.8ZM2.6 10.2V2.6h7.6v3.2",minimap:"M2.4 3.6h11.2v8.8H2.4ZM8.4 7.4h4.4v4.4H8.4Z",image:"M2.4 3.4h11.2v9.2H2.4ZM2.4 10.6 5.8 7.4l2.4 2.2 2.2-2 3.2 3M10.3 5.3a1.05 1.05 0 1 0 0 2.1 1.05 1.05 0 0 0 0-2.1Z",keyboard:"M1.8 4.2h12.4v7.6H1.8ZM4.2 6.6h.1M6.7 6.6h.1M9.2 6.6h.1M11.7 6.6h.1M4.8 9.4h6.4"};function D(t,e=14){let n=R("svg",{class:"mlv-uicon",viewBox:"0 0 16 16",width:e,height:e,"aria-hidden":"true",focusable:"false"}),o=document.createElementNS(he,"path");return fe(o,{d:Pr[t]||Pr.close,fill:"none",stroke:"currentColor","stroke-width":"1.4","stroke-linecap":"round","stroke-linejoin":"round"}),n.appendChild(o),n}var Yh=240;function re(t){return t==="inferred"?"inferred":t==="unresolved"?"? unresolved":""}function Zh(t,e){return t==="inferred"?"Inferred, not observed: reasoned from the cited code and stated assumptions; the quotes do not show all of it.":"Unresolved: the evidence does not settle this claim. It does not mean the "+e+" is missing."}function nt(t,e="step"){let n=re(t);if(!n||!t)return null;let o=f("span","mlv-basis-tag",n);return o.setAttribute("data-basis",t),o.setAttribute("aria-hidden","true"),o.title=Zh(t,e),o}function Fr(t){if(!t.stale)return"";let e=t.staleQuotes,n=t.staleElsewhere?"a file in another folder":"a changed or missing file";return e?e.stale+" of "+e.total+(e.total===1?" quote cites ":" quotes cite ")+n:"cites "+n}function Br(t){return"mlv-n-"+t.replace(/[^A-Za-z0-9-]/g,e=>"_"+e.charCodeAt(0).toString(16)+"_")}function Qo(t){return t.stage||"unknown"}function mn(t){return t.sublabel?t.sublabel:t.kind&&t.kind!=="unknown"&&Yo(t.kind)?t.kind.replace(/_/g," "):""}function Qh(t){return t.kind==="unknown"||!Yo(t.kind)?"step":t.kind.replace(/_/g," ")}function yt(t){let e=t.node,n=[];n.push(Qh(e)+" "+e.label),n.push(e.phaseLabel?e.phaseLabel+" phase":Qo(e)+" stage"),e.loc.file&&n.push(Rt(e.loc));let o=He(e.basis);o&&n.push(o.slice(2));let i=J(t.counts),s=Y(t.counts);i>0&&n.push(i+(i===1?" finding":" findings")+", highest severity "+s),t.descendants>0&&n.push(t.descendants+(t.descendants===1?" nested step":" nested steps")),t.stale&&n.push(Fr(t));let r=e.detail?gn(e.detail):"";return n.join(", ")+"."+(r?" "+r:"")}var pn=160,$h=new Set(["etc","vs","cf","approx","incl","esp","resp","fig","figs","eq","eqs","al"]);function Jh(t,e){if(t[e]!==".")return!1;let n=/[A-Za-z.]*$/.exec(t.slice(0,e)),o=(n?n[0]:"").toLowerCase();return o?/^(?:[a-z]\.)+[a-z]$/.test(o)?!0:$h.has(o):!1}function gn(t){let e=t.replace(/\s+/g," ").trim();if(!e)return"";let n=/[.!?](?=\s|$)/g;for(let s=n.exec(e);s&&s.indexpn/2?o.slice(0,i):o).replace(/[\s,;:]+$/,"")+"…"}function $o(t){return t.detail&&t.detail.trim()&&Bo(t)?2:1}function Dr(t,e){let n=t.node,o=e,i=f("div","mlv-node");i.id=Br(n.id),i.setAttribute("role","button"),i.setAttribute("tabindex","-1"),i.setAttribute("data-node-id",n.id),i.setAttribute("data-stage",Qo(n)),i.setAttribute("data-kind",n.kind),i.setAttribute("data-level",n.level),n.basis&&i.setAttribute("data-basis",n.basis),t.phase!==void 0&&se(i,t.phase);let s=Y(t.counts);s&&i.setAttribute("data-sev",s),i.setAttribute("aria-label",yt(t)),i.style.left=t.box.x+"px",i.style.top=t.box.y+"px",i.style.width=t.box.w+"px",i.style.height=t.box.h+"px",s&&i.classList.add("has-issues"),t.stale&&i.classList.add("is-stale"),o&&i.classList.add("is-collapsed-group"),g(i,f("div","mlv-node__rail"));let r=g(i,f("div","mlv-node__main"));g(r,f("div","mlv-node__iconbox")).appendChild(Zo(tt(n,o)));let a=g(r,f("div","mlv-node__text")),d=n.label||n.qualname||n.id;g(a,f("div","mlv-node__title mlv-node__title--wrap",d)).setAttribute("data-lines",String(Fo(n,t.box.w)));let u=o?1:$o(n),h=g(a,f("div","mlv-node__sub",mn(n).slice(0,Yh)));if(u===2?h.setAttribute("data-lines","2"):n.loc.file&&g(a,Oe("mlv-node__loc",n.loc,"div")),o){let m=g(a,f("div","mlv-node__chips"));g(m,f("span","mlv-chip",Pe(t.descendants)))}t.stale&&i.appendChild(Gr(t));let p=nt(n.basis,o?"group":"step");if(p&&i.appendChild(p),o){let m=Ht(t.counts,14);m&&(m.classList.add("mlv-node__cluster"),m.style.position="absolute",m.style.top="-8px",m.style.right="-6px",m.style.padding="1px 6px",m.style.borderRadius="var(--mlv-r-pill)",m.style.background="var(--mlv-surface)",m.style.boxShadow="var(--mlv-sh-1)",i.appendChild(m))}else{let m=Vi(t.counts,18,t.findings||[]);m&&i.appendChild(m)}return i}function Pe(t){return t+(t===1?" step":" steps")}function xt(t){return t+(t===1?" finding touches this phase":" findings touch this phase")}function Jo(t){let e=J(t),n=Y(t);return e+(e===1?" finding touches":" findings touch")+" this phase, highest severity "+(n||"none")+". A finding that cites steps or connections in several phases counts in each of them."}function jr(t){let e=t.node,n=f("div","mlv-group");n.id=Br(e.id),n.setAttribute("data-node-id",e.id),n.setAttribute("data-group","1"),n.setAttribute("data-stage",Qo(e)),t.phase!==void 0&&se(n,t.phase),e.basis&&n.setAttribute("data-basis",e.basis),n.setAttribute("data-depth",String(Math.min(2,t.box.depth)));let o=Y(t.counts);o&&n.setAttribute("data-sev",o),n.style.left=t.box.x+"px",n.style.top=t.box.y+"px",n.style.width=t.box.w+"px",n.style.height=t.box.h+"px";let i=f("div","mlv-group__header");i.setAttribute("role","button"),i.setAttribute("tabindex","-1"),i.setAttribute("aria-expanded","true"),i.setAttribute("aria-label",yt(t)+" Group, expanded. Double-click to collapse.");let s=f("button","mlv-group__chevron-btn");s.type="button",s.tabIndex=-1,s.setAttribute("aria-label","Collapse "+(e.label||e.qualname));let r=D("chevron",12);r.setAttribute("class","mlv-uicon mlv-group__chevron"),s.appendChild(r),i.appendChild(s),i.appendChild(Zo(tt(e,!1),14)),g(i,f("span","mlv-group__name",e.label||e.qualname));let l=nt(e.basis,"group");l&&i.appendChild(l),g(i,f("span","mlv-group__count",Pe(t.descendants)));let a=Ht(t.counts,13);return a&&i.appendChild(a),t.stale&&(n.classList.add("is-stale"),i.appendChild(Gr(t))),n.appendChild(i),n}function Gr(t){let e=f("span","mlv-node__stale");return e.setAttribute("data-stale","1"),e.setAttribute("aria-hidden","true"),e.title=Fr(t)+(t.staleElsewhere?". Its jumps are blocked; the notice above says which folder to add.":" since publishing. Its jumps are blocked."),e.appendChild(D("warning",12)),e}function zr(t,e,n,o){let i=f("div","mlv-lane");i.setAttribute("data-lane-id",t.id),i.setAttribute("data-stage",t.id),o!==void 0&&se(i,o),i.style.left=t.x+"px",i.style.top=t.y+"px",i.style.width=t.w+"px",i.style.height=t.h+"px",i.style.setProperty("--mlv-lane-header-h",t.headerH+"px"),n&&i.classList.add("is-absent");let s=g(i,f("div","mlv-lane__header"));if(o!==void 0){let d=g(s,f("span","mlv-lane__num",String(o+1)));d.title="Phase "+(o+1)}let r=g(s,f("span","mlv-lane__label",t.label));r.title=o!==void 0?"Phase "+(o+1)+": "+t.label:t.label,g(s,f("span","mlv-lane__count",Pe(t.nodeCount))),g(s,f("span","mlv-lane__spacer"));let l=Jo(e),a=Ht(e,13,l);if(a){s.appendChild(a);let d=g(s,f("span","mlv-lane__unit",xt(J(e))));d.setAttribute("aria-hidden","true"),d.title=l}return i}function Wr(t){let e=new Map,n=[];for(let i of t){if(!ef(i))continue;let s=i.trunk.key,r=e.get(s);r?r.push(i):(e.set(s,[i]),n.push(s))}let o=[];for(let i of n){let s=e.get(i);if(s.length<2)continue;s.sort((l,a)=>l.trunk.index-a.trunk.index||sf(l.id,a.id));let r=s[0].trunk.axis==="y"?tf(i,s):nf(i,s);for(let l of r)o.push(l)}return o}function ef(t){let e=t.trunk;return!e||e.joinFrom<1||e.joinTo<=e.joinFrom?!1:e.joinTo+1<=t.points.length-1}function tf(t,e){let n=e[0].trunk,o=n.channelX,i=1/0,s=1/0;for(let d of e)i=Math.min(i,d.points[d.trunk.joinFrom-1].x),s=Math.min(s,d.points[d.trunk.joinTo+1].x);if(Math.abs(n.exitY-n.entryY){let a=r.points[r.trunk.joinFrom].x,d=r.points[r.trunk.joinTo].x;return{i:l,lo:Math.min(a,d),hi:Math.max(a,d)}}),i=[],s=0;for(let r of of(o)){if(r.length<2)continue;let l=1/0,a=-1/0;for(let h of r)l=Math.min(l,h.lo),a=Math.max(a,h.hi);if(!(a-l>=ao))continue;let d=r.slice().sort((h,p)=>h.i-p.i).map(h=>e[h.i]),c=[oe(l,n.entryY),oe(a,n.entryY)],u=[];for(let h of d){let p=h.trunk.joinFrom,m=wt(h.points.slice(0,p+1).concat([oe(h.points[p].x,n.entryY)]));u.push({id:h.id,side:"in",points:m,d:ve(m,le)})}for(let h of d){let p=h.trunk.joinTo,m=wt([oe(h.points[p].x,n.entryY)].concat(h.points.slice(p)));u.push({id:h.id,side:"out",points:m,d:ve(m,le)})}i.push(Kr(t,s++,d,n,c,u,oe((l+a)/2,n.entryY)))}return i}function of(t){let e=t.slice().sort((s,r)=>s.lo-r.lo||s.hi-r.hi||s.i-r.i),n=[],o=[],i=-1/0;for(let s of e)o.length&&s.lo>i&&(n.push(o),o=[]),o.push(s),i=o.length===1?s.hi:Math.max(i,s.hi);return o.length&&n.push(o),n}function Kr(t,e,n,o,i,s,r){let l=[],a=0;for(let d of n)a+=d.ids.length,l.indexOf(d.kind)<0&&l.push(d.kind);return{id:"bundle:"+t+(e?"#"+e:""),sourceLane:o.sourceLane,targetLane:o.targetLane,part:e,memberIds:n.map(d=>d.id),count:n.length,edgeCount:a,axis:o.axis,trunk:i,d:ve(i,le),spurs:s.filter(d=>d.points.length>1),badge:r,kinds:l}}function wt(t){let e=[];for(let n of t){let o=e[e.length-1];o&&Math.abs(o.x-n.x)<.01&&Math.abs(o.y-n.y)<.01||e.push(n)}return e}function sf(t,e){return te?1:0}function oe(t,e){return{x:Vr(t),y:Vr(e)}}function Vr(t){return Math.round(t*100)/100}function vn(t){let{index:e,frame:n,routes:o,keep:i}=t,s=[];for(let h of n.lanes)s.push({lane:h,counts:e.laneCounts(h.id,i),phase:e.phaseIndexOf(h.id)});let r=[],l=Array.from(n.boxes.values()).sort((h,p)=>h.depth-p.depth||lf(h.id,p.id));for(let h of l){let p=e.nodeById.get(h.id);if(!p)continue;let m=h.isGroup&&!h.collapsed,y=m||h.collapsed?e.subtreeIssues(h.id,i):e.issuesOf(h.id,i);r.push({expandedGroup:m,visual:{node:p,box:h,counts:e.countsFor(y),findings:y.map(S=>S.short).filter(Boolean),phase:e.phaseIndexOf(p.stage),descendants:e.descendantCount(h.id),...Ur(p.evidenceLocs,p.loc,t.staleFiles)}})}let a=[];for(let h of o){let p=e.issuesOfEdges(h.ids,i),m=e.nodeById.get(h.source),v=e.nodeById.get(h.target);a.push({route:h,severity:Y(e.countsFor(p)),findings:p.map(y=>y.short).filter(Boolean),phase:m?e.phaseIndexOf(m.stage):void 0,labelVisible:h.back,stage:m?m.stage:void 0,sourceLabel:m?m.label||m.qualname:void 0,targetLabel:v?v.label||v.qualname:void 0,mountSerial:t.mountSerial,placement:t.labels?t.labels.get(h.id):void 0,weight:Ji(h.ids),...af(h.ids,e,t.staleFiles),basis:rf(h.ids,e)})}let d=new Map;for(let h of a)d.set(h.route.id,h.severity);let c=new Map;for(let h of n.lanes)c.set(h.id,h.label);let u=[];for(let h of Wr(o)){let p=null;for(let m of h.memberIds){let v=d.get(m)||null;v&&(!p||Z.indexOf(v)e?1:0}function Ur(t,e,n){if(!n.size)return{stale:!1};let o=t||(e.file?[e]:[]),i=[];for(let r of o){let l=r.file?n.get(r.file):void 0;l&&i.push(l)}if(!i.length)return{stale:!1};let s={stale:!0,staleQuotes:{stale:i.length,total:o.length}};return ie(i)&&(s.staleElsewhere=!0),s}function af(t,e,n){if(!n.size)return{};let o=!1,i=!0;for(let s of t){let r=e.edgeById.get(s),l=r?Ur(r.evidenceLocs,r.loc,n):null;!l||!l.stale||(o=!0,l.staleElsewhere||(i=!1))}return o?i?{stale:!0,staleElsewhere:!0}:{stale:!0}:{}}function Xr(t,e){let n=vn(e),o=n.frame;H(t.lanes),H(t.nodes),H(t.bundleGroup),H(t.edgeGroup),H(t.connectors),t.world.style.width=o.width+"px",t.world.style.height=o.height+"px",t.edgesSvg.setAttribute("width",String(o.width)),t.edgesSvg.setAttribute("height",String(o.height)),t.edgesSvg.setAttribute("viewBox","0 0 "+o.width+" "+o.height);for(let l of n.lanes)t.lanes.appendChild(zr(l.lane,l.counts,!1,l.phase));let i=new Map;for(let l of n.nodes){let a=l.visual,d=l.expandedGroup?jr(a):Dr(a,a.box.collapsed);e.wireNode(d,a.node.id,l.expandedGroup),i.set(a.node.id,d),l.expandedGroup?t.lanes.appendChild(d):t.nodes.appendChild(d)}let s=new Map;for(let l of n.bundles){let a=Nr(l);t.bundleGroup.appendChild(a),s.set(l.bundle.id,a)}let r=new Map;for(let l of n.edges){let a=bs(l);e.wireEdge(a,l.route),t.edgeGroup.appendChild(a),r.set(l.route.id,a)}return{nodeEls:i,edgeEls:r,bundleEls:s,plan:n}}function Yr(t,e,n){let o=[];for(let i of e.boxes.values()){let s=t.nodeById.get(i.id);s&&o.push({x:i.x,y:i.y,w:i.w,h:i.h,stage:s.stage,phase:t.phaseIndexOf(s.stage),severity:Y(t.subtreeCounts(i.id,n))})}return o}var ot=.15,Et=2.5,df=1.2;function el(t,e,n,o,i=24){let s=(n-i*2)/Math.max(1,t),r=(o-i*2)/Math.max(1,e);return{zoom:ue(Math.min(s,r),ot,df)}}var Fe=.62,Zr=[.15,.2,.25,.32,.4,.5,.62,.8,1,1.25,1.6,2,2.5];function cf(t){if(!(t>0))return 1;let e=Zr[0],n=1/0;for(let o of Zr){let i=Math.abs(Math.log(t/o));i=Fe||!l)return{zoom:r,x:(e-i*r)/2,y:Math.max(o,(n-s*r)/2),mode:"whole"};let a=l.x-Math.max(0,t.channelW||0),d=Math.max(1,l.x+l.w-a),c=Math.max(1,l.h),u=Math.min((e-o*2)/d,(n-o*2)/c),h=u>=uf,p=ue(h?Math.min(u,_t):_t,ot,Et),m=i*p<=e?(e-i*p)/2:o-a*p,v=s*p<=n?(n-s*p)/2:o-l.y*p;return{zoom:p,x:m,y:v,mode:h?"phase-fit":"phase-anchor"}}var Qr=80,$r=.1,bn=.45,ff=1,yn=class{constructor(e,n,o){this.vp={x:0,y:0,zoom:1};this.contentW=1;this.contentH=1;this.lod="full";this.zBucket=0;this.fitted=!1;this.fitSize=null;this.frame=null;this.canvas=e,this.world=n,this.onChange=o}setContent(e,n,o=null){this.contentW=Math.max(1,e),this.contentH=Math.max(1,n),this.frame=o}apply(){let{x:e,y:n,zoom:o}=this.vp;this.world.style.transform="translate("+e+"px,"+n+"px) scale("+o+")";let i=o=Qr&&o>=this.fitSize.w*$r||i>=Qr&&i>=this.fitSize.h*$r)return this.applyFit(this.fitSize.padding,this.fitSize.whole),!0}return this.apply(),!1}visibleArea(){return this.size()}acceptResize(){if(!this.fitted||!this.fitSize)return;let{w:e,h:n}=this.size();this.fitSize={...this.fitSize,w:e,h:n}}fitsAt(e,n,o=48){let{w:i,h:s}=this.visibleArea();return Math.min((i-o)/Math.max(1,e.w),(s-o)/Math.max(1,e.h))>=n}frameRect(e,n,o,i=48){let{w:s,h:r}=this.visibleArea(),l=Math.min((s-i)/Math.max(1,e.w),(r-i)/Math.max(1,e.h)),a=this.vp.zoom;if(a>=Fe&&l>=a){this.centerOn(e);return}let d=a>=Fe?Math.max(ff,a):o,c=Math.max(bn,Math.min(d,l));this.centerOn(l>=bn?e:n,c)}centerOn(e,n){this.fitted=!1;let{w:o,h:i}=this.visibleArea();typeof n=="number"&&(this.vp.zoom=ue(n,ot,Et));let s=this.vp.zoom;this.vp.x=o/2-(e.x+e.w/2)*s,this.vp.y=i/2-(e.y+e.h/2)*s,this.apply()}revealRect(e,n=16){let{w:o,h:i}=this.visibleArea(),s=this.vp.zoom,r=(d,c,u)=>{let h=Math.max(0,Math.min(n,(u-c)/2));return c>u?-d:d<0?h-d:d+c>u?u-h-(d+c):0},l=r(e.x*s+this.vp.x,e.w*s,o),a=r(e.y*s+this.vp.y,e.h*s,i);(l||a)&&this.panBy(l,a)}zoomToBox(e,n=80){let{w:o,h:i}=this.visibleArea(),s=ue(Math.min((o-n)/Math.max(e.w,1),(i-n)/Math.max(e.h,1)),ot,Et);this.centerOn(e,s)}isVisible(e,n=this.visibleArea()){let{w:o,h:i}=n,s=e.x*this.vp.zoom+this.vp.x,r=e.y*this.vp.zoom+this.vp.y;return s>=0&&r>=0&&s+e.w*this.vp.zoom<=o&&r+e.h*this.vp.zoom<=i}};function ue(t,e,n){return tn?n:t}function Jr(t,e,n,o){let i=Math.max(1,n),s=Math.max(1,o),r=Math.min(t/i,e/s);return{scale:r,ox:(t-i*r)/2,oy:(e-s*r)/2,w:t,h:e,contentW:i,contentH:s}}function pf(t,e,n){return{x:ue((e-t.ox)/t.scale,0,t.contentW),y:ue((n-t.oy)/t.scale,0,t.contentH)}}function mf(t,e,n){return{x:t.ox+e*t.scale,y:t.oy+n*t.scale}}var xn=class{constructor(e,n){this.collapsedState=!1;this.w=200;this.h=130;this.fit=Jr(200,130,1,1);this.root=f("div","mlv-minimap"),this.root.setAttribute("aria-hidden","true"),this.toggleBtn=ne("mlv-btn mlv-btn--icon mlv-minimap__toggle","Collapse minimap"),this.toggleBtn.appendChild(D("chevron",12)),this.toggleBtn.setAttribute("aria-expanded","true"),this.toggleBtn.tabIndex=-1,_(this.toggleBtn,"click",o=>{o.preventDefault(),o.stopPropagation(),this.setCollapsed(!this.collapsedState),n&&n(this.collapsedState)}),this.root.appendChild(this.toggleBtn),this.svgEl=R("svg",{class:"mlv-minimap__svg",viewBox:"0 0 200 130","aria-hidden":"true"}),this.nodesG=R("g"),this.viewRect=R("rect",{class:"mlv-minimap__view",x:0,y:0,width:0,height:0,rx:2}),this.svgEl.appendChild(this.nodesG),this.svgEl.appendChild(this.viewRect),this.root.appendChild(this.svgEl),this.svgEl.addEventListener("pointerdown",o=>{let i=o,s=this.svgEl.getBoundingClientRect(),r=s.width?(i.clientX-s.left)/s.width*this.w:0,l=s.height?(i.clientY-s.top)/s.height*this.h:0,a=pf(this.fit,r,l);e(a.x,a.y)})}get collapsed(){return this.collapsedState}setCollapsed(e){this.collapsedState=e,e?this.root.classList.add("is-collapsed"):this.root.classList.remove("is-collapsed"),this.toggleBtn.setAttribute("aria-expanded",e?"false":"true");let n=e?"Expand minimap":"Collapse minimap";this.toggleBtn.title=n,this.toggleBtn.setAttribute("aria-label",n)}render(e,n,o){this.fit=Jr(this.w,this.h,n,o);let{scale:i,ox:s,oy:r}=this.fit;for(;this.nodesG.firstChild;)this.nodesG.removeChild(this.nodesG.firstChild);for(let l of e){let a=R("rect",{class:"mlv-minimap__node"+(l.severity?" mlv-minimap__node--issue":""),x:Se(s+l.x*i),y:Se(r+l.y*i),width:Se(Math.max(2,l.w*i)),height:Se(Math.max(2,l.h*i)),rx:1});a.setAttribute("data-stage",l.stage),l.phase!==void 0&&se(a,l.phase),l.severity&&a.setAttribute("data-sev",l.severity),this.nodesG.appendChild(a)}}setViewport(e,n,o){let{scale:i,ox:s,oy:r}=this.fit,l=mf(this.fit,-e.x/e.zoom,-e.y/e.zoom),a=n/e.zoom*i,d=o/e.zoom*i,c=ue(l.x,s,s+this.fit.contentW*i),u=ue(l.y,r,r+this.fit.contentH*i);this.viewRect.setAttribute("x",String(Se(c))),this.viewRect.setAttribute("y",String(Se(u))),this.viewRect.setAttribute("width",String(Se(Math.max(0,Math.min(a,s+this.fit.contentW*i-c))))),this.viewRect.setAttribute("height",String(Se(Math.max(0,Math.min(d,r+this.fit.contentH*i-u)))))}};function Se(t){return Math.round(t*100)/100}function gf(t,e){let n=new Set([e]),o=new Set,i=s=>{let r=[e],l=new Set([e]);for(;r.length;){let a=r.pop();for(let d of t){let c=s?d.source:d.target,u=s?d.target:d.source;c===a&&(o.add(d.id),!l.has(u)&&(l.add(u),n.add(u),r.push(u)))}}};return i(!0),i(!1),{nodes:n,edges:o}}function ei(t,e){let n=new Set([e]),o=new Set;for(let i of t)i.source!==e&&i.target!==e||(o.add(i.id),n.add(i.source),n.add(i.target));return{nodes:n,edges:o}}function vf(t,e,n){return n==="direct"?ei(t,e):gf(t,e)}function tl(t,e,n,o,i){for(let r of t.nodes.values())r.classList.remove("is-lit");for(let r of t.edges.values())r.classList.remove("is-lit");if(!n){t.canvas.classList.remove(o);return}let s=vf(e,n,i);for(let r of s.nodes){let l=t.nodes.get(r);l&&l.classList.add("is-lit")}for(let r of s.edges){let l=t.edges.get(r);l&&l.classList.add("is-lit")}t.canvas.classList.add(o)}var G={MAX_EDGES:120,PULSE_SPEED:320,PULSE_MIN_MS:380,PULSE_MAX_MS:2200,STREAM_SPEED:220,HEAD_PX:12,GAP_DATA:34,GAP_CALL:96,GAP_BACK:24,PERIOD_DATA:210,PERIOD_CALL:490,PERIOD_BACK:165,HOP_MS:90,HOP_MAX:6,PORT_R:3.5,CHARGE_HALO_R:9,CHARGE_GLOW_R:5.5,CHARGE_CORE_R:2.6,CHARGE_TWIN_PX:360,SETTLE_PASSES:2,SETTLE_MIN_MS:1600,SETTLE_MAX_MS:6e3},bf=["--mlv-flow-dur","--mlv-flow-len","--mlv-flow-head","--mlv-flow-gap","--mlv-flow-end","--mlv-flow-delay","--mlv-flow-iter"],yf=["is-flowing","is-flowing--pulse","is-flow-settled"];function nl(t){let e=0;for(let n=1;n0?Math.max(1,Math.round((t-e)/n)):1}function ol(t){return Math.min(t,G.HOP_MAX)*G.HOP_MS}function Mf(t){return t.kind!=="config"}function kf(t,e){let n=new Map([[e,0]]),o=new Map,i=s=>{let r=[e],l=new Set([e]),a=0;for(;r.length;){a++;let d=[];for(let c of r)for(let u of t){let h=s?u.source:u.target,p=s?u.target:u.source;if(h!==c)continue;let m=o.get(u.id);if((m===void 0||athis.pulse(e);let n=this.modeFor(1);if(this.host.canvas.setAttribute("data-flow",n),n==="off")return;let o=this.host.edges().get(e.id);if(!o)return;this.decorate(o,e,n,null,0),o.classList.add("is-flowing--pulse"),this.decorated.push(e.id),this.decoratedRoutes.set(e.id,e);let i=this.host.nodes(),s=i.get(e.source),r=i.get(e.target);s&&(s.classList.add("is-flow-source"),this.sourceId=e.source),r&&(r.classList.add("is-flow-target"),this.targetId=e.target)}stream(e,n){this.clear(),this.lastRun=()=>{this.stream(e,n)};let o=this.host.routes(),i=Lf(o,e,n),s=i.edges.size,r=this.modeFor(s);this.host.canvas.setAttribute("data-flow",r);let l=s>G.MAX_EDGES,a=l&&r!=="off";if(r==="off"||l)return{mode:r,capped:a,edges:s};let d=this.host.edges(),c=[],u=0,h=0;for(let m of o){let v=i.edges.get(m.id);if(v===void 0||!Mf(m))continue;let y=d.get(m.id);y&&(c.push({route:m,g:y,hop:v}),u=Math.max(u,nl(m.points||[])),h=Math.max(h,ol(v)))}let p=_f(u,h);for(let{route:m,g:v,hop:y}of c)this.decorate(v,m,r,y,p),v.classList.add("is-flowing"),this.decorated.push(m.id),this.decoratedRoutes.set(m.id,m);return{mode:r,capped:a,edges:s}}settle(e){let n=this.decoratedRoutes.get(e),o=this.host.edges().get(e);if(!n||!o||o.classList.contains("is-flow-settled"))return!1;let i=o.querySelectorAll(".mlv-edge__flow, .mlv-edge__charge");for(let s=0;s=2&&!o.querySelector(".mlv-edge__dir")&&o.appendChild(ll(n)),o.classList.add("is-flow-settled"),this.settled&&this.host.canvas.setAttribute("data-flow-settled","true"),!0}get settled(){if(!this.decorated.length)return!1;let e=this.host.edges();return this.decorated.every(n=>{let o=e.get(n);return!o||o.classList.contains("is-flow-settled")})}get flowing(){return this.decorated.length>0}replay(){return!this.lastRun||!this.decorated.length||this.modeFor(1)!=="motion"?!1:(this.lastRun(),this.decorated.length>0)}modeFor(e){return this.enabledFlag?this.motionFlag==="reduced"||e>G.MAX_EDGES?"static":"motion":"off"}decorate(e,n,o,i,s){let r=n.points||[];if(r.length<2)return;let l=nl(r),a=e.style;if(e.appendChild(rl("out",r[0])),e.appendChild(rl("in",r[r.length-1])),o==="static"){e.appendChild(ll(n));return}let d=R("path",{class:"mlv-edge__flow",d:n.d,pathLength:100});e.appendChild(d);let c=sl(G.HEAD_PX,l);if(a.setProperty("--mlv-flow-len",Me(l)+"px"),a.setProperty("--mlv-flow-head",String(c)),i===null){let u=xf(l);a.setProperty("--mlv-flow-dur",u+"ms"),a.setProperty("--mlv-flow-iter",String(G.SETTLE_PASSES));let h=l>0?Af(e):"";if(h){let p=If(e),m=il(h,u,0,p);e.appendChild(m),this.settleOnEnd(m,n.id),l>G.CHARGE_TWIN_PX&&e.appendChild(il(h,u,-Math.round(u/2),p))}}else{let u=wf(n.kind,n.subkind,n.back),h=sl(u,l);a.setProperty("--mlv-flow-gap",String(h)),a.setProperty("--mlv-flow-end",String(Me(-(c+h))));let p=ol(i);a.setProperty("--mlv-flow-delay",p+"ms"),a.setProperty("--mlv-flow-iter",String(Sf(s,p,Ef(n.kind,n.back)))),this.settleOnEnd(d,n.id)}}settleOnEnd(e,n){e.addEventListener("animationend",o=>{o.target===e&&this.settle(n)})}};function Af(t){let e=t.querySelector(".mlv-edge__path");return e&&e.getAttribute("id")||""}function If(t){try{let e=t,n=e.ownerSVGElement||(typeof e.closest=="function"?e.closest("svg"):null);if(!n||typeof n.getCurrentTime!="function")return null;let o=n.getCurrentTime();return typeof o=="number"&&isFinite(o)?o:null}catch{return null}}function il(t,e,n,o){let i=R("g",{class:n===0?"mlv-edge__charge":"mlv-edge__charge mlv-edge__charge--twin"});i.appendChild(R("circle",{class:"mlv-edge__charge-halo",cx:0,cy:0,r:G.CHARGE_HALO_R})),i.appendChild(R("circle",{class:"mlv-edge__charge-glow",cx:0,cy:0,r:G.CHARGE_GLOW_R})),i.appendChild(R("circle",{class:"mlv-edge__charge-core",cx:0,cy:0,r:G.CHARGE_CORE_R}));let s=R("animateMotion",{dur:e+"ms",repeatCount:String(G.SETTLE_PASSES),calcMode:"linear",rotate:"auto",fill:"freeze"});o!==null?s.setAttribute("begin",Cf(o+n/1e3)+"s"):n!==0&&s.setAttribute("begin",n+"ms");let r=R("mpath");return r.setAttribute("href","#"+t),r.setAttributeNS(Bi,"xlink:href","#"+t),s.appendChild(r),i.appendChild(s),i}function sl(t,e){return e>0?Me(Math.min(100,t/e*100)):100}function Me(t){return Math.round(t*100)/100}function Cf(t){return Math.round(t*1e3)/1e3}function rl(t,e){return R("circle",{class:"mlv-edge__port mlv-edge__port--"+t,cx:Me(e.x),cy:Me(e.y),r:G.PORT_R})}function ll(t){return R("path",{class:"mlv-edge__dir",d:"M -3.4 -3.6 L 0 0 L -3.4 3.6",fill:"none",transform:"translate("+Me(t.mid.x)+","+Me(t.mid.y)+") rotate("+Me(t.midAngle)+")"})}function al(t){for(let o of yf)t.classList.remove(o);let e=t.style;if(e&&typeof e.removeProperty=="function")for(let o of bf)e.removeProperty(o);let n=t.querySelectorAll(".mlv-edge__flow, .mlv-edge__charge, .mlv-edge__port, .mlv-edge__dir");for(let o=0;o"u"||!window.matchMedia)return!1;let e=window.matchMedia(t);return!!(e&&e.matches)}catch{return!1}}var En=class{constructor(e){this.stop=null;this.current=dl();let n=()=>{let i=dl();i!==this.current&&(this.current=i,e(i))},o=[];try{let i=typeof window<"u"&&window.matchMedia?window.matchMedia(cl):null;i&&typeof i.addEventListener=="function"&&(i.addEventListener("change",n),o.push(()=>i.removeEventListener("change",n)))}catch{}try{let i=typeof document<"u"?document.body:null;if(i&&typeof MutationObserver=="function"){let s=new MutationObserver(n);s.observe(i,{attributes:!0,attributeFilter:["class"]}),o.push(()=>s.disconnect())}}catch{}o.length&&(this.stop=()=>{for(let i of o)try{i()}catch{}})}get mode(){return this.current}destroy(){this.stop&&this.stop(),this.stop=null}};function Hf(t,e="full",n="lineage"){if(n==="direct"){let o="This card has "+t+" connections, ";return e==="reduced"?o+"more than the "+G.MAX_EDGES+" that can be marked at once. Hover a single connection to see its direction.":o+"past the "+G.MAX_EDGES+" the animation can carry. Hover a single connection to see its flow."}return e==="reduced"?"This lineage has "+t+" connections, more than the "+G.MAX_EDGES+" that can be marked at once. Hover a single connection, or walk them with e, to see each direction.":"This lineage has "+t+" connections, past the "+G.MAX_EDGES+" the animation can carry. Hover a single connection, or walk them with e, to see each flow."}var _n=class{constructor(e){this.latchedEdgeId=null;this.cappedKey=null;this.host=e,this.controller=new wn({canvas:e.canvas,edges:()=>e.edges(),nodes:()=>e.nodes(),routes:()=>e.routes()}),this.motionWatch=new En(n=>{this.controller.setMotion(n),this.controller.syncCanvas(),this.cappedKey=null,this.stop()}),this.controller.setMotion(this.motionWatch.mode),this.controller.syncCanvas()}get enabled(){return this.controller.enabled}get motion(){return this.controller.motion}setEnabled(e){this.controller.setEnabled(e),e&&this.stop()}syncCanvas(){this.controller.syncCanvas()}clear(){this.cappedKey=null,this.controller.clear()}pulse(e){this.controller.pulse(e)}replay(){return this.controller.replay()}stream(e,n){let o=this.controller.stream(e,n);if(!o.capped)return;let i=n+":"+e;this.cappedKey!==i&&(this.cappedKey=i,this.host.toast(Hf(o.edges,this.controller.motion,n)))}stop(){if(this.latchedEdgeId){let i=this.routeById(this.latchedEdgeId);if(i){this.controller.pulse(i);return}}let e=this.host.lockedNodeId();if(e){this.stream(e,"lineage");return}let n=this.host.hoverNodeId();if(n){this.stream(n,"direct");return}let o=this.host.hoverEdgeId();if(o){let i=this.routeById(o);if(i){this.controller.pulse(i);return}}this.controller.clear()}setLatchedEdge(e){this.latchedEdgeId=e}routeById(e){for(let n of this.host.routes())if(n.id===e)return n;return null}incidentRoutes(e){return this.host.routes().filter(n=>n.source===e||n.target===e)}focusEdge(e){let n=this.host.edges().get(e);if(!n)return!1;let o=n.querySelector(".mlv-edge__hit");if(!o||typeof o.focus!="function")return!1;try{o.focus()}catch{return!1}return!0}blurFocusedEdge(){let e=this.host.canvas.ownerDocument,n=e?e.activeElement:null;return!n||!n.classList||!n.classList.contains("mlv-edge__hit")||typeof n.blur!="function"?!1:(n.blur(),!0)}edgeAnnouncement(e){let n=this.host.index(),o=n?n.nodeById.get(e.source):null,i=n?n.nodeById.get(e.target):null,s=o?o.label||o.qualname:e.source,r=i?i.label||i.qualname:e.target;return"Connection: "+s+" to "+r+(e.label?", "+e.label:"")+"."}destroy(){this.motionWatch.destroy()}};function Nf(t,e,n){let o=n.x-e.x,i=n.y-e.y,s=t.x-e.x,r=t.y-e.y,l=o*o+i*i,a=l>0?(s*o+r*i)/l:0;a<0?a=0:a>1&&(a=1);let d=s-a*o,c=r-a*i;return d*d+c*c}function Pf(t,e){let n=t.points||[];if(n.length===0)return 1/0;if(n.length===1){let i=e.x-n[0].x,s=e.y-n[0].y;return Math.sqrt(i*i+s*s)}let o=1/0;for(let i=1;ithis.resolve(e))}enter(e){this.set(e)}leave(e){this.currentId===e.id&&this.set(null)}reset(){this.currentId=null,this.timer!==null&&(clearTimeout(this.timer),this.timer=null),this.host.hide()}destroy(){this.reset()}resolve(e){let n=this.host.routes();if(!n.length||this.host.canvas.classList.contains("is-panning"))return;let o=e.target;if(o&&typeof o.closest=="function"&&o.closest(Bf)){this.currentId&&this.set(null);return}let i=this.host.canvas.getBoundingClientRect(),s=this.host.viewport.vp,r=s.zoom>0?s.zoom:1,l={x:(e.clientX-i.left-s.x)/r,y:(e.clientY-i.top-s.y)/r},a=this.host.markers?this.host.markers():null,d=a&&a.size?pl(n,a,l,qe):null;this.set(d||fl(n,l,Ff/r))}set(e){let n=e?e.id:null;if(this.currentId===n)return;let o=this.host.edges();if(this.currentId){let i=o.get(this.currentId);i&&i.classList.remove("is-hover")}if(this.currentId=n,e){let i=o.get(e.id);if(i){i.classList.add("is-hover");let s=i.parentNode;s&&s.lastChild!==i&&s.appendChild(i)}}this.host.changed&&this.host.changed(e),this.intent(e)}intent(e){this.timer!==null&&(clearTimeout(this.timer),this.timer=null);let n=()=>{e?this.host.open(e):this.host.close()},o=e?this.host.openDelayMs:this.host.closeDelayMs;if(o<=0){n();return}this.timer=setTimeout(()=>{this.timer=null,n()},o)}};var Mn=12,Be=8;function Df(t,e,n,o,i){let s=o.w>0&&o.h>0,r=!1;if(s){let d=e-Mn-o.h>=Be,c=i.h<=0||n+Mn+o.h<=i.h-Be;r=!d&&c}let l=r?n+Mn:e-Mn;s&&(r?l=Math.min(l,Math.max(Be,i.h-Be-o.h)):l=Math.max(l,Be+o.h));let a=t;if(s&&i.w>0){let d=o.w/2,c=d+Be,u=i.w-d-Be;a=u>=c?Math.min(Math.max(t,c),u):i.w/2}return{left:Math.round(a),top:Math.round(l),below:r}}var kn=class{constructor(e,n){this.root=f("div","mlv-tooltip"),this.root.setAttribute("role","tooltip"),this.root.hidden=!0,this.viewport=e,this.bounds=n||(()=>({w:0,h:0}))}showNode(e,n,o,i){let s=e.nodeById.get(n);if(!s)return;H(this.root),g(this.root,f("div","mlv-tooltip__title",s.label||s.qualname));let r=re(s.basis);r&&g(this.root,f("div","mlv-tooltip__row mlv-tooltip__basis",r.replace("? ","")+", not observed")),s.sublabel&&g(this.root,f("div","mlv-tooltip__row",s.sublabel)),s.loc.file&&g(this.root,Oe("mlv-tooltip__loc",s.loc,"div"));let l=o.isGroup||o.collapsed;this.issueRows(l?e.subtreeIssues(n,i):e.issuesOf(n,i)),this.placeAt(o.x+o.w/2,o.y,o.y+o.h)}showEdge(e,n,o,i){let s=e.edgeById.get(n.id);H(this.root);let r=n.count<=1&&s&&s.label?s.label:"";g(this.root,f("div","mlv-tooltip__title",r||n.label||ft(n.kind)));let l=s&&s.authoredKind?" · authored as "+s.authoredKind:"";g(this.root,f("div","mlv-tooltip__row",ft(n.kind)+(n.subkind?" · "+n.subkind:"")+l));let a=s?re(s.basis):"";a&&g(this.root,f("div","mlv-tooltip__row mlv-tooltip__basis",a.replace("? ","")+", not observed")),s&&s.loc.file&&g(this.root,Oe("mlv-tooltip__loc",s.loc,"div")),n.count>1&&g(this.root,f("div","mlv-tooltip__row",n.count+" merged connections")),this.issueRows(e.issuesOfEdges(n.ids,o)),i?this.placeAt(i.x,i.y-qe,i.y+qe):this.placeAt(n.mid.x,n.mid.y,n.mid.y)}issueRows(e){for(let n of e){let o=g(this.root,f("div","mlv-tooltip__row"));o.appendChild(U(n.severity,12,"")),g(o,f("span",""," "+(n.short?n.short+" · ":"")+n.code+" "+n.title))}}placeAt(e,n,o){let i=this.viewport();this.root.hidden=!1,this.root.style.transform="translate(-50%, -100%)";let s=this.root.getBoundingClientRect(),r=Df(e*i.zoom+i.x,n*i.zoom+i.y,o*i.zoom+i.y,{w:s.width,h:s.height},this.bounds());this.root.style.left=r.left+"px",this.root.style.top=r.top+"px",this.root.style.transform=r.below?"translate(-50%, 0)":"translate(-50%, -100%)"}hide(){this.root.hidden=!0}};function ml(t){let e=f("div","mlv-state mlv-state--empty");e.setAttribute("role","status");let n=g(e,f("div","mlv-state__inner"));g(n,f("h2","mlv-state__title","No workflow steps")),g(n,f("p","mlv-state__body","This authored workflow does not contain any steps."));let o=t?t.diagnostics||[]:[];if(o.length){let i=g(n,f("ul","mlv-state__list"));for(let s of o.slice(0,8))g(i,f("li","",s.kind+" — "+s.message))}else t&&g(n,f("p","mlv-state__body","The author inspected "+t.workspace.filesAnalyzed+" files."));return e}function gl(t){let e=f("div","mlv-state mlv-state--empty");e.setAttribute("role","status");let n=g(e,f("div","mlv-state__inner"));g(n,f("h2","mlv-state__title","No steps match your filters"));let o=g(n,f("div","mlv-state__actions")),i=W("mlv-btn","Clear all filters");return _(i,"click",t),o.appendChild(i),e}var Ln=class{constructor(){this.timers=[];this.root=f("div","mlv-toasts"),this.root.setAttribute("role","status")}show(e){let n=f("div","mlv-toast",e);for(this.root.appendChild(n);this.root.childElementCount>3&&this.root.firstElementChild;)this.root.removeChild(this.root.firstElementChild);let o=setTimeout(()=>{n.parentNode===this.root&&this.root.removeChild(n)},4e3);this.timers.push(o)}destroy(){for(let e of this.timers)clearTimeout(e);this.timers=[],H(this.root)}};var jf=0,Gf=1,zf=2,Vf=100/3,ni=800,Wf=.999,xl=40,Kf=6;function qf(t,e=ni){let n=typeof t.deltaMode=="number"?t.deltaMode:jf,o=1;return n===Gf?o=Vf:n===zf&&(o=e>0?e:ni),{dx:vl(t.deltaX)*o,dy:vl(t.deltaY)*o,mode:n}}function vl(t){return typeof t=="number"&&isFinite(t)?t:0}function Uf(t,e){return t.ctrlKey||t.metaKey?"zoom":t.shiftKey?"pan":e.dx===0&&Math.abs(e.dy)>=xl?"zoom":"pan"}function Xf(t,e){let n=(t.ctrlKey||t.metaKey)&&Math.abs(e.dy){n.preventDefault();let o=t.getBoundingClientRect(),i=qf(n,o.height||t.clientHeight||ni);if(Uf(n,i)==="zoom"){e.zoomAt(Xf(n,i),n.clientX-o.left,n.clientY-o.top);return}let s=Yf(n,i);e.panBy(s.dx,s.dy)})}function bl(t,e){let n=t.x-e.x,o=t.y-e.y;return Math.sqrt(n*n+o*o)}function yl(t,e){return{x:(t.x+e.x)/2,y:(t.y+e.y)/2}}var An=class{constructor(e,n){this.points=new Map;this.lastDistance=0;this.lastMid=null;this.canvas=e,this.viewport=n}active(){return this.points.size>=2}down(e){this.points.set(e.pointerId,{x:e.clientX,y:e.clientY}),this.reseed()}up(e){this.points.delete(e.pointerId),this.reseed()}clear(){this.points.clear(),this.lastDistance=0,this.lastMid=null}move(e){if(!this.points.has(e.pointerId)||(this.points.set(e.pointerId,{x:e.clientX,y:e.clientY}),this.points.size<2))return!1;let n=this.pair();if(!n)return!1;let o=bl(n[0],n[1]),i=yl(n[0],n[1]),s=this.canvas.getBoundingClientRect();return this.lastDistance>0&&o>0&&(this.lastMid&&this.viewport.panBy(i.x-this.lastMid.x,i.y-this.lastMid.y),this.viewport.zoomAt(o/this.lastDistance,i.x-s.left,i.y-s.top)),this.lastDistance=o,this.lastMid=i,!0}pair(){let e=Array.from(this.points.keys()).sort((n,o)=>n-o);return e.length<2?null:[this.points.get(e[0]),this.points.get(e[1])]}reseed(){let e=this.pair();if(!e){this.lastDistance=0,this.lastMid=null;return}this.lastDistance=bl(e[0],e[1]),this.lastMid=yl(e[0],e[1])}};function El(t){let e=t.ownerDocument;return!e||!e.body||t.parentElement!==e.body?()=>{}:(e.documentElement.classList.add("mlv-fills-page"),e.body.classList.add("mlv-fills-page"),()=>{e.documentElement.classList.remove("mlv-fills-page"),e.body.classList.remove("mlv-fills-page")})}var Zf=0;function _l(t,e){t.classList.add("mlv-root"),t.setAttribute("data-theme",e),H(t);let n="mlv-shell"+ ++Zf,o=f("div","mlv-body"),i=f("main","mlv-main");i.setAttribute("aria-labelledby",n+"-main-heading");let s=g(i,f("h2","mlv-sr","Pipeline diagram"));s.id=n+"-main-heading";let r=f("div","mlv-canvas");r.id=n+"-canvas",r.setAttribute("role","application"),r.setAttribute("aria-label","ML pipeline diagram"),r.setAttribute("data-lod","full"),r.tabIndex=0,i.appendChild(r);let l=f("a","mlv-skiplink","Skip to diagram");l.href="#"+r.id,_(l,"click",w=>{w.preventDefault();try{r.focus()}catch{}}),t.appendChild(l);let a=g(r,f("div","mlv-world")),d=g(a,f("div","mlv-layer mlv-layer--lanes")),c=R("svg",{class:"mlv-layer mlv-layer--edges",width:1,height:1});c.appendChild(gs());let u=R("g",{class:"mlv-bundles"}),h=R("g",{class:"mlv-edges"}),p=R("g",{class:"mlv-connectors"});c.appendChild(u),c.appendChild(h),c.appendChild(p),a.appendChild(c);let m=g(a,f("div","mlv-layer mlv-layer--nodes")),v=f("div","mlv-zoom"),y=g(v,f("span","mlv-zoom__level","100%"));y.setAttribute("aria-label","Zoom level");let S=g(r,f("div","mlv-statehost")),x=f("div","mlv-sr");return x.setAttribute("aria-live","polite"),x.setAttribute("role","status"),{body:o,main:i,canvas:r,world:a,lanesLayer:d,edgesSvg:c,bundleGroup:u,edgeGroup:h,connectorLayer:p,nodesLayer:m,zoomBar:v,zoomLevel:y,stateHost:S,live:x}}function Qf(t){let e=t;return!!e.classList&&e.classList.contains("mlv-root")}function Sl(t,e,n){let o=[],i=new An(t,e),s=!1,r=0,l=0;o.push(_(t,"pointerdown",c=>{if(i.down(c),i.active()){s=!1,t.classList.remove("is-panning");return}let u=c.target;if(!(u.closest&&u.closest(".mlv-node, .mlv-group__header, .mlv-minimap, .mlv-zoom, .mlv-edge__hit, .mlv-legend, .mlv-statehost, .mlv-tooltip, .mlv-toasts"))){s=!0,r=c.clientX,l=c.clientY,t.classList.add("is-panning");try{t.setPointerCapture(c.pointerId)}catch{}}})),o.push(_(t,"pointermove",c=>{i.move(c)||s&&(e.panBy(c.clientX-r,c.clientY-l),r=c.clientX,l=c.clientY)}));let a=c=>{c&&i.up(c),s=!1,t.classList.remove("is-panning")};o.push(_(t,"pointerup",a)),o.push(_(t,"pointercancel",a)),o.push(_(t,"pointerleave",a)),o.push(wl(t,e)),o.push(_(t,"keydown",c=>n.onKeyDown(c)));let d=t.ownerDocument;return d&&o.push(_(d,"keydown",c=>{let u=c.target;u&&(u!==d.body&&u!==d.documentElement&&!Qf(u)||n.onKeyDown(c))})),o.push(_(t,"click",c=>{let u=c.target;(!u.closest||!u.closest(".mlv-node, .mlv-edge__hit, .mlv-group"))&&n.onBackgroundClick()})),typeof window<"u"&&o.push(_(window,"resize",()=>n.onResize?n.onResize():e.apply())),o.push(()=>i.clear()),o}function Ml(t,e,n,o,i){H(t);let s=i.nodeIds[0];if(!s)return 0;let r=n.boxes.get(e.visibleRepresentative(s,o));if(!r)return 0;let l=$(i.severity),a=0,d=new Set([r.id]);for(let c of i.nodeIds.slice(1)){let u=e.nodeById.get(c);if(!u)continue;let h=n.boxes.get(e.visibleRepresentative(c,o));!h||d.has(h.id)||(d.add(h.id),a++,t.appendChild(ys({x:r.x+r.w/2,y:r.y},{x:h.x+h.w/2,y:h.y},a,l,"Also affects — "+(u.label||u.id))))}return a}var In=class{constructor(e){this.hoverId=null;this.hoverTimer=null;this.pendingHover=null;this.focusLocked=!1;this.focusNodeId=null;this.focusShown=null;this.ctx=e}get hoverNodeId(){return this.hoverId}get lockedNodeId(){return this.focusLocked?this.focusShown??this.focusNodeId:null}get isFocusLocked(){return this.focusLocked}resetHover(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null),this.pendingHover=null,this.hoverId=null,this.trace(null,"is-tracing","direct"),this.ctx.tooltip.hide(),this.retraceFocus()}retraceFocus(){if(!this.focusLocked||!this.focusNodeId)return;let e=this.ctx,n=e.index(),o=n&&n.nodeById.has(this.focusNodeId)?n.visibleRepresentative(this.focusNodeId,e.collapsed()):null;if(o&&e.nodeEls().has(o)){this.focusShown=o,e.canvas.classList.remove("is-tracing"),this.trace(o,"is-focusing","lineage");return}this.focusLocked=!1,this.focusNodeId=null,this.focusShown=null,e.canvas.classList.remove("is-focusing"),this.trace(null,"is-focusing","lineage"),e.announce("Focus mode off.")}applySelection(e){let n=this.ctx;for(let o of n.nodeEls().values())o.classList.remove("is-selected");for(let o of n.edgeEls().values())o.classList.remove("is-selected");if(H(n.connectors),n.flow.setLatchedEdge(e&&e.kind==="edge"?e.id:null),!e){n.canvas.removeAttribute("aria-activedescendant"),this.retraceFocus(),n.flow.stop(),n.syncBundles();return}if(e.kind==="node"){let o=n.nodeEls().get(e.id);o&&(o.classList.add("is-selected"),n.canvas.setAttribute("aria-activedescendant",o.id))}else if(e.kind==="edge"){let o=n.edgeEls().get(e.id);o&&o.classList.add("is-selected")}else e.kind==="issue"&&this.highlightIssue(e.id);this.focusLocked&&e.kind==="node"?(this.focusNodeId=e.id,this.retraceFocus()):this.focusLocked&&this.trace(null,"is-focusing","lineage"),n.flow.stop(),n.syncBundles()}highlightIssue(e){let n=this.ctx,o=n.index(),i=n.frame();if(!o||!i)return;let s=o.issueById.get(e);if(!s)return;let r=s.nodeIds[0];if(r){let l=n.nodeEls().get(o.visibleRepresentative(r,n.collapsed()));l&&(l.classList.add("is-selected"),n.canvas.setAttribute("aria-activedescendant",l.id))}for(let l of s.edgeIds)for(let[a,d]of n.edgeEls())(d.getAttribute("data-edge-ids")||a).split(" ").indexOf(l)>=0&&d.classList.add("is-selected");Ml(n.connectors,o,i,n.collapsed(),s)}hoverIntent(e){if(this.hoverTimer!==null){if(this.pendingHover===e)return;clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null}if(this.hoverId===e)return;let n=e?400:120;if(n<=0){this.setHover(e);return}this.pendingHover=e,this.hoverTimer=setTimeout(()=>{this.hoverTimer=null,this.pendingHover=null,this.setHover(e)},n)}setHover(e){if(this.hoverId===e||(this.hoverId=e,this.focusLocked))return;let n=this.ctx;if(!e){this.trace(null,"is-tracing","direct"),n.tooltip.hide(),n.flow.stop(),n.syncBundles();return}this.trace(e,"is-tracing","direct"),n.flow.stream(e,"direct"),n.syncBundles();let o=n.index(),i=n.frame();if(!o||!i)return;let s=i.boxes.get(e);s&&n.tooltip.showNode(o,e,s,r=>n.keep(r))}trace(e,n,o){let i=this.ctx;tl({nodes:i.nodeEls(),edges:i.edgeEls(),canvas:i.canvas},i.routes(),e,n,o),i.syncBundles()}toggleFocusMode(e){let n=this.ctx;if(this.focusLocked){this.focusLocked=!1,this.focusNodeId=null,this.focusShown=null,n.canvas.classList.remove("is-focusing"),this.trace(null,"is-focusing","lineage"),n.flow.clear(),n.syncBundles(),n.announce("Focus mode off.");let o=this.hoverId;o&&(this.hoverId=null,this.setHover(o));return}if(!e||e.kind!=="node"){n.toast("Select a node first, then press F to focus.");return}this.focusLocked=!0,this.focusNodeId=e.id,this.retraceFocus(),this.focusLocked&&(n.flow.stream(this.focusShown??e.id,"lineage"),n.announce("Focus mode on."))}destroy(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null)}};function kl(t,e,n,o){let i=n?t.querySelector(".mlv-group__header"):t;if(!i)return;let s=t.querySelector(".mlv-group__chevron-btn");s&&(_(s,"click",d=>{d.preventDefault(),d.stopPropagation(),o.isGroup(e)&&o.toggleCollapse(e)}),_(s,"dblclick",d=>{d.preventDefault(),d.stopPropagation()}));let r=n||o.isGroup(e),l=null,a=()=>{l!==null&&(clearTimeout(l),l=null)};o.addDisposer(a),_(i,"click",d=>{if(d.stopPropagation(),!r){o.activateNode(e,d);return}a(),l=setTimeout(()=>{l=null,o.activateNode(e)},220)}),_(i,"dblclick",d=>{d.preventDefault(),d.stopPropagation(),a(),o.isGroup(e)?o.toggleCollapse(e):r||o.openNode(e,!1)}),_(i,"pointerenter",()=>o.hoverIntent(e)),_(i,"pointerleave",()=>o.hoverIntent(null)),_(i,"keydown",d=>{d.key==="Enter"&&!d.ctrlKey&&!d.metaKey&&!d.shiftKey?(d.preventDefault(),d.stopPropagation(),o.openNode(e,d.altKey)):d.key===" "&&o.isGroup(e)&&(d.preventDefault(),d.stopPropagation(),o.toggleCollapse(e))})}function Ll(t,e,n){let o=t.querySelector(".mlv-edge__hit");if(!o)return;let i=o;_(i,"click",s=>{s.stopPropagation(),n.activateEdge(e.id,s)}),_(i,"dblclick",s=>{s.preventDefault(),s.stopPropagation(),n.openEdge(e.id,!1)}),_(i,"pointerenter",()=>n.enterEdge(e)),_(i,"pointerleave",()=>n.leaveEdge(e)),_(i,"keydown",s=>{s.key!=="Enter"||s.ctrlKey||s.metaKey||s.shiftKey||(s.preventDefault(),s.stopPropagation(),n.openEdge(e.id,s.altKey))}),_(i,"focus",()=>{t.classList.add("is-hover"),n.syncBundles(),n.pulse(e)}),_(i,"blur",()=>{t.classList.remove("is-hover"),n.syncBundles(),n.stopFlow()})}var Cn=class{constructor(e,n){this.index=null;this.frameData=null;this.routes=[];this.labelPlan=null;this.collapsedSet=new Set;this.staleFiles=new Map;this.nodeEls=new Map;this.edgeEls=new Map;this.markerPoints=new Map;this.bundles=new hn;this.mountSerial=ps();this.disposers=[];this.lastArea=null;this.host=n,this.canvasEl=e.canvas,this.worldEl=e.world,this.lanesLayer=e.lanesLayer,this.edgesSvg=e.edgesSvg,this.bundleGroup=e.bundleGroup,this.edgeGroup=e.edgeGroup,this.connectorLayer=e.connectorLayer,this.nodesLayer=e.nodesLayer,this.zoomLevelEl=e.zoomLevel,this.stateHost=e.stateHost,this.tooltip=new kn(()=>this.viewport.vp,()=>this.viewport.size()),this.canvasEl.appendChild(this.tooltip.root),this.minimap=new xn((i,s)=>{let r=this.viewport.size();this.viewport.set({x:r.w/2-i*this.viewport.vp.zoom,y:r.h/2-s*this.viewport.vp.zoom})},i=>this.host.onMinimapCollapsed(i)),e.main.insertBefore(this.minimap.root,this.canvasEl),this.toasts=new Ln,this.canvasEl.appendChild(this.toasts.root),this.viewport=new yn(this.canvasEl,this.worldEl,i=>{this.zoomLevelEl.textContent=Math.round(i.zoom*100)+"%";let s=this.viewport.size();this.minimap.setViewport(i,s.w,s.h),this.lastArea=this.viewport.visibleArea(),this.host.onViewportChange(i)});for(let i of Sl(this.canvasEl,this.viewport,{onKeyDown:s=>this.host.onKeyDown(s),onBackgroundClick:()=>this.host.onBackgroundClick(),onResize:()=>this.handleResize()}))this.disposers.push(i);let o=typeof window<"u"?window.ResizeObserver:void 0;if(o){let i=new o(()=>this.handleResize());i.observe(this.canvasEl),this.disposers.push(()=>i.disconnect())}this.flow=new _n({canvas:this.canvasEl,edges:()=>this.edgeEls,nodes:()=>this.nodeEls,routes:()=>this.routes,index:()=>this.index,hoverNodeId:()=>this.emphasis.hoverNodeId,lockedNodeId:()=>this.emphasis.lockedNodeId,hoverEdgeId:()=>this.edgeHover.hoveredId,toast:i=>this.toasts.show(i)}),this.emphasis=new In({canvas:this.canvasEl,connectors:this.connectorLayer,flow:this.flow,tooltip:this.tooltip,nodeEls:()=>this.nodeEls,edgeEls:()=>this.edgeEls,routes:()=>this.routes,index:()=>this.index,frame:()=>this.frameData,collapsed:()=>this.collapsedSet,syncBundles:()=>this.syncBundles(),keep:i=>this.host.keep(i),announce:i=>this.host.announce(i),toast:i=>this.toasts.show(i)}),this.edgeHover=new Sn({canvas:this.canvasEl,viewport:this.viewport,routes:()=>this.routes,edges:()=>this.edgeEls,markers:()=>this.markerPoints,open:i=>{this.index&&(this.tooltip.showEdge(this.index,i,s=>this.host.keep(s),this.markerPoints.get(i.id)),this.flow.pulse(i))},close:()=>{this.emphasis.hoverNodeId||this.tooltip.hide(),this.flow.stop()},hide:()=>this.tooltip.hide(),changed:()=>this.syncBundles(),openDelayMs:400,closeDelayMs:120}),this.disposers.push(this.edgeHover.wire())}get frame(){return this.frameData}get collapsed(){return this.collapsedSet}setIndex(e){this.index=e}setCollapsed(e){this.collapsedSet=new Set(e)}get minimapCollapsed(){return this.minimap.collapsed}setMinimapCollapsed(e){this.minimap.setCollapsed(e)}setPanelWidth(e){e>0&&this.minimap.root.classList.toggle("is-narrow",e<=900)}minimapUnavailable(){let e=this.minimap.root;return e.hidden?"Shown when "+30+" or more cards are drawn":e.classList.contains("is-narrow")?"No room in a panel "+900+" px wide or narrower":e.classList.contains("is-short")?"No room in a canvas under "+350+" px tall":null}setStale(e){this.staleFiles=new Map(e.map(n=>[n.path,n.reason]))}nodeElement(e){return this.nodeEls.get(e)}relayout(){this.index&&(this.frameData=kr(this.index,this.collapsedSet),this.routes=Or(this.index,this.frameData,this.collapsedSet),this.labelPlan=hs(this.frameData,this.routes),this.viewport.setContent(this.frameData.width,this.frameData.height,this.frameData),this.render())}planInputs(){return!this.index||!this.frameData?null:{index:this.index,frame:this.frameData,routes:this.routes,labels:this.labelPlan?this.labelPlan.placements:null,mountSerial:this.mountSerial,keep:e=>this.host.keep(e),staleFiles:this.staleFiles}}scenePlan(){let e=this.planInputs();return e?vn(e):null}render(){let e=this.planInputs();if(!e)return;let n=this.nodeWiring(),o=this.edgeWiring(),i=Xr({world:this.worldEl,lanes:this.lanesLayer,edgesSvg:this.edgesSvg,bundleGroup:this.bundleGroup,edgeGroup:this.edgeGroup,connectors:this.connectorLayer,nodes:this.nodesLayer},{...e,wireNode:(s,r,l)=>kl(s,r,l,n),wireEdge:(s,r)=>Ll(s,r,o)});this.nodeEls=i.nodeEls,this.edgeEls=i.edgeEls,this.markerPoints=new Map;for(let s of i.plan.edges)s.severity&&this.markerPoints.set(s.route.id,go(s));this.bundles.adopt(i.bundleEls,i.plan.bundles.map(s=>s.bundle)),this.syncBundles(),this.emphasis.resetHover(),this.edgeHover.reset(),this.flow.clear(),this.renderMinimap(),this.renderEmptyState()}renderMinimap(){if(!this.index||!this.frameData)return;let e=Yr(this.index,this.frameData,n=>this.host.keep(n));this.minimap.render(e,this.frameData.width,this.frameData.height),this.minimap.root.hidden=e.length<30,this.syncShortCanvas()}syncShortCanvas(){let e=this.canvasEl.getBoundingClientRect().height;e>0&&this.minimap.root.classList.toggle("is-short",e<350)}handleResize(){let e=this.host.keptTarget(),n=e?this.targetRect(e):null,o=this.lastArea,i=!!(n&&o&&this.viewport.isVisible(n,o));n&&i?(this.viewport.acceptResize(),this.viewport.apply(),this.viewport.isVisible(n)||this.viewport.revealRect(n)):this.viewport.onResize(),this.syncShortCanvas()}afterSheetToggle(){this.viewport.acceptResize(),this.handleResize(),this.lastArea=this.viewport.visibleArea()}afterChromeChange(){this.viewport.refitIfFitted(),this.syncShortCanvas()}renderEmptyState(){let e=this.stateHost.querySelector(".mlv-state--empty");if(e&&this.stateHost.removeChild(e),!this.index)return;let n=this.index.graph;n.nodes.length===0?this.stateHost.appendChild(ml(n)):this.frameData&&this.frameData.boxes.size===0&&this.stateHost.appendChild(gl(()=>this.host.clearFilters()))}nodeWiring(){return{isGroup:e=>!!this.index&&this.index.isGroup(e),toggleCollapse:e=>this.toggleCollapse(e),hoverIntent:e=>this.emphasis.hoverIntent(e),activateNode:(e,n)=>this.host.activateNode(e,n),openNode:(e,n)=>this.host.openNode(e,n),addDisposer:e=>this.disposers.push(e)}}edgeWiring(){return{activateEdge:(e,n)=>this.host.activateEdge(e,n),openEdge:(e,n)=>this.host.openEdge(e,n),enterEdge:e=>this.edgeHover.enter(e),leaveEdge:e=>this.edgeHover.leave(e),syncBundles:()=>this.syncBundles(),pulse:e=>this.flow.pulse(e),stopFlow:()=>this.flow.stop()}}incidentRoutes(e){return this.flow.incidentRoutes(e)}focusEdge(e){return this.flow.focusEdge(e)}blurFocusedEdge(){return this.flow.blurFocusedEdge()}edgeAnnouncement(e){return this.flow.edgeAnnouncement(e)}applySelection(e){this.emphasis.applySelection(e)}hoverIntent(e){this.emphasis.hoverIntent(e)}setHover(e){this.emphasis.setHover(e)}get isFocusLocked(){return this.emphasis.isFocusLocked}toggleFocusMode(e){this.emphasis.toggleFocusMode(e)}syncBundles(){this.bundles.sync(this.edgeEls)}toggleCollapse(e){if(!this.frameData)return;let n=this.frameData.boxes.get(e),o=this.viewport.vp.zoom,i=n?{x:n.x*o+this.viewport.vp.x,y:n.y*o+this.viewport.vp.y}:null;this.collapsedSet.has(e)?this.collapsedSet.delete(e):this.collapsedSet.add(e),this.relayout();let s=this.frameData.boxes.get(e);s&&i&&this.viewport.set({x:i.x-s.x*this.viewport.vp.zoom,y:i.y-s.y*this.viewport.vp.zoom});let r=this.index?this.index.nodeById.get(e):null,l=this.index?this.index.descendantCount(e):0;this.host.afterCollapse(),this.host.announce("Group "+(r?r.label:e)+(this.collapsedSet.has(e)?" collapsed, "+l+(l===1?" step":" steps")+" hidden.":" expanded."))}overview(){if(!this.index)return 0;let e=[];for(let n of this.index.graph.nodes||[])this.index.isGroup(n.id)&&e.push(n.id);return this.collapsedSet=new Set(e),this.relayout(),this.viewport.fitWhole(),this.host.afterCollapse(),this.host.announce("Overview: "+e.length+(e.length===1?" group":" groups")+" collapsed, whole diagram fitted."),e.length}expandAncestors(e){if(!this.index)return!1;let n=!1;for(let o of this.index.ancestors(e))this.collapsedSet.delete(o)&&(n=!0);return n&&this.relayout(),n}centerOnNode(e,n){if(!this.index||!this.frameData)return;let o=this.index.visibleRepresentative(e,this.collapsedSet),i=this.frameData.boxes.get(o);if(!i||(this.viewport.centerOn(i),!n))return;let s=this.nodeEls.get(o);s&&(s.classList.add("is-pulse"),setTimeout(()=>s.classList.remove("is-pulse"),700))}revealNode(e,n){if(!this.index||!this.frameData)return;let o=this.frameData.boxes.get(this.index.visibleRepresentative(e,this.collapsedSet));if(o){if(this.viewport.vp.zoom>=Fe){this.centerOnNode(e,n);return}this.viewport.centerOn(o,Math.max(this.viewport.vp.zoom,this.readableZoomFor(o))),n&&this.pulseNode(this.index.visibleRepresentative(e,this.collapsedSet))}}keepInView(e){let n=this.targetRect(e);n&&!this.viewport.isVisible(n)&&this.viewport.centerOn(n)}refresh(e){let n=this.canvasEl.ownerDocument,o=n?n.activeElement:null,i=!!o&&o!==this.canvasEl&&this.canvasEl.contains(o)&&typeof o.closest=="function",s=i&&o.closest("[data-node-id]")?.getAttribute("data-node-id")||null,r=i&&!s&&o.classList.contains("mlv-edge__hit")&&o.closest("[data-edge-id]")?.getAttribute("data-edge-id")||null;this.render(),this.applySelection(e);let l=null;if(s){let a=this.nodeEls.get(s);l=a&&a.classList.contains("mlv-group")?a.querySelector(".mlv-group__header"):a}else r&&(l=this.edgeEls.get(r)?.querySelector(".mlv-edge__hit"));if(l)try{l.focus()}catch{}}frameIssue(e){if(!this.index||!this.frameData)return;let n=this.index.issueById.get(e);if(!n)return;let o=[],i=[];for(let d of n.nodeIds){let c=this.targetRect({kind:"node",id:d});c&&o.push(c)}for(let d of n.edgeIds){let c=this.targetRect({kind:"edge",id:d});c&&i.push(c)}let s=o.concat(i);if(!s.length)return;let r=d=>{let c=Math.min(...d.map(h=>h.x)),u=Math.min(...d.map(h=>h.y));return{x:c,y:u,w:Math.max(...d.map(h=>h.x+h.w))-c,h:Math.max(...d.map(h=>h.y+h.h))-u}},l=r(s),a=!o.length||!i.length||this.viewport.fitsAt(l,bn)?l:r(o);this.viewport.frameRect(a,s[0],_t),n.nodeIds[0]&&this.pulseNode(this.index.visibleRepresentative(n.nodeIds[0],this.collapsedSet))}revealEdge(e){let n=this.targetRect({kind:"edge",id:e});n&&(this.viewport.vp.zoomn.classList.remove("is-pulse"),700))}zoomStep(e){let n=this.viewport.size();this.viewport.zoomAt(e>0?1.2:1/1.2,n.w/2,n.h/2)}fit(){this.viewport.fit()}fitWhole(){this.viewport.fitWhole()}zoomToNode(e){if(!this.index||!this.frameData)return;let n=this.frameData.boxes.get(this.index.visibleRepresentative(e,this.collapsedSet));n&&this.viewport.zoomToBox(n)}nextSelection(e,n){if(!this.index||!this.frameData)return null;let o=e&&this.frameData.boxes.has(e)?e:null,i=o?Hr(this.index,this.frameData,o,n):Uo(this.frameData);return i?{id:i.id,visible:this.viewport.isVisible(i)}:null}labelOf(e){let n=this.nodeEls.get(e);return n?n.getAttribute("aria-label"):null}toast(e){this.toasts.show(e)}destroy(){this.emphasis.destroy(),this.edgeHover.destroy();for(let e of this.disposers)try{e()}catch{}this.disposers=[],this.flow.destroy(),this.toasts.destroy()}};var Tl='button, [role="button"], a[href], select';function Rl(t){return typeof t.getClientRects=="function"&&t.getClientRects().length>0}var Tn=class{constructor(e,n="input, textarea"){this.activeIndex=0;this.activeEl=null;this.disposers=[];this.container=e,this.ownStopSelector=n,this.disposers.push(_(e,"keydown",o=>this.onKey(o))),this.disposers.push(_(e,"focusin",o=>{let i=o.target;if(!i||typeof i.closest!="function")return;let r=this.items().indexOf(i);r>=0&&this.setActive(r,!1)})),this.sync()}items(){let e=this.container.querySelectorAll(Tl),n=Rl(this.container),o=[];for(let i=0;i=0?s:r>=0?r:this.activeEl?Math.min(this.activeIndex,e.length-1):0;this.activeIndex=l,(s>=0||this.activeEl)&&(this.activeEl=e[l]);for(let a=0;a"u")return!1;let t=navigator,e=t.userAgentData&&t.userAgentData.platform||t.platform||t.userAgent||"";return/mac/i.test(e)}catch{return!1}}function De(t,e=si()){return e?t.replace(/^Mod\+/,"⌘").replace(/^Ctrl\+/,"⌃").replace(/^Alt\+/,"⌥").replace(/^Shift\+/,"⇧"):t.replace(/^Mod\+/,"Ctrl+")}var Ol=[{id:"search",label:"Search steps and findings",icon:"search",keys:"Mod+F",folded:!0},{id:"about",label:"About this revision",icon:"info",folded:!0},{id:"exceptions",label:"Not observed",icon:"notobserved",check:!0,folded:!0},{id:"legend",label:"Legend",icon:"legend",keys:"L",check:!0,group:!0},{id:"flow",label:"Connection flow animation",icon:"flow",keys:"A",check:!0},{id:"minimap",label:"Overview map",icon:"minimap",check:!0},{id:"rail",label:"Side panel",icon:"rail",keys:"B",check:!0},{id:"fit",label:"Fit the whole diagram",icon:"fit",group:!0},{id:"zoomsel",label:"Zoom to the selection",icon:"target",keys:"Z"},{id:"svg",label:"Export SVG…",icon:"image",group:!0},{id:"png",label:"Export PNG…",icon:"image"},{id:"copy-svg",label:"Copy SVG",icon:"copy"},{id:"shortcuts",label:"Keyboard shortcuts",icon:"keyboard",keys:"?",group:!0}],Hl=["svg","png","copy-svg"],Jf=0,Rn=class{constructor(e,n=()=>{}){this.items=new Map;this.openState=!1;this.disposers=[];this.onPick=e,this.beforeOpen=n;let o="mlv-more"+ ++Jf;this.button=ne("mlv-btn mlv-btn--icon mlv-btn--more","More actions"),this.button.title="More: legend, flow, fit, export, shortcuts",this.button.appendChild(D("more",16)),this.button.setAttribute("aria-haspopup","menu"),this.button.setAttribute("aria-expanded","false"),this.button.setAttribute("aria-controls",o),_(this.button,"click",s=>{s.preventDefault(),s.stopPropagation(),this.setOpen(!this.openState)}),_(this.button,"keydown",s=>{if(s.key==="Escape"){if(!this.openState)return;s.preventDefault(),s.stopPropagation(),this.setOpen(!1);return}s.key!=="ArrowDown"&&s.key!=="ArrowUp"||(s.preventDefault(),s.stopPropagation(),this.setOpen(!0),this.focusItem(s.key==="ArrowDown"?0:this.shown().length-1))}),this.panel=f("div","mlv-moremenu"),this.panel.id=o,this.panel.setAttribute("role","menu"),this.panel.setAttribute("aria-label","More actions"),this.panel.hidden=!0;for(let s of Ol){if(s.group){let l=g(this.panel,f("div","mlv-moremenu__sep"));l.setAttribute("role","separator"),l.setAttribute("data-sep-before",s.id)}let r=f("button","mlv-moremenu__item");r.type="button",r.tabIndex=-1,r.setAttribute("role",s.check?"menuitemcheckbox":"menuitem"),r.setAttribute("data-more-item",s.id),Hl.indexOf(s.id)>=0&&r.setAttribute("data-export-action",s.id),s.folded&&r.setAttribute("data-folded","1"),r.appendChild(D(s.icon,14)),g(r,f("span","mlv-moremenu__label",s.label)),s.id==="minimap"&&(g(r,f("span","mlv-moremenu__note")).hidden=!0),s.keys&&g(r,f("kbd","mlv-moremenu__keys",De(s.keys))).setAttribute("aria-hidden","true"),_(r,"click",l=>{if(l.preventDefault(),r.disabled)return;let a=l.detail===0;this.setOpen(!1),this.onPick(s.id,a)}),this.panel.appendChild(r),this.items.set(s.id,r)}_(this.panel,"keydown",s=>this.onKey(s));let i=typeof document>"u"?null:document;i&&(this.disposers.push(_(i,"pointerdown",s=>{if(!this.openState)return;let r=s.target;r&&(this.panel.contains(r)||this.button.contains(r))||this.setOpen(!1)})),this.disposers.push(_(i,"keydown",s=>{if(!this.openState||s.key!=="Escape")return;s.preventDefault(),s.stopPropagation();let r=i.activeElement,l=!!r&&(this.panel.contains(r)||this.button.contains(r));this.setOpen(!1),l&&this.focusButton()},!0)))}get open(){return this.openState}update(e){for(let[o,i]of this.items){let s=Ol.find(l=>l.id===o),r=!s.folded||(o==="search"?e.folded.search:o==="about"?e.folded.about:e.folded.exceptions);if(i.hidden=!r,o==="about"){let l=i.querySelector(".mlv-moremenu__label");l&&(l.textContent=e.aboutLabel),i.title="Show About: the request, coverage, limitations and provenance"}if(o==="exceptions"){let l=i.querySelector(".mlv-moremenu__label");l&&(l.textContent=e.exceptionsLabel),i.title=e.exceptionsOn?"The observed claims are faded; choose to show them again":"Fade the observed claims so these stand out"}if(o==="rail"){let l=i.querySelector(".mlv-moremenu__label");l&&(l.textContent=e.railMode==="sheet"?"Bottom panel":"Side panel"),i.title=e.railMode==="sheet"?"Open or collapse the bottom panel":"Show or hide the side panel"}if(s.check){let l=o==="legend"?e.legendOpen:o==="flow"?e.flowOn:o==="minimap"?e.minimapShown&&!e.minimapUnavailable:o==="exceptions"?e.exceptionsOn:e.railOpen;i.setAttribute("aria-checked",l?"true":"false")}if(o==="minimap"){let l=i.querySelector(".mlv-moremenu__note");l&&(l.textContent=e.minimapUnavailable||"",l.hidden=!e.minimapUnavailable),i.disabled=!!e.minimapUnavailable,i.title=e.minimapUnavailable?"Overview map: "+e.minimapUnavailable.charAt(0).toLowerCase()+e.minimapUnavailable.slice(1):"Show or hide the overview map"}o==="zoomsel"&&(i.disabled=!e.hasSelection),Hl.indexOf(o)>=0&&(i.disabled=!e.canExport),i.disabled?i.setAttribute("aria-disabled","true"):i.removeAttribute("aria-disabled")}let n=this.shown()[0];for(let o of Array.from(this.panel.querySelectorAll(".mlv-moremenu__sep")))o.hidden=!!n&&o.getAttribute("data-sep-before")===n.getAttribute("data-more-item")}setOpen(e){if(this.openState!==e)if(this.openState=e,this.panel.hidden=!e,this.button.setAttribute("aria-expanded",e?"true":"false"),e){try{this.beforeOpen()}catch{}this.position(),this.focusItem(0)}else{let n=this.panel.ownerDocument?this.panel.ownerDocument.activeElement:null;n&&this.panel.contains(n)&&this.focusButton()}}shown(){return Array.from(this.items.values()).filter(e=>!e.hidden&&!e.disabled)}position(){let e=null;try{e=this.button.getBoundingClientRect()}catch{e=null}e&&(this.panel.style.position="fixed",this.panel.style.top=Math.round(e.bottom+4)+"px",this.panel.style.left="auto",this.panel.style.right=Math.max(8,Math.round(ep()-e.right))+"px")}focusItem(e){let n=this.shown();if(!n.length)return;let o=(e%n.length+n.length)%n.length;try{n[o].focus()}catch{}}focusButton(){try{this.button.focus()}catch{}}onKey(e){if(e.key==="Escape"){e.preventDefault(),e.stopPropagation(),this.setOpen(!1);return}if(e.key==="Tab"){this.setOpen(!1);return}let n=this.shown(),o=n.indexOf(e.target);if(!(o<0)){if(e.key==="ArrowDown")this.focusItem(o+1);else if(e.key==="ArrowUp")this.focusItem(o-1);else if(e.key==="Home")this.focusItem(0);else if(e.key==="End")this.focusItem(n.length-1);else return;e.preventDefault(),e.stopPropagation()}}destroy(){for(let e of this.disposers)try{e()}catch{}this.disposers=[]}};function ep(){try{if(typeof window<"u"&&window.innerWidth)return window.innerWidth}catch{}return 1280}var ri={mid:620,wide:1e3,full:1200};function tp(t){return t>0?tt+" "+(t===1?e:n);function Nl(t){let e=t.steps+t.connections+t.findings,n=[];return t.steps&&n.push(ke(t.steps,"step","steps")),t.connections&&n.push(ke(t.connections,"connection","connections")),t.findings&&n.push(ke(t.findings,"finding","findings")),e+" not observed ("+n.join(", ")+")"}function np(t){let e=t.coverage.limitations.length;return"Coverage: "+t.coverage.status+(e?" · "+ke(e,"limitation","limitations"):"")}var Pl=4;function op(t){let e=t.steps+t.connections+t.findings;return e+(e===1?" claim":" claims")+" not observed"}var ip=0,On=class{constructor(e,n){this.sevButtons=new Map;this.roving=null;this.layout="full";this.width=0;this.fitLevel=0;this.hasDocument=!1;this.hasExceptions=!1;this.sevShown=new Set;this.searchOpen=!1;this.lastAboutLabel="About this revision";this.state=null;this.cb=e;let o="mlv"+ ++ip;this.header=f("header","mlv-header"),this.header.setAttribute("data-layout",this.layout),this.title=g(this.header,f("h1","mlv-header__title","")),this.toolbar=g(this.header,f("div","mlv-header__bar")),this.toolbar.setAttribute("role","toolbar"),this.toolbar.setAttribute("aria-label","Diagram controls"),this.toolbar.setAttribute("aria-orientation","horizontal"),this.provenance=f("button","mlv-chip mlv-header__prov"),this.provenance.type="button",g(this.provenance,f("span","mlv-header__dot")).setAttribute("aria-hidden","true"),this.provHost=g(this.provenance,f("span","mlv-header__host","")),this.provRev=g(this.provenance,f("span","mlv-header__rev","")),_(this.provenance,"click",a=>e.onAbout(a.detail===0)),this.toolbar.appendChild(this.provenance),g(this.toolbar,f("span","mlv-header__spacer")),this.search=g(this.toolbar,f("div","mlv-search")),g(this.search,D("search",14)).setAttribute("class","mlv-uicon mlv-search__glyph");let s=g(this.search,f("label","mlv-sr","Search steps, findings, IDs, or cited text"));s.htmlFor=o+"-search-input",this.searchInput=g(this.search,f("input","mlv-input")),this.searchInput.id=o+"-search-input",this.searchInput.type="search",this.searchInput.placeholder="Search steps and findings",this.searchInput.setAttribute("role","combobox"),this.searchInput.setAttribute("aria-expanded","false"),this.searchInput.setAttribute("aria-controls",o+"-search-results"),this.searchInput.autocomplete="off",this.results=g(this.search,f("ul","mlv-search__results")),this.results.id=o+"-search-results",this.results.setAttribute("role","listbox"),this.results.setAttribute("aria-label","Search results"),this.results.hidden=!0,_(this.searchInput,"input",()=>e.onQuery(this.searchInput.value)),_(this.searchInput,"keydown",a=>e.onSearchKey(a)),_(this.searchInput,"blur",()=>{this.searchInput.value||this.setSearchOpen(!1)}),this.searchBtn=ne("mlv-btn mlv-btn--icon mlv-header__searchbtn","Search ("+De("Mod+F")+")"),this.searchBtn.appendChild(D("search",16)),this.searchBtn.setAttribute("aria-controls",this.searchInput.id),_(this.searchBtn,"click",()=>e.onSearchOpen()),this.toolbar.appendChild(this.searchBtn);for(let a of Z){let d=f("button","mlv-chip mlv-chip--btn mlv-chip--sev");d.type="button",d.setAttribute("aria-pressed","true"),d.setAttribute("data-severity",a),d.appendChild(U(a,14,"")),g(d,f("span","mlv-chip__count","0")),_(d,"click",()=>e.onSeverity(a)),this.sevButtons.set(a,d),this.toolbar.appendChild(d)}this.sevUnit=g(this.toolbar,f("span","mlv-header__unit","findings")),this.sevUnit.setAttribute("aria-hidden","true"),this.sevUnit.hidden=!0,this.exceptionsBtn=f("button","mlv-chip mlv-chip--btn mlv-chip--exceptions"),this.exceptionsBtn.type="button",this.exceptionsBtn.setAttribute("aria-pressed","false"),this.exceptionsBtn.hidden=!0,_(this.exceptionsBtn,"click",()=>e.onToggleExceptions(this.exceptionsBtn.getAttribute("aria-pressed")!=="true")),this.toolbar.appendChild(this.exceptionsBtn),this.more=new Rn((a,d)=>this.pick(a,d),()=>e.onMenuOpen()),this.toolbar.appendChild(this.more.button),this.refineSlot=g(this.toolbar,f("span","mlv-header__refine")),this.status=f("footer","mlv-status"),this.status.setAttribute("data-layout",this.layout),this.counts=g(this.status,f("span","mlv-status__counts","")),this.coverage=f("button","mlv-status__coverage"),this.coverage.type="button",this.coverage.hidden=!0,_(this.coverage,"click",a=>e.onAbout(a.detail===0)),this.status.appendChild(this.coverage),g(this.status,f("span","mlv-status__spacer")),this.fresh=g(this.status,f("span","mlv-status__freshness"));let r=ne("mlv-btn mlv-btn--icon mlv-zoom__btn","Zoom out");r.appendChild(D("minus",12)),_(r,"click",()=>e.onZoom(-1));let l=ne("mlv-btn mlv-btn--icon mlv-zoom__btn","Zoom in");l.appendChild(D("plus",12)),_(l,"click",()=>e.onZoom(1)),n.insertBefore(r,n.firstChild),n.appendChild(l),this.status.appendChild(n),this.roving=new Tn(this.toolbar)}destroy(){this.roving&&this.roving.destroy(),this.roving=null,this.more.destroy()}syncRoving(){this.fitRow()}setWidth(e){let n=tp(e),o=n!==this.layout;return o&&(this.layout=n,this.header.setAttribute("data-layout",n),this.status.setAttribute("data-layout",n),this.syncSearch()),(o||e!==this.width)&&(this.width=e,this.fitRow()),n}get headerFit(){return this.fitLevel}get headerLayout(){return this.layout}setSearchOpen(e){this.searchOpen=e,this.syncSearch(),this.fitRow()}syncSearch(){let n=!(this.layout==="mid"||this.layout==="narrow")||this.searchOpen||!!this.searchInput.value;this.header.setAttribute("data-search",n?"open":"closed"),this.searchBtn.setAttribute("aria-expanded",n?"true":"false"),this.syncControls()}syncControls(){this.provenance.hidden=!this.hasDocument||this.layout==="narrow"||this.fitLevel>=1;let e=(this.layout==="mid"||this.layout==="narrow")&&this.header.getAttribute("data-search")!=="open";this.searchBtn.hidden=!(this.layout==="mid"&&e),this.exceptionsBtn.hidden=!this.hasExceptions||this.fitLevel>=2;for(let[n,o]of this.sevButtons)o.hidden=!this.sevShown.has(n)||this.fitLevel>=4;this.sevUnit.hidden=this.sevShown.size===0||this.fitLevel>=4,this.header.setAttribute("data-fit",String(this.fitLevel))}fitRow(){this.fitLevel=0,this.syncControls();let e=this.header.clientWidth,n=this.header.getAttribute("data-search")==="open"&&(this.layout==="mid"||this.layout==="narrow")?Pl:Pl-1;if(e>0)for(;this.fitLevelthis.header.clientWidth;)this.fitLevel++,this.syncControls();this.updateMenu(),this.roving&&this.roving.sync()}updateMenu(){let e=this.state;if(!e)return;let n=e.notObserved,o=this.hasExceptions&&this.fitLevel>=2;this.more.update({folded:{search:this.layout==="narrow",about:this.layout==="narrow"||this.fitLevel>=1,exceptions:o},aboutLabel:this.lastAboutLabel,exceptionsLabel:this.hasExceptions?Nl(n):"",exceptionsOn:e.exceptionsOn,legendOpen:e.legendOpen,flowOn:e.flowOn,minimapShown:!e.minimapCollapsed,minimapUnavailable:e.minimapUnavailable,railOpen:e.railOpen,railMode:e.railMode,hasSelection:e.hasSelection,canExport:!!e.graph})}pick(e,n){let o=this.cb;e==="search"?o.onSearchOpen():e==="about"?o.onAbout(n):e==="exceptions"?o.onToggleExceptions(!(this.state&&this.state.exceptionsOn)):e==="legend"?o.onToggleLegend(!(this.state&&this.state.legendOpen)):e==="flow"?o.onToggleFlow(!(this.state&&this.state.flowOn)):e==="minimap"?o.onToggleMinimap(!(this.state&&this.state.minimapCollapsed)):e==="rail"?o.onToggleRail():e==="fit"?o.onFitWhole():e==="zoomsel"?o.onZoomToSelection():e==="shortcuts"?o.onShortcuts():o.onExport(e)}update(e){this.state=e;let n=e.document,o=e.graph;this.renderHeader(e,n);let i=e.notObserved,s=i.steps+i.connections+i.findings;if(this.hasExceptions=!!o&&s>0,s>0){let l=Nl(i),a=l.indexOf(" (");H(this.exceptionsBtn),this.exceptionsBtn.appendChild(D("notobserved",14)),this.layout==="full"&&a>0?(g(this.exceptionsBtn,f("span","mlv-chip__lead",l.slice(0,a))),g(this.exceptionsBtn,f("span","mlv-chip__detail",l.slice(a)))):g(this.exceptionsBtn,f("span","mlv-chip__lead",op(i))),this.exceptionsBtn.setAttribute("aria-label",l),this.exceptionsBtn.setAttribute("aria-pressed",e.exceptionsOn?"true":"false"),this.exceptionsBtn.title=l+(e.exceptionsOn?". The observed claims are faded; press to show them again.":". Press to fade the observed claims so these stand out.")}for(let l of Z){let a=this.sevButtons.get(l),d=e.filters.severities.indexOf(l)>=0,c=e.visibleCounts[l];a.setAttribute("aria-pressed",d?"true":"false");let u=a.querySelector(".mlv-chip__count");u&&(u.textContent=String(c)),o&&c>0?this.sevShown.add(l):this.sevShown.delete(l),a.title=ke(c,l+" finding",l+" findings")+(d?". Press to hide them.":", hidden. Press to show them."),a.setAttribute("aria-label",a.title)}let r=Z.filter(l=>this.sevShown.has(l));this.sevUnit.textContent=r.length===1&&e.visibleCounts[r[0]]===1?"finding":"findings",this.syncSearch(),this.renderStatus(e),this.fitRow()}renderHeader(e,n){if(!n){this.title.textContent="",this.title.removeAttribute("title"),this.hasDocument=!1;return}this.title.textContent=n.title,this.title.title=n.title,this.hasDocument=!0;let o=n.producer.host,i=n.producer.model?" ("+n.producer.model+")":"";this.provHost.textContent=o+" · ",this.provRev.textContent=n.revision.id;let s=!!e.freshness&&e.freshness.state==="stale";this.provenance.setAttribute("data-stale",s?"true":"false");let r=n.verification?"Published "+n.verification.publishedAt+" with hashes of "+ke(Object.keys(n.verification.files||{}).length,"file","files")+".":"Published without source hashes.",l=s&&e.freshness?" "+e.freshness.text+".":"";this.provenance.title="Revision "+n.revision.id+" by "+o+i+". "+r+l+" Show About: the request, coverage and provenance.",this.provenance.setAttribute("aria-label","Revision "+n.revision.id+", "+o+"."+l+" Show About this revision"),this.lastAboutLabel="About revision "+n.revision.id+" · "+o}renderStatus(e){let n=e.graph,o=e.document;if(!n){this.counts.textContent="Waiting for a workflow…",this.coverage.hidden=!0,H(this.fresh);return}if(this.counts.textContent=ke(n.nodes.length,"step","steps")+" · "+ke(n.edges.length,"connection","connections"),o){this.coverage.hidden=!1;let s=np(o);this.coverage.textContent=this.layout==="narrow"?s.replace(/^Coverage: /,""):s,this.coverage.setAttribute("aria-label",s);let l=o.coverage.status==="scoped"?'"scoped": the assistant lists no remaining work within the stated scope.':'"partial": the assistant lists work that remains.';this.coverage.title="Coverage as the assistant recorded it. "+l+" Show About, where the limitations are listed."}else this.coverage.hidden=!0;H(this.fresh);let i=e.freshness;if(this.fresh.setAttribute("data-stale",i&&i.state==="stale"?"true":"false"),i&&i.state==="stale"){let s=g(this.fresh,f("span","mlv-status__fresh is-warn"));s.appendChild(D("warning",12)),g(s,f("span","",i.text)),s.title=i.title,s.setAttribute("data-freshness","stale")}if(e.checking)g(this.fresh,f("span","mlv-status__fresh is-checking","Checking source freshness…")).setAttribute("data-freshness","checking");else if(i&&i.state!=="stale"){let s=g(this.fresh,f("span","mlv-status__fresh is-muted",i.text));s.title=i.title,s.setAttribute("data-freshness",i.state)}}};function St(t){let e=nt(t.basis,"finding");return e?(e.removeAttribute("aria-hidden"),e.classList.add("mlv-basis-tag--rail"),e):null}function Hn(t,e){if(!e)return[];let n=[];for(let o of[t.loc].concat(t.relatedLocs||[])){let i=o&&o.file?e(o.file):void 0;i&&n.indexOf(i)<0&&n.push(i)}return n}function it(t){return ie(t)?"cites a file in another folder":t.length===1&&t[0]==="changed"?"cites a changed file":t.indexOf("changed")<0?t.length===1?"cites a missing file":"cites missing files":"cites changed or missing files"}function Nn(t,e="div"){if(!(t.fixHint||"").trim())return null;let o=f("div","mlv-insp__fix");return o.setAttribute("data-suggestion",t.id),g(o,f(e,"mlv-insp__fix-label","What to change")),g(o,f("p","mlv-insp__fix-text",t.fixHint)),o}function Mt(t,e,n,o){if(o){t.disabled=!0,t.classList.add("is-stale"),t.setAttribute("data-stale",o);let i=de(e)+": "+me[o]+". Not opened.";t.title=i,t.setAttribute("aria-label",i);return}_(t,"click",i=>{i.stopPropagation(),n(e,i.altKey)}),_(t,"keydown",i=>{i.key!=="Enter"||!i.altKey||(i.preventDefault(),i.stopPropagation(),n(e,!0))})}function Fl(t,e,n){if(H(t),g(t,f("h3","mlv-sr","Findings")),!e.index){g(t,f("div","mlv-empty-note","No workflow loaded yet."));return}let o=e.issues.filter(e.keep);if(!o.length){e.issues.length?t.appendChild(hp(n)):(e.index.graph.nodes||[]).length===0?t.appendChild(up(e)):t.appendChild(cp(e));return}for(let i of Z){let s=o.filter(a=>$(a.severity)===i);if(!s.length)continue;let r=g(t,f("section","mlv-rail__section"));r.setAttribute("data-severity-section",i);let l=g(r,f("h4","mlv-rail__heading"));l.appendChild(U(i,12,"")),g(l,f("span","",i+" · "+s.length+(s.length===1?" finding":" findings"))),r.appendChild(sp(s,i,e,n))}}function sp(t,e,n,o,i){let s=f("ul","mlv-issues");s.setAttribute("role","listbox"),s.setAttribute("aria-label",i||e+" severity findings");for(let r of t)s.appendChild(rp(r,n,o));return dp(s,o),s}function rp(t,e,n){let o=e.selectedIssueId===t.id,i=f("li","mlv-issues__item");i.setAttribute("role","presentation");let s=f("div","mlv-issue");s.setAttribute("role","option"),s.tabIndex=-1,s.setAttribute("data-issue-id",t.id),s.setAttribute("aria-selected",o?"true":"false"),s.setAttribute("aria-label",(t.short?t.short+", ":"")+t.code+" "+t.severity+" severity, "+t.title+(t.loc.file?", "+Rt(t.loc):"")+He(t.basis)),o&&s.classList.add("is-selected");let r=Hn(t,e.staleReason);r.length&&(s.classList.add("is-stale"),s.setAttribute("data-stale",r.join(" ")),s.setAttribute("aria-label",s.getAttribute("aria-label")+", "+it(r)+(ie(r)?"":" since publishing"))),s.appendChild(U(t.severity,14,"")),t.short&&g(s,f("span","mlv-issue__short",t.short));let l=g(s,f("div","mlv-issue__text"));g(l,f("div","mlv-issue__title",t.title));let a=g(l,f("div","mlv-issue__meta"));g(a,f("span","",t.code)),t.loc.file&&a.appendChild(Oe("",t.loc));let d=St(t);if(d&&a.appendChild(d),r.length&&a.appendChild(lp(r)),_(s,"click",c=>n.onSelectIssue(t.id,c)),i.appendChild(s),t.loc.file){let c=ne("mlv-btn mlv-btn--icon mlv-issue__open","Open "+de(t.loc));c.appendChild(D("open",12)),Mt(c,t.loc,n.onOpen,e.staleReason?e.staleReason(t.loc.file):void 0),i.appendChild(c)}return o&&i.appendChild(ap(t,e,n)),i}function lp(t){let e=f("span","mlv-chip mlv-chip--stale");return e.appendChild(D("warning",11)),g(e,f("span","",it(t))),e.setAttribute("data-stale",t.join(" ")),e.title=ie(t)?"A file this finding cites is not under the workspace root; it is unchanged in another folder, which the notice above names. Its jump is blocked.":"A file this finding cites no longer matches the published revision. Its jump is blocked.",e}function ap(t,e,n){let o=f("div","mlv-issue__detail");o.setAttribute("data-issue-detail",t.id),t.message&&g(o,f("p","mlv-insp__line",t.message));let i=Nn(t);i&&o.appendChild(i);let s=g(o,f("div","mlv-issue__goto"));if(t.loc.file){let r=W("mlv-btn","Go to "+de(t.loc));Mt(r,t.loc,n.onOpen,e.staleReason?e.staleReason(t.loc.file):void 0),s.appendChild(r)}for(let r of t.relatedLocs||[]){let l="Go to "+(r.message||r.role.replace(/_/g," "))+" — "+de(r),a=W("mlv-btn",l);Mt(a,r,n.onOpen,e.staleReason?e.staleReason(r.file):void 0),s.appendChild(a)}return o}function dp(t,e){let n=()=>Array.prototype.slice.call(t.querySelectorAll('[role="option"]')),o=n(),i=o.filter(s=>s.getAttribute("aria-selected")==="true")[0]||o[0]||null;for(let s of o)s.tabIndex=s===i?0:-1;_(t,"keydown",s=>{let r=s.target;if(!r||typeof r.closest!="function")return;let l=r.closest('[role="option"]');if(!l||!t.contains(l))return;let a=n(),d=a.indexOf(l),c=null;if(s.key==="ArrowDown")c=a[Math.min(a.length-1,d+1)];else if(s.key==="ArrowUp")c=a[Math.max(0,d-1)];else if(s.key==="Home")c=a[0];else if(s.key==="End")c=a[a.length-1];else if(s.key==="Enter"){if(s.ctrlKey||s.metaKey||s.shiftKey)return;s.preventDefault();let u=l.getAttribute("data-issue-id");u&&e.onOpenIssue(u,s.altKey);return}else if(s.key===" "){s.preventDefault();let u=l.getAttribute("data-issue-id");u&&e.onSelectIssue(u);return}else return;if(s.preventDefault(),!!c){for(let u of a)u.tabIndex=u===c?0:-1;c.focus()}})}function cp(t){let e=f("div","mlv-empty-note mlv-nofindings");e.setAttribute("role","status"),g(e,f("div","mlv-clean__title","No findings recorded in this revision"));let n=t.index?t.index.graph.authoredCoverage:void 0,o=n?n.status:"unknown",i=n?n.limitations:0;return g(e,f("div","mlv-clean__detail","The assistant recorded no findings. Coverage: "+o+(i?"; "+i+(i===1?" limitation":" limitations")+" listed in About":"")+". This is not a check result.")),e}function up(t){let e=f("div","mlv-empty-note");e.setAttribute("role","status"),g(e,f("div","mlv-clean__title","Nothing analyzed"));let n=t.index?t.index.graph:null,o=n?n.diagnostics||[]:[],i=n?n.workspace.filesAnalyzed:0;if(g(e,f("div","mlv-clean__detail","No ML pipeline was found, so there is nothing to flag. "+i+(i===1?" file":" files")+" analyzed.")),o.length){let s=g(e,f("ul","mlv-state__list"));for(let r of o.slice(0,5))g(s,f("li","",r.kind+" — "+r.message))}return e}function hp(t){let e=f("div","mlv-empty-note");g(e,f("div","","No findings match these filters."));let n=W("mlv-btn","Clear filters");return _(n,"click",()=>t.onClearFilters()),e.appendChild(n),e}var li='[role="treeitem"]';function Dl(t,e,n){let o=g(t,f("ul","mlv-outline"));o.setAttribute("role","tree"),o.setAttribute("aria-label","Pipeline outline");for(let i of e.index.lanes){let s=g(o,f("li","mlv-outline__item"));s.setAttribute("role","treeitem"),s.setAttribute("data-outline-lane",i.id),s.tabIndex=-1;let r=g(s,f("div","mlv-outline__row mlv-outline__row--lane"));g(r,f("span","mlv-outline__label",e.index.phaseIndexOf(i.id)+1+" · "+(i.label||i.id)));let l=e.index.laneCounts(i.id,e.keep);if(J(l)>0){r.appendChild(U(Y(l),12,""));let d=J(l),c=g(r,f("span","mlv-outline__stage mlv-outline__phasecount",xt(d)));c.title=Jo(l)}_(r,"click",()=>n.onSelectLane(i.id));let a=e.index.roots(i.id);s.setAttribute("aria-expanded",a.length?"true":"false"),a.length&&s.appendChild(jl(a,e,n))}return fp(t,e,n),yp(o,n),Gl(o,mp(o,e.selectedNodeId)),o}function fp(t,e,n){let o=g(t,f("section","mlv-relations"));g(o,f("h4","mlv-rail__heading","Text relationships"));let i=e.selectedNodeId&&e.index.nodeById.has(e.selectedNodeId)?e.selectedNodeId:null;i&&g(o,f("p","mlv-relations__context","For "+(e.index.nodeById.get(i)?.label||i)));let s=g(o,f("div","mlv-relations__views"));s.setAttribute("role","group"),s.setAttribute("aria-label","Relationship direction");let r=g(o,f("div")),l=(c,u=!1)=>{for(let m of Array.from(s.querySelectorAll("button")))m.setAttribute("aria-pressed",m.getAttribute("data-relation-view")===c?"true":"false");for(;r.firstChild;)r.removeChild(r.firstChild);let h=c==="unresolved"?e.index.graph.edges.filter(m=>m.basis==="unresolved"||e.index.nodeById.get(m.source)?.basis==="unresolved"||e.index.nodeById.get(m.target)?.basis==="unresolved"):!i||c==="all"?e.index.graph.edges:c==="incoming"?e.index.inEdges.get(i)||[]:e.index.outEdges.get(i)||[],p=g(r,f("ul","mlv-relations__list"));if(p.setAttribute("aria-label",c[0].toUpperCase()+c.slice(1)+" relationships"),!h.length){g(p,f("li","mlv-empty-note","No "+c+" relationships in this view."));return}for(let m of h){let v=e.index.nodeById.get(m.source),y=e.index.nodeById.get(m.target),S=g(p,f("li","mlv-relations__item")),x=g(S,f("button","mlv-relations__row"));x.type="button",x.setAttribute("data-relation-id",m.id);let w=i?m.target===i?"Incoming":m.source===i?"Outgoing":"Related":"Directed",E=He(m.basis);x.setAttribute("aria-label",`${w}: ${v?.label||m.source} to ${y?.label||m.target}; ${m.label||m.kind}${E?";"+E.slice(1):""}`),g(x,f("span","mlv-relations__path",(v?.label||m.source)+" → "+(y?.label||m.target))),g(x,f("span","mlv-relations__detail",(m.label||m.kind||"connection")+(re(m.basis)?" · "+re(m.basis):""))),_(x,"click",()=>n.onSelectEdge(m.id))}u&&p.querySelector("button")?.focus()},a=[{id:"all",label:"All"},{id:"incoming",label:"Incoming"},{id:"outgoing",label:"Outgoing"},{id:"unresolved",label:"Unresolved"}];for(let c of a){let u=g(s,f("button","mlv-btn",c.label));u.type="button",u.setAttribute("data-relation-view",c.id),u.disabled=!i&&c.id!=="all"&&c.id!=="unresolved",_(u,"click",()=>{n.onRelationMode(c.id),l(c.id,!0)})}let d=!i&&(e.relationMode==="incoming"||e.relationMode==="outgoing")?"all":e.relationMode;l(d)}function jl(t,e,n){let o=f("ul");o.setAttribute("role","group");for(let i of t){let s=e.index.nodeById.get(i);if(!s)continue;let r=g(o,f("li","mlv-outline__item"));r.setAttribute("role","treeitem"),r.setAttribute("data-outline-id",i),r.tabIndex=-1;let l=e.index.laneChildren(i),a=l.length>0&&e.collapsed.has(i);l.length&&r.setAttribute("aria-expanded",a?"false":"true");let d=g(r,f("div","mlv-outline__row")),c=e.selectedNodeId===i;if(r.setAttribute("aria-selected",c?"true":"false"),c&&d.classList.add("is-selected"),l.length){let p=D("chevron",12);p.setAttribute("class","mlv-uicon mlv-outline__chevron"),d.appendChild(p)}else g(d,f("span","mlv-outline__chevron mlv-outline__chevron--none"));g(d,f("span","mlv-outline__label",s.label||s.qualname));let u=re(s.basis);u&&g(d,f("span","mlv-outline__stage",u)).setAttribute("data-basis",s.basis||"");let h=pp(e,i);if(h&&d.appendChild(h),_(d,"click",p=>n.onSelectNode(i,p)),l.length){let p=jl(l,e,n);p.hidden=a,r.appendChild(p)}}return o}function pp(t,e){let n=Y(t.index.subtreeCounts(e,t.keep));return n?U(n,12,""):null}function ai(t){return Array.prototype.slice.call(t.querySelectorAll(li)).filter(n=>{let o=n.parentElement;for(;o&&o!==t;){if(o.hidden)return!1;o=o.parentElement}return!0})}function Gl(t,e){let n=ai(t),o=e&&n.indexOf(e)>=0?e:n[0]||null;for(let i of n)i.tabIndex=i===o?0:-1}function mp(t,e){if(!e)return null;let n=ai(t);for(let o of n)if(o.getAttribute("data-outline-id")===e)return o;return null}function je(t,e){e&&(Gl(t,e),e.focus())}function gp(t,e){let n=e.parentElement;for(;n&&n!==t;){if(n.getAttribute("role")==="treeitem")return n;n=n.parentElement}return null}function vp(t){let e=t.querySelector('[role="group"]');return!e||e.hidden?null:e.querySelector(li)}function Bl(t,e){let n=t.getAttribute("data-outline-id");if(n){e.onSelectNode(n);return}let o=t.getAttribute("data-outline-lane");o&&e.onSelectLane(o)}function bp(t){let e=t.querySelector(".mlv-outline__label");return(e&&e.textContent||"").toLowerCase()}function yp(t,e){_(t,"keydown",n=>{let o=n.target;if(!o||typeof o.closest!="function")return;let i=o.closest(li);if(!i||!t.contains(i))return;let s=ai(t),r=s.indexOf(i);if(r<0)return;let l=i.getAttribute("aria-expanded"),a=i.getAttribute("data-outline-id");switch(n.key){case"ArrowDown":n.preventDefault(),je(t,s[Math.min(s.length-1,r+1)]);return;case"ArrowUp":n.preventDefault(),je(t,s[Math.max(0,r-1)]);return;case"Home":n.preventDefault(),je(t,s[0]);return;case"End":n.preventDefault(),je(t,s[s.length-1]);return;case"ArrowRight":n.preventDefault(),l==="false"&&a?e.onToggleCollapse(a):l==="true"&&je(t,vp(i));return;case"ArrowLeft":n.preventDefault(),l==="true"&&a?e.onToggleCollapse(a):je(t,gp(t,i));return;case"Enter":if(n.ctrlKey||n.metaKey||n.shiftKey)return;n.preventDefault(),a?e.onOpenNode(a,n.altKey):Bl(i,e);return;case" ":n.preventDefault(),Bl(i,e);return;default:break}if(n.key.length!==1||n.ctrlKey||n.metaKey||n.altKey)return;let d=n.key.toLowerCase();for(let c=1;c<=s.length;c++){let u=s[(r+c)%s.length];if(bp(u).indexOf(d)===0){n.preventDefault(),je(t,u);return}}})}var xp="A matching quote shows these lines exist unchanged since publishing. Whether they support the claim is for you to judge.",Le=(t,e,n)=>t+" "+(t===1?e:n);function wp(t,e){return t==="inferred"?"Reasoned from the cited code and stated assumptions; the quotes do not show all of it directly.":t==="unresolved"?"The evidence does not settle this claim. It does not mean the "+e+" is missing.":""}function zl(t,e,n){let o=g(t,f("div","mlv-sel"));o.setAttribute("data-columns",String(e.columns)),e.issue?(o.setAttribute("data-kind","finding"),Sp(o,e.issue,e,n)):e.edge?(o.setAttribute("data-kind","connection"),_p(o,e.edge,e,n)):e.node?(o.setAttribute("data-kind",e.index.isGroup(e.node.id)?"group":"step"),Ep(o,e.node,e,n)):g(o,f("p","mlv-empty-note","Select a step, a connection or a finding to read its claim beside its evidence."))}function hi(t,e){if(e.columns===1)return{claim:t,evidence:t};let n=g(t,f("div","mlv-sel__col mlv-sel__col--claim")),o=g(t,f("div","mlv-sel__col mlv-sel__col--evidence"));return{claim:n,evidence:o}}function Ep(t,e,n,o){let i=n.index,s=i.isGroup(e.id),r=s?"group":"step",{claim:l,evidence:a}=hi(t,n),d=g(l,f("p","mlv-insp__eyebrow")),c=i.phaseIndexOf(e.stage),u=e.phaseLabel||Wl(i,e.stage),h=g(d,f("span","mlv-insp__phase"));h.setAttribute("data-stage",e.stage),se(h,c),g(h,f("span","mlv-insp__phasedot")).setAttribute("aria-hidden","true"),g(h,f("span","",c+1+" · "+u)),h.title="Phase "+(c+1)+": "+u,e.kind&&e.kind!=="unknown"&&di(d,"mlv-insp__kind",e.kind);let p=i.parentOf.get(e.id),m=p?i.nodeById.get(p):void 0;m&&di(d,"mlv-insp__parent","in "+(m.label||m.id)),g(l,f("h4","mlv-insp__title",e.label||e.qualname)),fi(l,e.basis,r),e.detail&&g(l,f("p","mlv-insp__detail",e.detail)),s?(ci(l,i.subtreeIssues(e.id,n.keep),"Findings in this group",n,o),Ip(l,e,n,o)):ci(l,i.issuesOf(e.id,n.keep),"Findings on this step",n,o);let v=()=>s?Cp(l,e,n,o):Ap(l,e,n,o);n.columns===1?(ui(a,e.evidenceLocs||[],n,o,r),v()):(v(),ui(a,e.evidenceLocs||[],n,o,r)),pi(a,o),mi(a,n,o)}function _p(t,e,n,o){let i=n.index,{claim:s,evidence:r}=hi(t,n),l=g(s,f("p","mlv-insp__eyebrow"));g(l,f("span","","Connection"));let a=di(l,"mlv-insp__edgekind",ft(e.kind)+(e.authoredKind?" · authored as "+e.authoredKind:""));a.setAttribute("data-edge-kind",e.kind),a.title="Kind of connection, as the author named it",g(s,f("h4","mlv-insp__title",e.label||"Connection"));let d=i.nodeById.get(e.source),c=i.nodeById.get(e.target),u=g(s,f("p","mlv-insp__ends"));u.appendChild(Ge(e.source,d,o)),u.appendChild(document.createTextNode(" → ")),u.appendChild(Ge(e.target,c,o)),fi(s,e.basis,"connection"),ci(s,i.issuesOfEdge(e.id,n.keep),"Findings on this connection",n,o),ui(r,e.evidenceLocs||[],n,o,"connection"),pi(r,o),mi(r,n,o)}function Sp(t,e,n,o){let i=n.index,{claim:s,evidence:r}=hi(t,n),l=g(s,f("div","mlv-insp__eyebrow mlv-insp__issue-head"));if(l.setAttribute("data-issue-id",e.id),l.appendChild(U(e.severity,14,"")),g(l,f("span","mlv-insp__severity",Tp(e.severity)+" finding")),e.short){let m=g(l,f("span","mlv-insp__short",e.short));m.title=e.short+" is this finding's number in this revision; its id is "+e.code+"."}g(l,f("span","mlv-mono",e.code));let a=St(e);a&&l.appendChild(a),g(s,f("h4","mlv-insp__title",e.title)),fi(s,e.basis,"finding"),e.message&&g(s,f("p","mlv-insp__detail mlv-insp__message",e.message));let d=Nn(e,"h5");d&&s.appendChild(d);let c=g(s,f("section","mlv-insp__section mlv-insp__cited"));c.setAttribute("data-section","cited");let u=e.nodeIds.filter(m=>i.nodeById.has(m)),h=e.edgeIds.filter(m=>i.edgeById.has(m));if(!u.length&&!h.length&&g(c,f("p","mlv-insp__note","This finding cites no step or connection: it is about the workflow as a whole.")),u.length){c.appendChild(Ae("Cited steps",Le(u.length,"step","steps")));let m=g(c,f("ul","mlv-insp__links"));for(let v of u){let y=i.nodeById.get(v),S=g(m,f("li"));S.appendChild(Ge(v,y,o));let x=y.phaseLabel||Wl(i,y.stage);g(S,f("span","mlv-insp__aside"," · "+(i.phaseIndexOf(y.stage)+1)+" · "+x))}}if(h.length){c.appendChild(Ae("Cited connections",Le(h.length,"connection","connections")));let m=g(c,f("ul","mlv-insp__links"));for(let v of h){let y=i.edgeById.get(v),S=g(m,f("li")),x=W("mlv-link mlv-insp__flow-edge",y.label||"connection","Select the connection "+vi(i,y));x.setAttribute("data-edge-id",y.id),_(x,"click",()=>o.onShowEdge(y.id)),S.appendChild(x),g(S,f("span","mlv-insp__aside"," · "+gi(i,y)))}}let p=e.relatedLocs||[];Vl(r,p,n,o,{noun:"finding",role:m=>m.role||"Supporting evidence",staleNote:()=>{let m=Hn(e,n.staleReason);return m.length?ie(m)?"This finding "+it(m)+"; those jumps are blocked. The notice above says which folder to add.":"This finding "+it(m)+" since publishing; those jumps are blocked.":""}}),pi(r,o),mi(r,n,o)}function di(t,e,n){return g(t,f("span","mlv-insp__sep"," · ")).setAttribute("aria-hidden","true"),g(t,f("span",e,n))}function fi(t,e,n){let o=wp(e,n);if(!o)return;let i=g(t,f("p","mlv-insp__basis"));i.setAttribute("data-basis",e||"");let s=n==="finding"?null:nt(e,n);s&&(s.removeAttribute("aria-hidden"),s.removeAttribute("title"),i.appendChild(s),i.appendChild(document.createTextNode(" "))),g(i,f("span","",o))}function Ae(t,e){let n=f("h5","mlv-rail__heading");return g(n,f("span","mlv-rail__headtext",t)),e&&g(n,f("span","mlv-rail__count",e)),n}function ci(t,e,n,o,i){if(!e.length)return;let s=g(t,f("section","mlv-insp__section"));s.setAttribute("data-section","findings"),s.appendChild(Ae(n,Le(e.length,"finding","findings")));for(let r of e){let l=g(s,f("div","mlv-insp__issue"));l.setAttribute("data-issue-id",r.id);let a=g(l,f("div","mlv-insp__issue-head"));if(a.appendChild(U(r.severity,14,"")),r.short){let p=g(a,f("span","mlv-insp__short",r.short));p.title=r.short+" is this finding's number in this revision; its id is "+r.code+"."}let d=W("mlv-link mlv-insp__issue-title",r.title,"Select finding "+(r.short?r.short+", ":"")+r.title);_(d,"click",()=>i.onShowIssue(r.id)),a.appendChild(d);let c=St(r);c&&a.appendChild(c),g(l,f("p","mlv-insp__issue-id mlv-mono",r.code)),r.message&&g(l,f("p","mlv-insp__line",r.message));let u=Nn(r,"h6");u&&l.appendChild(u);let h=Hn(r,o.staleReason);if(h.length){l.classList.add("is-stale");let p=g(l,f("p","mlv-insp__stale-note"));p.appendChild(D("warning",12)),g(p,f("span","","This finding "+it(h)+(ie(h)?".":" since publishing.")))}}}function ui(t,e,n,o,i){Vl(t,e,n,o,{noun:i,nav:!0,staleNote:()=>{let s=d=>n.staleReason&&d.file?n.staleReason(d.file):void 0,r=qi(e,d=>!!(n.staleReason&&n.staleReason(d)));if(!r.stale)return"";let l=e.map(s).filter(d=>!!d),a=r.stale+" of "+r.total+(r.total===1?" quote cites ":" quotes cite ");return ie(l)?a+"a file that is not under the workspace root but is unchanged in another folder; those jumps are blocked. The notice above says which folder to add.":a+"a file that changed or went missing since publishing; those jumps are blocked. To compare the claim with the code as it is now, ask the assistant for a fresh revision."}})}function Vl(t,e,n,o,i){let s=g(t,f("section","mlv-insp__section mlv-insp__quotes"));if(s.setAttribute("data-section","quotes"),!e.length){s.appendChild(Ae("Source evidence","no quotes")),g(s,f("p","mlv-empty-note mlv-insp__no-evidence","No source evidence was authored for this "+i.noun+". Its basis and the coverage limitations describe what remains uncertain."));return}s.appendChild(Ae("Source evidence",Le(e.length,"quote","quotes"))),g(s,f("p","mlv-insp__caption",xp));let r=i.staleNote();if(r){let c=g(s,f("p","mlv-insp__stale-note"));c.appendChild(D("warning",12)),g(c,f("span","",r))}let l=0,a=()=>{};if(i.nav&&e.length>1){let c=g(s,f("div","mlv-insp__evidence-nav")),u=W("mlv-btn","Previous evidence"),h=W("mlv-btn","Next evidence");a=()=>{u.disabled=l===0,h.disabled=l===e.length-1,u.title=u.disabled?"This is the first evidence item":"Open the previous evidence item",h.title=h.disabled?"This is the last evidence item":"Open the next evidence item"},_(u,"click",()=>{l>0&&o.onOpen(e[--l]),a()}),_(h,"click",()=>{l{let h=g(d,f("li","mlv-quote")),p=n.staleReason&&c.file?n.staleReason(c.file):void 0;p&&(h.classList.add("is-stale"),h.setAttribute("data-stale",p));let m=g(h,f("div","mlv-quote__head"));g(m,f("span","mlv-quote__num",String(u+1))).setAttribute("aria-hidden","true");let v=g(m,f("span","mlv-quote__loc"));g(v,f("span","mlv-quote__file",c.file)),g(v,f("span","mlv-quote__at",Mp(c))),Re(c)&&(v.title=Re(c)),i.role&&g(m,f("span","mlv-quote__role",i.role(c))),m.appendChild(kp(c,p,n));let y=W("mlv-link mlv-quote__open","Open","Open "+de(c));y.setAttribute("data-evidence-id",c.evidenceId||""),p||(y.title="Open "+de(c)+" beside the diagram and highlight the cited lines; focus stays here (Alt+click moves it)."),Re(c)&&!p&&(y.title=Re(c)),Mt(y,c,(S,x)=>{l=u,a(),o.onOpen(S,x)},p),m.appendChild(y),c.snippet&&h.appendChild(Lp(c))})}function Mp(t){let e=Te(t),n=e?" › cell "+e.cell:"",o=t.endLine&&t.endLine>t.line?t.endLine:t.line;return n+(o>t.line?", lines "+t.line+"–"+o:", line "+t.line)}function kp(t,e,n){if(e){let s=f("span","mlv-quote__fresh mlv-insp__stale");return s.setAttribute("data-fresh","stale"),s.setAttribute("data-stale",e),s.appendChild(D("warning",12)),g(s,f("span","",me[e])),s}let o=!!(n.document&&n.document.verification&&t.file&&Object.prototype.hasOwnProperty.call(n.document.verification.files||{},t.file)),i=f("span","mlv-quote__fresh is-muted",o?"unchanged":"not checked");return i.setAttribute("data-fresh",o?"unchanged":"unchecked"),i.title=o?"Unchanged means these exact lines still exist as quoted. It does not mean they support the claim.":"This revision has no published hash for this file, so MLView cannot tell whether it changed.",i}function Lp(t){let e=f("pre","mlv-quote__text"),n=String(t.snippet||"").split(` -`);return n.forEach((o,i)=>{let s=g(e,f("span","mlv-quote__line"));g(s,f("span","mlv-quote__ln",String(t.line+i))).setAttribute("aria-hidden","true"),g(s,f("span","mlv-quote__code",o)),i{let h=g(a,f("li"));h.setAttribute("data-direction",u);let p=u==="in"?c.source:c.target;p===e.id?g(h,f("span","","Loops back to this step")):(g(h,f("span","",u==="in"?"Comes from ":"Feeds ")),h.appendChild(Ge(p,i.nodeById.get(p),o))),g(h,f("span","",": "));let m=W("mlv-link mlv-insp__flow-edge",c.label||"an unlabelled connection","Select the connection "+vi(i,c));m.setAttribute("data-edge-id",c.id),_(m,"click",()=>o.onShowEdge(c.id)),h.appendChild(m);let v=c.basis==="inferred"?" (inferred, not observed)":c.basis==="unresolved"?" (unresolved)":"";g(h,f("span",v?"mlv-insp__flow-basis":"",v+"."))};for(let c of s)d(c,"in");for(let c of r)c.source!==c.target&&d(c,"out")}function Ip(t,e,n,o){let i=n.index,s=i.laneChildren(e.id).filter(a=>i.nodeById.has(a));if(!s.length)return;let r=g(t,f("section","mlv-insp__section"));r.setAttribute("data-section","members"),r.appendChild(Ae("Steps in this group",Le(s.length,"step","steps")));let l=g(r,f("ul","mlv-insp__links"));for(let a of s){let d=g(l,f("li"));d.appendChild(Ge(a,i.nodeById.get(a),o));let c=i.descendantCount(a);c&&g(d,f("span","mlv-insp__aside"," · group of "+Le(c,"step","steps")))}}function Cp(t,e,n,o){let i=n.index,s=new Set,r=h=>{s.add(h);for(let p of i.laneChildren(h))r(p)};r(e.id);let l=[],a=[];for(let h of i.graph.edges){let p=s.has(h.source),m=s.has(h.target);if(p&&m){h.source===e.id&&h.target===e.id&&l.push(h);continue}m?l.push(h):p&&a.push(h)}if(!l.length&&!a.length)return;let d=g(t,f("section","mlv-insp__section mlv-insp__flow"));d.setAttribute("data-section","connections"),d.appendChild(Ae("Connections across its edge",Le(l.length+a.length,"connection","connections")));let c=g(d,f("ul","mlv-insp__sentences")),u=(h,p)=>{let m=g(c,f("li"));m.setAttribute("data-direction",p);let v=p==="in"?h.source:h.target,y=p==="in"?h.target:h.source;v===e.id&&y===e.id?g(m,f("span","","Loops back to this group")):(g(m,f("span","",p==="in"?"Comes from ":"Feeds ")),m.appendChild(Ge(v,i.nodeById.get(v),o)),y!==e.id&&(g(m,f("span","",p==="in"?" into ":" from ")),m.appendChild(Ge(y,i.nodeById.get(y),o)))),g(m,f("span","",": "));let S=W("mlv-link mlv-insp__flow-edge",h.label||"an unlabelled connection","Select the connection "+vi(i,h));S.setAttribute("data-edge-id",h.id),_(S,"click",()=>o.onShowEdge(h.id)),m.appendChild(S);let x=h.basis==="inferred"?" (inferred, not observed)":h.basis==="unresolved"?" (unresolved)":"";g(m,f("span",x?"mlv-insp__flow-basis":"",x+"."))};for(let h of l)u(h,"in");for(let h of a)u(h,"out")}function pi(t,e){let n=g(t,f("div","mlv-insp__actions")),o=W("mlv-btn mlv-insp__challenge","Challenge this claim","Copy a prompt that asks your assistant to check this claim against the code. MLView calls no model.");_(o,"click",()=>e.onChallenge());let i=W("mlv-btn mlv-insp__refine","Refine…","Copy a refinement prompt about this selection. MLView calls no model.");_(i,"click",()=>e.onRefine()),n.append(o,i)}function mi(t,e,n){let o=(e.index.graph.diagnostics||[]).filter(r=>r.kind==="workflow_limitation").length;if(!o)return;let i=g(t,f("p","mlv-insp__limits"));i.setAttribute("data-limitations",String(o)),g(i,f("span","",(o===1?"1 document-wide limitation applies":o+" document-wide limitations apply")+" to every claim. "));let s=W("mlv-link mlv-link--inline mlv-insp__limits-show",o===1?"Read it in About":"Read them in About");s.setAttribute("aria-label",o===1?"Read the document-wide limitation in About":"Read the "+o+" document-wide limitations in About"),_(s,"click",()=>n.onShowLimitations()),i.appendChild(s)}function Ge(t,e,n){let o=e?e.label||e.id:t,i=W("mlv-link mlv-insp__flow-step",o,"Select the step "+o);return i.setAttribute("data-node-id",t),_(i,"click",()=>n.onShowNode(t)),i}function gi(t,e){let n=t.nodeById.get(e.source),o=t.nodeById.get(e.target);return(n?n.label:e.source)+" → "+(o?o.label:e.target)}function vi(t,e){return(e.label?e.label+", ":"")+"from "+gi(t,e).replace(" → "," to ")}function Wl(t,e){let n=(t.graph.stages||[]).find(o=>o.id===e);return n&&n.label||e}function Tp(t){return t&&t[0].toUpperCase()+t.slice(1)}function Kl(t,e){let n=s=>s==="inferred"?" Inferred, not observed.":s==="unresolved"?" Unresolved.":"";if(e.kind==="node"){let s=t.nodeById.get(e.id);if(!s)return"";let r=s.detail?" "+gn(s.detail):"";return"Step: "+(s.label||s.id)+"."+n(s.basis)+r}if(e.kind==="edge"){let s=t.edgeById.get(e.id);return s?"Connection: "+(s.label?s.label+", ":"")+"from "+gi(t,s).replace(" → "," to ")+"."+n(s.basis):""}let o=t.issueById.get(e.id);if(!o)return"";let i=o.message?" "+gn(o.message):"";return"Finding "+(o.short?o.short+", ":"")+o.severity+" severity: "+o.title+"."+n(o.basis)+i}var Rp=280,Op=4,Hp=3,bi=/(^|[.!?]["')\]]?\s+|\n\s*)([A-Z][A-Za-z0-9&/(),' -]{0,47}?):\s+/g;function Np(t){let e=String(t||""),n=[];bi.lastIndex=0;for(let s=bi.exec(e);s;s=bi.exec(e)){let r=s[2].trim();if(r.split(/\s+/).filter(Boolean).length>6)continue;let a=s.index+s[1].length;n.push({at:a,textAt:s.index+s[0].length,head:r})}if(n.length{let l=r+1n&&e.push({text:t.slice(n,o.index),kv:!1}),e.push({text:i,kv:!0}),n=o.index+i.length,Pn.lastIndex=n}return nRp||n.request.question.split(` -`).length>Op){r.setAttribute("data-clamped","true");let b=W("mlv-link mlv-about__more","Show all","Show the whole question");b.setAttribute("aria-expanded","false"),b.setAttribute("aria-controls",r.id),_(b,"click",()=>{let M=r.getAttribute("data-clamped")==="true";r.setAttribute("data-clamped",M?"false":"true"),b.textContent=M?"Show less":"Show all",b.setAttribute("aria-expanded",M?"true":"false"),b.setAttribute("aria-label",M?"Show less of the question":"Show the whole question")}),s.appendChild(b)}let a=ze(i,"traced","What the model traced"),d=Np(n.coverage.summary);a.setAttribute("data-paragraphs",String(d.length));for(let b of d){let M=g(a,f("p","mlv-about__para"));b.head&&(g(M,f("strong","mlv-about__lead",b.head+":")),M.appendChild(document.createTextNode(" "))),M.appendChild(document.createTextNode(b.text))}let c=ze(i,"coverage","Coverage");g(c,f("p","mlv-about__status mlv-workflow__coverage",Fp(n.coverage.status))).setAttribute("data-status",n.coverage.status);let h=n.coverage.limitations;if(h.length){let b=g(c,f("details","mlv-about__limitations mlv-workflow__limitations"));b.open=!0;let M=h.length;g(b,f("summary","",M+(M===1?" coverage limitation":" coverage limitations")+" (apply to every claim)"));let T=g(b,f("ul"));for(let L of h)g(T,f("li","",L))}else g(c,f("p","mlv-about__note","The assistant listed no coverage limitations."));let p=ze(i,"scope","Scope");g(p,f("p","mlv-about__text",n.request.scope));let m=n.request.entrypoints&&n.request.entrypoints.length?n.request.entrypoints.join(", "):"not specified";g(p,f("p","mlv-about__meta","Entrypoints: "+m));let v=ze(i,"config","Run configuration"),y=n.request.configuration;if(y){let b=g(v,f("p","mlv-about__text mlv-about__config"));for(let M of Pp(y))M.kv?g(b,f("code","mlv-about__kv",M.text)):b.appendChild(document.createTextNode(M.text))}else g(v,f("p","mlv-about__meta","Configuration: not specified."));let S=ze(i,"files","Cited files"),x=[];for(let b of n.evidence||[])x.indexOf(b.file)<0&&x.push(b.file);if(!x.length)g(S,f("p","mlv-about__note","No file is cited."));else{let b=S.querySelector(".mlv-rail__heading");b&&g(b,f("span","mlv-rail__count",x.length+(x.length===1?" file":" files")));let M=g(S,f("ul","mlv-about__files")),T=n.verification?n.verification.files||{}:null;for(let L of x){let C=g(M,f("li","mlv-about__file"));C.setAttribute("data-file",L),g(C,f("span","mlv-about__path",L)).title=L;let N=e.staleReason?e.staleReason(L):void 0;if(N){let O=g(C,f("span","mlv-quote__fresh mlv-insp__stale"));O.setAttribute("data-fresh","stale"),O.setAttribute("data-stale",N),O.appendChild(D("warning",12)),g(O,f("span","",me[N]))}else{let O=!!T&&Object.prototype.hasOwnProperty.call(T,L);g(C,f("span","mlv-quote__fresh is-muted",O?"unchanged":"not checked")).setAttribute("data-fresh",O?"unchanged":"unchecked")}}g(S,f("p","mlv-about__note","Unchanged means the quoted lines still exist as published. It does not mean they support the claims."))}let w=ze(i,"provenance","Provenance"),E=[n.producer.host,n.producer.model||"model not named","revision "+n.revision.id+(n.revision.parent?" (after "+n.revision.parent+")":"")];E.push(n.verification?"published "+n.verification.publishedAt:"published without source hashes"),g(w,f("p","mlv-about__meta",E.join(" · "))),g(w,f("p","mlv-about__trust mlv-workflow__provenance","Model-authored; MLView checks citations, not the interpretation."))}function ze(t,e,n){let o=g(t,f("section","mlv-about__section"));o.setAttribute("data-about",e);let i=g(o,f("h4","mlv-rail__heading"));return g(i,f("span","mlv-rail__headtext",n)),o}var Ie=[{id:"about",label:"About",heading:"About this revision"},{id:"issues",label:"Findings",heading:"Findings"},{id:"inspector",label:"Selection",heading:"Selection"},{id:"outline",label:"Outline",heading:"Outline"}],Ul=.05,Dp=.15,jp=0,Fn=class{constructor(e){this.tabs=new Map;this.counts=new Map;this.panels=new Map;this.relationMode="outgoing";this.relationNodeId=null;this.current="about";this.mode="docked";this.open=!0;this.cb=e;let n="mlv"+ ++jp;this.root=f("aside","mlv-rail"),this.root.id=n+"-rail",this.root.setAttribute("data-mode","docked"),this.root.setAttribute("aria-labelledby",n+"-rail-heading"),this.heading=g(this.root,f("h2","mlv-sr","Side panel")),this.heading.id=n+"-rail-heading",this.grip=g(this.root,f("div","mlv-rail__grip")),this.grip.setAttribute("role","separator"),this.grip.setAttribute("aria-orientation","vertical"),this.grip.setAttribute("aria-label","Resize side panel"),this.grip.tabIndex=0,this.wireGrip(this.grip);let o=g(this.root,f("div","mlv-rail__bar")),i=g(o,f("div","mlv-rail__tabs"));i.setAttribute("role","tablist"),i.setAttribute("aria-label","Panel");for(let s=0;se.onTab(r.id)),_(l,"keydown",d=>{let c;if(d.key==="ArrowRight")c=(s+1)%Ie.length;else if(d.key==="ArrowLeft")c=(s+Ie.length-1)%Ie.length;else if(d.key==="Home")c=0;else if(d.key==="End")c=Ie.length-1;else return;d.preventDefault();let u=Ie[c].id;e.onTab(u),this.tabs.get(u)?.focus()}),i.appendChild(l),this.tabs.set(r.id,l);let a=f("div","mlv-rail__panel");a.id=n+"-panel-"+r.id,a.setAttribute("role","tabpanel"),a.setAttribute("aria-labelledby",l.id),a.setAttribute("data-tab",r.id),a.tabIndex=0,a.hidden=!0,this.root.appendChild(a),this.panels.set(r.id,a)}this.chevron=g(o,f("button","mlv-btn mlv-btn--icon mlv-rail__chevron")),this.chevron.type="button",this.chevron.setAttribute("aria-controls",this.root.id),this.chevron.appendChild(D("chevron",14)),this.chevron.hidden=!0,_(this.chevron,"click",()=>e.onSheetToggle()),_(this.root,"keydown",s=>{s.key!=="Escape"||s.defaultPrevented||this.mode!=="sheet"||!this.open||(s.preventDefault(),s.stopPropagation(),e.onSheetCollapse())}),this.applyShape()}setMode(e,n){this.mode=e,this.root.setAttribute("data-mode",e);let o=this.root.parentElement;o&&o.setAttribute("data-rail",e),e==="sheet"&&(this.root.style.width=""),this.setSheetFraction(n),this.applyShape()}setOpen(e){this.open=e,this.applyShape()}setSheetFraction(e){if(this.root.style.setProperty("--mlv-sheet-fraction",String(e)),this.mode==="sheet"){let n=this.cb.sheetFractionMax(),o=Math.round(Math.min(e,n)*100);this.grip.setAttribute("aria-valuemax",String(Math.round(n*100))),this.grip.setAttribute("aria-valuenow",String(o)),this.grip.setAttribute("aria-valuetext",this.open?o+" percent of the height":"collapsed")}}focusTab(e){let n=this.tabs.get(e);if(n)try{n.focus()}catch{}}activePanel(){return this.panels.get(this.current)}revealLimitations(){let e=this.panels.get("about"),n=e.querySelector(".mlv-about__limitations");if(!n)return e;n.open=!0;let o=n.querySelector("summary")||n;if(typeof n.scrollIntoView=="function")try{n.scrollIntoView({block:"nearest"})}catch{}return o}applyShape(){let e=this.mode==="sheet";if(this.root.hidden=!e&&!this.open,e?this.root.setAttribute("data-expanded",this.open?"true":"false"):this.root.removeAttribute("data-expanded"),this.heading.textContent=e?"Bottom panel":"Side panel",this.grip.setAttribute("aria-orientation",e?"horizontal":"vertical"),this.grip.setAttribute("aria-label",e?"Resize the bottom panel":"Resize side panel"),e)this.grip.setAttribute("aria-valuemin","25"),this.setSheetFraction(this.cb.sheetFraction());else for(let o of["aria-valuemin","aria-valuemax","aria-valuenow","aria-valuetext"])this.grip.removeAttribute(o);this.chevron.hidden=!e,this.chevron.setAttribute("aria-expanded",this.open?"true":"false");let n=this.open?"Collapse the bottom panel (Escape)":"Expand the bottom panel";this.chevron.setAttribute("aria-label",n),this.chevron.title=n;for(let[o,i]of this.panels)i.hidden=o!==this.current||e&&!this.open}wireGrip(e){let n=0,o=0,i=0,s=0,r=0,l=!1,a=!1,d=!0,c=0,u=0,h=m=>{if(!a){this.cb.onResize(i+(n-m.clientX));return}let v=o-m.clientY;!l&&Math.abs(v)<4||(l||(l=!0,!d&&v>0&&this.cb.onSheetToggle()),c=s+v/r,this.open&&this.cb.onSheetResize(c))},p=()=>{document.removeEventListener("pointermove",h),document.removeEventListener("pointerup",p),a&&(l?this.open&&c{a=this.mode==="sheet",n=m.clientX,o=m.clientY,i=this.root.getBoundingClientRect().width||360,r=Math.max(1,this.cb.bodyHeight()),d=this.open,u=this.cb.sheetFraction(),s=d?Math.min(u,this.cb.sheetFractionMax()):32/r,c=s,l=!1,document.addEventListener("pointermove",h),document.addEventListener("pointerup",p),m.preventDefault()}),_(e,"keydown",m=>{if(this.mode==="sheet"){if(m.key==="Enter"||m.key===" ")this.cb.onSheetToggle();else if(m.key==="ArrowUp"||m.key==="ArrowDown"){let y=m.key==="ArrowUp";this.open?this.cb.onSheetResize(this.cb.sheetFraction()+(y?Ul:-Ul)):y&&this.cb.onSheetToggle()}else return;m.preventDefault();return}let v=this.root.getBoundingClientRect().width||360;if(m.key==="ArrowLeft")this.cb.onResize(v+16);else if(m.key==="ArrowRight")this.cb.onResize(v-16);else return;m.preventDefault()})}update(e){let n=this.captureFocus();this.current=e.tab;for(let[i,s]of this.tabs){let r=i===e.tab;s.setAttribute("aria-selected",r?"true":"false"),s.tabIndex=r?0:-1}let o=this.counts.get("issues");if(o){let i=e.issues.length;o.textContent=e.index?" ("+i+")":"";let s=this.tabs.get("issues");s.title=e.index?i+(i===1?" finding":" findings")+" in this revision":"",e.index?s.setAttribute("aria-label","Findings, "+i+(i===1?" finding":" findings")):s.removeAttribute("aria-label")}this.applyShape();for(let[i,s]of this.panels)i!==e.tab&&H(s);e.tab==="about"?this.renderAbout(e):e.tab==="issues"?this.renderIssues(e):e.tab==="outline"?this.renderOutline(e):this.renderSelection(e),n()}captureFocus(){let e=typeof document<"u"?document.activeElement:null;if(!e||typeof e.closest!="function"||!this.root.contains(e))return()=>{};let n=e.closest('[role="tabpanel"]'),o=()=>{if(!n||e.isConnected)return;let l=this.root.ownerDocument;if(l.activeElement&&l.activeElement!==l.body)return;let a=this.panels.get(this.current);if(!(!a||a.hidden))try{a.focus()}catch{}},i=e.closest('[data-issue-id][role="option"], [data-outline-id], [data-outline-lane], [data-relation-id]');if(!i)return o;let s=i.hasAttribute("data-issue-id")?"data-issue-id":i.hasAttribute("data-outline-id")?"data-outline-id":i.hasAttribute("data-outline-lane")?"data-outline-lane":"data-relation-id",r=i.getAttribute(s)||"";return()=>{let l=this.root.querySelector("["+s+'="'+r+'"]');if(!l){o();return}if(l.closest('[role="tabpanel"]')?.hidden)return;let d=l.closest('[role="tree"], [role="listbox"]');if(d){let c=d.querySelectorAll('[role="treeitem"], [role="option"]');for(let u=0;uthis.cb.onSelectIssue(n,o),onOpenIssue:(n,o)=>this.cb.onOpenIssue(n,o),onOpen:(n,o)=>this.cb.onOpen(n,o),onClearFilters:()=>this.cb.onClearFilters()})}renderSelection(e){let n=this.panels.get("inspector");if(H(n),g(n,f("h3","mlv-sr","Selection")),!e.index){g(n,f("div","mlv-empty-note","No workflow loaded yet."));return}zl(n,{index:e.index,keep:e.keep,node:e.selectedNode,edge:e.selectedEdge,issue:e.selectedIssue,columns:e.columns,document:e.document,staleReason:e.staleReason},{onOpen:(o,i)=>this.cb.onOpen(o,i),onShowNode:o=>this.cb.onShowNode(o),onShowEdge:o=>this.cb.onShowEdge(o),onShowIssue:o=>this.cb.onShowIssue(o),onChallenge:()=>this.cb.onChallenge(),onRefine:()=>this.cb.onRefine(),onShowLimitations:()=>this.cb.onShowLimitations()})}renderOutline(e){let n=this.panels.get("outline");H(n),g(n,f("h3","mlv-sr","Outline"));let o=e.index;if(!o){g(n,f("div","mlv-empty-note","No workflow loaded yet."));return}e.selectedNode&&(this.relationNodeId=e.selectedNode.id);let i=this.relationNodeId&&o.nodeById.has(this.relationNodeId)?this.relationNodeId:null;Dl(n,{index:o,keep:e.keep,selectedNodeId:e.selectedNode?e.selectedNode.id:i,collapsed:e.collapsed,relationMode:this.relationMode},{onSelectNode:(s,r)=>this.cb.onSelectNode(s,r),onOpenNode:(s,r)=>this.cb.onOpenNode(s,r),onSelectEdge:s=>this.cb.onSelectEdge(s),onRelationMode:s=>{this.relationMode=s},onSelectLane:s=>this.cb.onSelectLane(s),onToggleCollapse:s=>this.cb.onToggleCollapse(s)})}};var Gp=[{group:"basis",key:"observed",label:"Observed",detail:"Directly supported by cited workspace source. Most claims are observed, so they carry no mark."},{group:"basis",key:"inferred",label:"Inferred",detail:'Reasoned from cited source and stated assumptions; the quotes do not show all of it. Dashed border or line, and an "inferred" tag.'},{group:"basis",key:"unresolved",label:"Unresolved",detail:'The available evidence does not settle this claim. It does not mean the step is absent. Dotted border or line, faint hatching, and a "? unresolved" tag.'}],zp=[{group:"edge",key:"arrow",label:"connection",detail:"Points from the step that produces something to the step that uses it. The line style shows the basis above, not the kind."},{group:"edge",key:"back",label:"loop back",detail:"The return leg of a loop, marked with a chevron."},{group:"edge",key:"kinds",label:"kind",detail:"What a connection carries (data, call, state…) is written in its hover card and the Selection tab."}],Vp=[{group:"phase",key:"order",label:"phase colour",detail:"Each phase gets a colour by its place in the document, shown on the lane's left rule, the swatch before its number and name, and the card's left edge. It means nothing else. Problems are the only other colour on the diagram."}],Wp=[{group:"freshness",key:"verified",label:"Unchanged",detail:"The status bar counts the cited files that still match the hashes published with this revision. Unchanged means the quoted lines still exist; it does not prove the interpretation."},{group:"freshness",key:"draft",label:"Not checked",detail:"This revision has no published source hashes, so the status bar says freshness is not checked."},{group:"freshness",key:"stale",label:"Changed or missing",detail:"A cited file no longer matches the published revision. Cards, connections, findings and quotes that cite it carry this mark and a border in the warning colour, and their jumps are blocked. It does not say whether the claim is still right. When the notice above says the workspace root is the wrong folder, the mark means the file is unchanged in another folder."}];function Kp(){return[{id:"basis",title:"Claim basis",rows:Gp},{id:"severity",title:"Findings",rows:Z.map(t=>({group:"severity",key:t,label:ye[t],detail:t==="high"?"High potential impact if the finding is correct.":t==="medium"?"Medium potential impact if the finding is correct.":"Low potential impact if the finding is correct. Severity does not express certainty."})).concat([{group:"severity",key:"short",label:"numbers",detail:"Findings numbered in document order. A new revision can renumber them; the hover card, the Selection tab and Refine prompts use the real id."}])},{id:"edges",title:"Connections",rows:zp},{id:"phases",title:"Phases",rows:Vp},{id:"freshness",title:"Source freshness",rows:Wp}]}function Xl(t,e=!1){let n=R("svg",{class:"mlv-legend__swatch",viewBox:"0 0 44 14",width:44,height:14,"aria-hidden":"true"}),o=R("g",{class:"mlv-edge mlv-edge--data"+(e?" mlv-edge--back":"")});return o.setAttribute("data-basis",t),o.appendChild(R("path",{class:"mlv-legend__edge",d:"M2 7 H 33"})),o.appendChild(R("path",{class:"mlv-arrow",d:pt.d,transform:"translate(31,2)"})),e&&o.appendChild(R("path",{class:"mlv-edge__loopmark",d:"M22 3 L18 7 L22 11",fill:"none",stroke:"var(--mlv-edge)","stroke-width":1.3,"stroke-linecap":"round"})),n.appendChild(o),n}function qp(t){let e=f("span","mlv-legend__basis"),n=g(e,f("span","mlv-legend__card"));n.setAttribute("data-basis",t);let o=re(t);return o&&g(n,f("span","mlv-legend__tag",o)).setAttribute("data-basis",t),e.appendChild(Xl(t)),e}function Up(){let t=f("span","mlv-legend__phases");t.setAttribute("aria-hidden","true");for(let e=0;e{this.setOpen(!1),e(!1)}),n.appendChild(this.toggleBtn),this.body=g(this.root,f("div","mlv-legend__body"));for(let o of Kp()){let i=g(this.body,f("section","mlv-legend__section"));i.setAttribute("data-legend-section",o.id),g(i,f("h3","mlv-legend__heading",o.title));let s=g(i,f("dl","mlv-legend__list"));for(let r of o.rows){let l=g(s,f("dt","mlv-legend__term"));l.setAttribute("data-legend-row",r.group+":"+r.key);let a=Xp(r);l.appendChild(a);let d=r.group==="basis";!d&&(a.textContent||"").trim()!==r.label&&g(l,f("span","mlv-legend__label",r.label));let c=g(s,f("dd","mlv-legend__desc"));d&&g(c,f("strong","mlv-legend__label",r.label+". ")),c.appendChild(c.ownerDocument.createTextNode(r.detail))}}this.setOpen(!1)}get open(){return this.openState}setKinds(e,n){let o=this.root.querySelector('[data-legend-row="edge:kinds"]'),i=o&&o.nextElementSibling;if(!i)return;let s=i.querySelector(".mlv-legend__authored");s&&s.remove();let r=[];if(e.length){let l=e.slice(0,8).map(a=>a.kind+" "+a.count).join(", ");r.push("Connections in this diagram, by kind: "+l+(e.length>8?", and "+(e.length-8)+" more kinds":"")+".")}n>0&&r.push(n+(n===1?" connection has":" connections have")+" no kind."),r.length&&g(i,f("span","mlv-legend__authored"," "+r.join(" ")))}setOpen(e){this.openState=e,this.root.hidden=!e}toggle(){return this.setOpen(!this.openState),this.openState}};var Yl=[{keys:["Mod+F","/"],action:"focusSearch",description:"Search steps, findings, IDs, or cited text"},{keys:["n","p"],action:"cycleIssue",description:"Next / previous finding (document order)"},{keys:["Enter"],action:"open",description:"Open the cited source beside the diagram; focus stays here"},{keys:["Alt+Enter"],action:"openFocus",description:"Open the cited source and move focus to the editor"},{keys:["Space"],action:"collapse",description:"Collapse or expand the selected group"},{keys:["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"],action:"move",description:"Move the selection"},{keys:["0"],action:"fit",description:"Readable view: the whole diagram if it fits at reading size, otherwise phase 1"},{keys:["Shift+0"],action:"overview",description:"Overview: collapse every group and fit the whole diagram"},{keys:["+","="],action:"zoomIn",description:"Zoom in"},{keys:["-","_"],action:"zoomOut",description:"Zoom out"},{keys:["z"],action:"zoomToSelection",description:"Zoom to the selection"},{keys:["f"],action:"focusMode",description:"Focus mode: light the full lineage of the selection"},{keys:["l"],action:"legend",description:"Show or hide the legend"},{keys:["a"],action:"flow",description:"Turn the connection flow animation on or off"},{keys:["Shift+A"],action:"replayFlow",description:"Play the connection flow again (it stops after two passes)"},{keys:["e","Shift+E"],action:"cycleConnections",description:"Next / previous connection of the selected step"},{keys:["1","2","3"],action:"toggleSeverity",description:"Toggle the high / medium / low filters"},{keys:["b"],action:"toggleRail",description:"Show or hide the side panel; open or collapse the bottom panel"},{keys:["t"],action:"railTab",description:"Go to the panel tabs, About / Findings / Selection / Outline (opens the panel); ← and → pick a tab"},{keys:["?"],action:"shortcuts",description:"Show this shortcut sheet"},{keys:["Escape"],action:"escape",description:"Close the menu, this sheet, the Refine popover or the legend, collapse the bottom panel, exit focus mode, clear the selection, leave the canvas"},{keys:["Tab","Shift+Tab"],action:"browser",description:"Move focus out of the diagram (never intercepted)"}];function Zl(t,e){let n=t.key,o=t.ctrlKey||t.metaKey,i=()=>(t.preventDefault(),!0);return n==="Tab"?!1:o?n==="f"||n==="F"?(e.focusSearch(),i()):!1:t.altKey&&n==="Enter"&&!t.shiftKey?e.openSelection(!0)?i():!1:t.altKey?!1:n==="/"?(e.focusSearch(),i()):n==="?"?(e.toggleShortcuts(),i()):n==="Escape"?(e.escape(),i()):n==="n"||n==="N"||n==="p"||n==="P"?e.cycleIssue(n==="p"||n==="P")?i():!1:n==="+"||n==="="?(e.zoom(1),i()):n==="-"||n==="_"?(e.zoom(-1),i()):n===")"||n==="0"&&t.shiftKey?(e.overview(),i()):n==="0"?(e.fit(),i()):n==="1"||n==="2"||n==="3"?(e.toggleSeverity(Number(n)-1),i()):n==="f"||n==="F"?(e.toggleFocusMode(),i()):n==="l"||n==="L"?(e.toggleLegend(),i()):n==="a"||n==="A"?t.shiftKey?e.replayFlow()?i():!1:(e.toggleFlow(),i()):n==="e"||n==="E"?e.cycleConnections(t.shiftKey)?i():!1:n==="z"||n==="Z"?(e.zoomToSelection(),i()):n==="b"||n==="B"?(e.toggleRail(),i()):n==="t"||n==="T"?(e.focusRailTabs(),i()):n===" "?e.toggleCollapse()?i():!1:n==="Enter"?e.openSelection(!1)?i():!1:n.indexOf("Arrow")===0?(e.move(n),i()):!1}var Dn=class{constructor(e){this.opener=null;this.root=f("div","mlv-sheet"),this.root.hidden=!0;let n=g(this.root,f("div","mlv-sheet__scrim"));_(n,"click",()=>e());let o=g(this.root,f("div","mlv-sheet__panel"));this.panel=o,o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.setAttribute("aria-label","Keyboard shortcuts");let i=g(o,f("div","mlv-sheet__head"));g(i,f("h2","mlv-sheet__title","Keyboard shortcuts")),this.closeBtn=ne("mlv-btn mlv-btn--icon","Close shortcuts"),this.closeBtn.appendChild(D("close")),_(this.closeBtn,"click",()=>e()),i.appendChild(this.closeBtn),_(this.root,"keydown",a=>{if(Bt(this.panel,a)){a.stopPropagation();return}a.key==="Escape"&&(a.preventDefault(),a.stopPropagation(),e())});let s=si();g(o,f("p","mlv-sheet__note","Click a card, connection, finding or Outline row to select it and read its claim. Double-click a step, connection, finding or Outline step, or press Enter on it, to open the cited source beside the diagram; focus stays here. Double-click a group to collapse it. "+(s?"Option":"Alt")+"+click an Open link, or press "+De("Alt+Enter",s)+", to move focus to the editor."));let l=g(o,f("dl","mlv-sheet__list"));for(let a of Yl){let d=g(l,f("dt","mlv-sheet__keys"));for(let c of a.keys)g(d,f("kbd","mlv-kbd",De(c,s)));g(l,f("dd","mlv-sheet__desc",a.description))}}get open(){return!this.root.hidden}show(){if(!this.open){let e=this.root.ownerDocument,n=e?e.activeElement:null;this.opener=n&&!this.root.contains(n)?n:null}this.root.hidden=!1;try{this.closeBtn.focus()}catch{}}hide(e=null){let n=this.open;if(this.root.hidden=!0,!n)return;let o=this.opener;this.opener=null,at(o,e)}toggle(){return this.open?this.hide():this.show(),this.open}};function Ql(t,e){return(t.id||"mlv-search-results")+"-opt-"+e}var Yp=52;function Zp(t,e=Yp){return t.length<=e?t:"…"+t.slice(t.length-(e-1))}var yi=(t,e,n)=>t+" "+(t===1?e:n);function Qp(t){let e=[];return t.totalNodes&&e.push(yi(t.totalNodes,"step","steps")),t.totalIssues&&e.push(yi(t.totalIssues,"finding","findings")),yi(t.total,"match","matches")+(e.length?": "+e.join(", "):"")}function $l(t,e,n){let{query:o,result:i,cursor:s,onPick:r}=n,l=i.hits;if(H(t),!o){t.hidden=!0,e.setAttribute("aria-expanded","false"),e.removeAttribute("aria-activedescendant");return}if(t.hidden=!1,e.setAttribute("aria-expanded","true"),!l.length){g(t,f("li","mlv-result__empty","No matches")),e.removeAttribute("aria-activedescendant");return}let a=g(t,f("li","mlv-result__count",Qp(i)));if(a.setAttribute("role","presentation"),a.setAttribute("data-search-count",String(i.total)),l.forEach((d,c)=>{let u=g(t,f("li"));u.setAttribute("role","presentation");let h=f("button","mlv-result");if(h.type="button",h.id=Ql(t,c),h.setAttribute("role","option"),h.tabIndex=-1,h.setAttribute("aria-selected",c===s?"true":"false"),d.severity&&h.appendChild(U(d.severity,12,"")),g(h,f("span","mlv-result__label",d.label)),d.meta){let p=g(h,f("span","mlv-result__meta",Zp(d.meta)));p.textContent!==d.meta&&(p.title=d.meta)}if(d.location){let p=d.location.line===null?"first in file":"line "+d.location.line;g(h,f("span","mlv-result__jump",d.location.exact?"jump to "+p:"nearest to "+p)).setAttribute("data-search-jump",d.location.exact?"exact":"nearest"),u.setAttribute("data-search-pinned","1")}_(h,"click",()=>r(d)),u.appendChild(h)}),i.truncated){let d=g(t,f("li","mlv-result__foot"));d.setAttribute("role","presentation"),d.setAttribute("data-search-truncated",String(i.total)),g(d,f("span","","showing "+l.length+" of "+i.total));let c=W("mlv-link mlv-link--inline","Show more","Show more search results");c.setAttribute("data-search-more","1"),_(c,"click",u=>{u.preventDefault(),n.onShowMore()}),d.appendChild(c)}s>=0&&s|]+\.[A-Za-z][A-Za-z0-9_]{0,7}$/;function ta(t){let e=(t||"").trim();if(!e)return null;let n=/^(.+?):(\d+)(?::\d+)?$/.exec(e);if(n){let o=n[1].trim(),i=Number(n[2]);return!ea.test(o)||!isFinite(i)||i<1?null:{path:xi(o),line:i}}return ea.test(e)?{path:xi(e),line:null}:null}function xi(t){let e=t.split("\\").join("/");for(;e.indexOf("./")===0;)e=e.slice(2);return e}function $p(t,e){let n=xi(t);return n===e?!0:n.length>e.length&&n.slice(n.length-e.length-1)==="/"+e}function na(t,e){let n=t.graph.nodes||[],o=null;for(let i of n){if(!i.loc||!$p(i.loc.file,e.path))continue;let s=i.loc.line,r=Math.max(s,i.loc.endLine||s),l=r-s,a,d;e.line===null?(a=!1,d=s):(a=e.line>=s&&e.line<=r,d=a?0:e.linem.hit),p=em(t,e);if(p){let m=h.findIndex(v=>v.kind==="node"&&v.id===p.id);m>=0&&h.splice(m,1),h.unshift(p),h.length>n&&(h.length=n)}return{hits:h,total:i.length+s.length,totalNodes:i.length,totalIssues:s.length,limit:n,truncated:i.length+s.length>h.length}}function em(t,e){let n=ta(e);if(!n||n.line===null)return null;let o=na(t,n);if(!o)return null;let i=o.node;return{kind:"node",id:i.id,label:i.label||i.qualname,meta:Ke(i.loc),stage:i.stage,location:{path:n.path,line:n.line,exact:o.contains}}}function wi(t){t.sort((e,n)=>e.score-n.score||e.at-n.at)}var kt=40,Ei={hits:[],total:0,totalNodes:0,totalIssues:0,limit:kt,truncated:!1},jn=class{constructor(e,n,o){this.result=Ei;this.limit=kt;this.cursor=-1;this.input=e,this.results=n,this.host=o}run(e){this.limit=kt,this.execute(e),this.host.onQueryChanged(e)}execute(e){let n=this.host.index();this.result=n?ia(n,e,this.limit):Ei,this.cursor=this.result.hits.length?0:-1,this.render()}showMore(){this.limit+=kt,this.execute(this.input.value)}get hits(){return this.result.hits}clear(){this.input.value="",this.result=Ei,this.limit=kt,this.cursor=-1,this.render()}focus(){this.input.focus(),this.input.select()}hideResults(){this.results.hidden=!0}handleKey(e){if(e.key==="Escape"){this.clear(),this.host.onQueryChanged(""),this.host.blurToCanvas();return}let n=this.result.hits;if(n.length){if(e.key==="ArrowDown"||e.key==="ArrowUp")e.preventDefault(),this.cursor=Jl(this.cursor,n.length,e.key==="ArrowDown"?1:-1),this.render();else if(e.key==="Enter"){e.preventDefault();let o=n[Math.max(0,this.cursor)];o&&(this.host.activate(o),this.hideResults())}}}render(){$l(this.results,this.input,{query:this.input.value,result:this.result,cursor:this.cursor,onPick:e=>{this.host.activate(e),this.hideResults()},onShowMore:()=>this.showMore()})}};var tm=new Set(["invalid","parse","missing","unreadable","obsolete","same-id-changed"]),nm=new Set(["stale","root-hint"]),Gn=class{constructor(e){this.cb=e,this.root=f("div","mlv-hostnotice"),this.root.setAttribute("role","status"),this.root.hidden=!0,this.icon=g(this.root,f("span","mlv-hostnotice__icon")),this.icon.setAttribute("aria-hidden","true"),this.text=g(this.root,f("p","mlv-hostnotice__text")),this.actions=g(this.root,f("div","mlv-hostnotice__actions"))}update(e,n){let o=n||[],i=o.some(s=>tm.has(s))?"error":o.some(s=>nm.has(s))?"warn":"info";if(this.root.hidden=!e,this.root.className="mlv-hostnotice mlv-hostnotice--"+i,this.root.setAttribute("data-codes",o.join(" ")),this.text.textContent=e,H(this.icon),i!=="info"&&this.icon.appendChild(D("warning",14)),H(this.actions),this.actions.hidden=!0,e&&o.indexOf("root-hint")>=0){let s=W("mlv-btn mlv-btn--primary","Add folder to workspace");s.setAttribute("data-workspace-hint","add"),_(s,"click",()=>this.cb.onWorkspaceHint("add"));let r=W("mlv-btn","Open folder");r.setAttribute("data-workspace-hint","open"),r.title="Open the folder the citations resolve against in a new window",_(r,"click",()=>this.cb.onWorkspaceHint("open")),this.actions.append(s,r),this.actions.hidden=!1}}};var sa={bg:"--mlv-bg",surface:"--mlv-surface",surface2:"--mlv-surface-2",border:"--mlv-border",borderStrong:"--mlv-border-strong",text:"--mlv-text",text2:"--mlv-text-2",text3:"--mlv-text-3",link:"--mlv-link",accent:"--mlv-accent",edge:"--mlv-edge",sevHigh:"--mlv-sev-high",sevMedium:"--mlv-sev-medium",sevLow:"--mlv-sev-low",sevHighInk:"--mlv-sev-high-ink",sevMediumInk:"--mlv-sev-medium-ink",sevLowInk:"--mlv-sev-low-ink",stageUnknown:"--mlv-stage-unknown",nodeEdge:"--mlv-node-edge"},om=["--mlv-phase-0","--mlv-phase-1","--mlv-phase-2","--mlv-phase-3","--mlv-phase-4","--mlv-phase-5","--mlv-phase-6","--mlv-phase-7"],im={light:.64,dark:.54,hc:1},sm={light:.58,dark:.46,hc:1},ra={laneTint:"--mlv-lane-tint",groupTint:"--mlv-group-tint"},_i={bg:"#FBFBFD",surface:"#FFFFFF",surface2:"#F3F4F8",border:"#E3E5EB",borderStrong:"#C9CDD6",text:"#16181D",text2:"#5A6070",text3:"#676E81",link:"#2B57C4",accent:"#3B6CF6",edge:"#8C93A3",sevHigh:"#D0342C",sevMedium:"#E8A317",sevLow:"#2F5FD0",sevHighInk:"#FFFFFF",sevMediumInk:"#3A2500",sevLowInk:"#FFFFFF",stageUnknown:"#7A8090",nodeEdge:"",phases:["#00796B","#6A4FB3","#0A693C","#A0388F","#558B2F","#0B7F99","#546E7A","#1F6FB2"],laneTint:.025,groupTint:0},rm={bg:"#131417",surface:"#1A1C21",surface2:"#22252C",border:"#2C3038",borderStrong:"#3B414C",text:"#E6E8EE",text2:"#9AA1B1",text3:"#969DAD",link:"#8FB0FF",accent:"#6E96FF",edge:"#79808F",sevHigh:"#FF6169",sevMedium:"#F2B03C",sevLow:"#6E96FF",sevHighInk:"#1A0405",sevMediumInk:"#2A1A00",sevLowInk:"#0B1020",stageUnknown:"#8B93A7",phases:["#4DB6AC","#A48BE0","#4CA871","#D684C5","#9CCC65","#4FC3D9","#90A4AE","#6FA8DC"],laneTint:.035,groupTint:0},lm={phases:["#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF"],bg:"#000000",surface:"#000000",surface2:"#000000",border:"#FFFFFF",borderStrong:"#FFFFFF",text:"#FFFFFF",text2:"#FFFFFF",text3:"#FFFFFF",link:"#6BB7FF",edge:"#FFFFFF",laneTint:0,groupTint:0},la={light:zn({..._i},"light"),dark:zn({..._i,...rm},"dark"),hc:zn({..._i,...lm},"hc")};function am(t){let e=la[t]||la.light;return{...e,phases:e.phases.slice()}}function Mi(t,e){if(e===void 0||!t.phases.length)return t.stageUnknown;let n=t.phases.length;return t.phases[(e%n+n)%n]||t.stageUnknown}function zn(t,e){return e==="hc"?(t.edge=t.border,t.nodeEdge=t.border,t):(t.edge=Si(t.text,t.bg,im[e])||t.edge,t.nodeEdge=Si(t.text,t.bg,sm[e])||t.border,t)}function aa(t){let e=t.trim().toLowerCase(),n=/^#([0-9a-f]{3,8})$/.exec(e);if(n){let i=n[1];return i.length===3||i.length===4?[0,1,2].map(s=>parseInt(i[s]+i[s],16)):i.length===6||i.length===8?[0,2,4].map(s=>parseInt(i.slice(s,s+2),16)):null}let o=/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/.exec(e);return o?[Number(o[1]),Number(o[2]),Number(o[3])].map(i=>Math.max(0,Math.min(255,i))):null}function dm(t){return"#"+t.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("").toUpperCase()}function Si(t,e,n){let o=aa(t),i=aa(e);return!o||!i?"":dm(o.map((s,r)=>s*n+i[r]*(1-n)))}function cm(t){let e=/^color-mix\(\s*in srgb\s*,\s*(.+?)\s+([\d.]+)%\s*,\s*(.+?)\s*\)$/i.exec(t.trim());return e?Si(e[1],e[3],Number(e[2])/100):""}function Ve(t,e){return e==="high"?t.sevHigh:e==="medium"?t.sevMedium:e==="low"?t.sevLow:t.edge}function ca(t,e){return e==="high"?t.sevHighInk:e==="medium"?t.sevMediumInk:t.sevLowInk}function ua(t,e){let o={...am(e)},i=um(t);if(!t||!i||typeof i.getComputedStyle!="function")return o;let s;try{s=i.getComputedStyle(t)}catch{return o}if(!s||typeof s.getPropertyValue!="function")return o;let r=new Set;for(let a of Object.keys(sa)){let d=da(s.getPropertyValue(sa[a]));d&&(o[a]=d,r.add(a))}om.forEach((a,d)=>{let c=da(s.getPropertyValue(a));c&&(o.phases[d]=c)});let l=zn({...o},e);r.has("edge")||(o.edge=l.edge),r.has("nodeEdge")||(o.nodeEdge=l.nodeEdge);for(let a of Object.keys(ra)){let d=ha(s.getPropertyValue(ra[a])),c=d?Number(d):NaN;isFinite(c)&&c>=0&&c<=1&&(o[a]=c)}return o}function um(t){if(!t)return null;let e=t.ownerDocument;return e?e.defaultView:null}function da(t){let e=(t||"").trim();return e.toLowerCase().startsWith("color-mix(")?cm(e):ha(e)}function ha(t){let e=(t||"").trim();return!e||e.indexOf("var(")>=0||e.indexOf("color-mix(")>=0?"":e}var ki="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",At="ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",hm=.51,fm=.55,pm=.6;function te(t,e,n,o){return t?'"+I(t)+"":""}function Li(t,e,n,o,i,s){return''}function Q(t,e,n,o){let i=n?pm:o?fm:hm;return t.length*e*i}function Ce(t,e,n,o,i){if(Q(t,n,o,i)<=e)return t;let s=t.length;for(;s>1&&Q(t.slice(0,s)+"…",n,o,i)>e;)s--;return t.slice(0,Math.max(1,s))+"…"}function Vn(t,e,n,o,i,s){let r=$(t),l=o/18,a=Ve(i,r),d=s?'':'',c='';return''+d+c+""}function fa(t,e,n,o,i,s=[]){let r=Y(t);if(!r)return"";let l=J(t),a=s.length?Jn(s):l>1?String(l):"",d=26+(a?Q(a,10.5,!1,!0)+4:0),c=e-d,u=['',Vn(r,c+4,n+1,18,o,i)];return a&&u.push(te(a,c+24,n+14,{size:10.5,fill:o.text2,weight:650})),u.join("")}function Wn(t,e,n,o,i,s){let r=[];for(let c of Z)t[c]>0&&r.push({sev:c,n:t[c]});if(!r.length)return"";let l=0;for(let c of r)l+=o+2+Q(String(c.n),10.5,!1,!1)+6;let a=e-l,d=[];for(let c of r)d.push(Vn(c.sev,a,n-o/2,o,i,s)),a+=o+2,d.push(te(String(c.n),a,n+3.5,{size:10.5,fill:i.text2})),a+=Q(String(c.n),10.5,!1,!1)+6;return d.join("")}function pa(t){if(!t.length)return{x:0,y:0,w:0,h:0};let e=t[0].x,n=t[0].y,o=t[0].x,i=t[0].y;for(let s of t)s.xo&&(o=s.x),s.y>i&&(i=s.y);return{x:e,y:n,w:o-e,h:i-n}}function ma(t,e){return{x:t.x-e,y:t.y-e,w:t.w+e*2,h:t.h+e*2}}function It(t,e){return t.xe.x&&t.ye.y}function ga(t){return{x:Lt(t.x),y:Lt(t.y),w:Lt(Math.max(1,t.w)),h:Lt(Math.max(1,t.h))}}function Lt(t){return Math.round((isFinite(t)?t:0)*100)/100}function k(t){return String(Lt(t))}function I(t){let e="",n=String(t??"");for(let o=0;o=55296&&s<=56319){let r=o+1=56320&&r<=57343?(e+=i+n.charAt(o+1),o++):e+="�";continue}if(s>=56320&&s<=57343||s===65534||s===65535){e+="�";continue}i==="&"?e+="&":i==="<"?e+="<":i===">"?e+=">":i==='"'?e+=""":i==="'"?e+="'":i<" "&&i!==` -`&&i!==" "?e+=" ":e+=i}return e}var mm=10,Ri=4,Ma=8,gm=12,vm=10,Oi=26,bm=8,va=Ri+Ma+Oi+bm,ba=13,rt=11,Ai=10.5,ya=10.5,Ct=13,ym=.05,st=10.5,Ii=12,xm=10,wm=22.5,xa=38.5,Em=52.5,_m=22,Sm=15,Mm=98,Ci=14,wa=3,Ea=6,Hi=14,km=10,_a=10,Lm="6 4",Am="1.5 3.5",Im=12,Ti=34;function ka(t){let{plan:e,palette:n,theme:o}=t,i=o==="hc",s=ga(t.region),r=[],l=[],a=[],d=[],c=[],u=[],h=0,p=w=>{u.indexOf(w)<0&&u.push(w)};r.push('');let m=[];for(let w of e.lanes){let E=w.lane,b={x:E.x,y:E.y,w:E.w,h:E.h};if(!It(b,s))continue;c.push(E.id);let M=Mi(n,w.phase);p(M),m.push(Cm(E,w.counts,M,w.phase,n,i))}let v=[];for(let w of e.nodes){let E=w.visual.box,b={x:E.x-8,y:E.y-8,w:E.w+16,h:E.h+16};if(!It(b,s))continue;l.push(w.visual.node.id);let M=Mi(n,w.visual.phase);p(M);let T=w.expandedGroup?Hm(w.visual,n,i):Tm(w.visual,M,n,i);w.expandedGroup?m.push(T):v.push(T)}let y=[];for(let w of e.edges){let E=w.route;if(!It(ma(pa(E.points),16),s))continue;a.push(E.id);for(let M of E.ids)d.push(M);let b=Nm(w,n,i);y.push(b.markup),b.labelled&&h++}r.push(''+m.join("")+""),r.push(''+y.join("")+""),r.push(''+v.join("")+"");let S='',x=""+I(t.title)+""+(t.desc?""+I(t.desc)+"":"");return{svg:` -`+S+x+r.join("")+` -`,width:s.w,height:s.h,region:s,nodeIds:l,edgeIds:a,edgeDocIds:d,laneIds:c,labels:h,stageColors:u}}function Cm(t,e,n,o,i,s){let r=[''];r.push(''),r.push(''),r.push(''),r.push('');let l=t.y+(t.headerH-8)/2;r.push('');let a=t.y+t.headerH/2+4.5,d=String(o+1),c=t.x+26;r.push(te(d,c,a,{size:Ct,fill:i.text2,weight:650}));let u=c+Q(d,Ct,!1,!0)+6,h=J(e),p=h>0?"· "+xt(h):"",m=p?Q(p,st,!1,!1):0,v=Pe(t.nodeCount),y=Q(v,st,!1,!1),S=Math.max(40,t.x+t.w-12-u-y-10-(p?m+96:0)),x=Ce(t.label||t.id,S,Ct,!1,!0);r.push(te(x,u,a,{size:Ct,fill:i.text,weight:650}));let w=u+Q(x,Ct,!1,!0)+10;if(r.push(te(v,w,a,{size:st,fill:i.text3})),p){let E=t.x+t.w-12;r.push(te(p,E-m,a,{size:st,fill:i.text3})),r.push(Wn(e,E-m-6,t.y+t.headerH/2,13,i,s))}return r.push(""),r.join("")}function Tm(t,e,n,o){let i=t.node,s=t.box,r=s.collapsed,l=Y(t.counts),a=Ni(i.basis),c=[""];c.push(""+I(yt(t))+"");let u=t.stale?n.sevMedium:l?Ve(n,l):a?n.text2:n.nodeEdge;c.push('"),c.push('');let h=s.x+Ri+Ma,p=s.y+vm;c.push(''),c.push(Li(fn(tt(i,r)),h+5,p+5,16/16,n.text2,1.3));let m=s.x+va,v=Math.max(24,s.w-va-gm),y=i.label||i.qualname||i.id,S=Po(y,s.w);S.forEach((E,b)=>{c.push(te(Ce(E,v,ba,!1,!0),m,s.y+wm+b*18,{size:ba,fill:n.text,weight:650}))});let x=(S.length-1)*18;if(!r&&$o(i)===2)Om(mn(i),v,2).forEach((E,b)=>{c.push(te(E,m,s.y+xa+x+b*Rm,{size:rt,fill:n.text2}))});else{let E=Ce(mn(i),v,rt,!1,!1);c.push(te(E,m,s.y+xa+x,{size:rt,fill:n.text2}));let b=lt(i.loc),M=Q(b.tail,Ai,!0,!1),T=Ce(b.head,Math.max(12,v-M),Ai,!0,!1)+b.tail;c.push(te(T,m,s.y+Em+x,{size:Ai,fill:n.text3,mono:!0}))}let w=r?[Pe(t.descendants)]:[];if(w.length&&s.h-x>=Mm){let E=s.y+s.h-_m,b=m;for(let M of w){let T=Q(M,ya,!1,!1)+12;if(b+T>m+v)break;c.push(''),c.push(te(M,b+6,E+11,{size:ya,fill:n.text2})),b+=T+4}}return l&&!r?c.push(fa(t.counts,s.x+s.w+6,s.y-6,n,o,t.findings||[])):r&&J(t.counts)>0&&c.push(Wn(t.counts,s.x+s.w+6,s.y+4,14,n,o)),c.push(La(i.basis,s.x+km,s.y+s.h-Hi/2,n)),c.push(""),c.join("")}var Rm=14.85;function Om(t,e,n){let o=t.replace(/\s+/g," ").trim().split(" ").filter(Boolean),i=[],s="";for(let r=0;r'+te(i,e+4,n+3.5,{size:_a,fill:o.text})+""}function Hm(t,e,n){let o=t.node,i=t.box,s=Y(t.counts),r=Ni(o.basis),l=['"];l.push(""+I(yt(t))+""),l.push('");let a=i.y+(Ti-14)/2;l.push(Li(fn(tt(o,!1)),i.x+8,a,14/16,e.text2,1.3));let d=i.x+28,c=i.y+Ti/2+4,u=Math.max(40,i.w-(s?170:96)),h=Ce(o.label||o.qualname,u,Ii,!1,!0);l.push(te(h,d,c,{size:Ii,fill:e.text,weight:650}));let p=Pe(t.descendants),m=Q(p,st,!1,!1)+12,v=d+Q(h,Ii,!1,!0)+8;return l.push(''),l.push(te(p,v+6,c,{size:st,fill:e.text2})),l.push(La(o.basis,v+m+6,c-3.5,e)),s&&l.push(Wn(t.counts,i.x+i.w-10,i.y+Ti/2,13,e,n)),l.push(""),l.join("")}function Nm(t,e,n){let o=t.route,i=t.severity?Ve(e,t.severity):e.edge,s=Ni(t.basis),r=t.weight&&t.weight>1?Math.floor(t.weight):1,l=r>=2,a=l?Math.max(Sa,jt(r)):Sa,d=['"];d.push('"),d.push(Fm(o.points,i));let c=!1,u=t.placement,h=!u||!u.hidden&&u.always;if(o.label&&h){let m=u?u.x:o.mid.x,v=u?u.y:o.mid.y-8,y=u?u.text:o.label;d.push(''+I(y)+""),c=!0}let p=u?u.marker:o.mid;if(t.severity){let m=$(t.severity);d.push(''),d.push(Vn(m,p.x-7,p.y-7,14,e,n))}else o.back&&d.push('');return l&&d.push(Pm(r,p,o.midAngle,!!t.severity||o.back,e)),d.push(""),{markup:d.join(""),labelled:c}}function Pm(t,e,n,o,i){let s=ht(t),r=zt(t),l=Gt(e,n,o);return''+I(s)+""}var Sa=1.5;function Fm(t,e){let n=pt;if(t.length<2)return"";let o=t[t.length-1],i=t[t.length-2];for(let a=t.length-2;a>=0;a--)if(t[a].x!==o.x||t[a].y!==o.y){i=t[a];break}let s=Math.atan2(o.y-i.y,o.x-i.x)*180/Math.PI,r="translate("+k(o.x)+","+k(o.y)+") rotate("+k(s)+") translate(-7.65,-4.5) scale(0.9)",l='fill="'+I(e)+'"';return'"}var Bm="image/svg+xml",Dm="image/png";function jm(t,e,n){let o=n,i=Math.max(t,e);i>0&&i*o>16384&&(o=16384/i);let s=t*e;return s>0&&s*o*o>268435456&&(o=Math.sqrt(268435456/s)),o>=1?o:null}function Gm(t){return"data:"+Bm+";charset=utf-8,"+encodeURIComponent(t)}function Aa(t,e,n,o=2){return new Promise(i=>{if(typeof document>"u"||typeof Image>"u"){i(null);return}let s=jm(e,n,o);if(s===null){i(null);return}let r=s,l,a;try{l=document.createElement("canvas"),l.width=Math.max(1,Math.round(e*r)),l.height=Math.max(1,Math.round(n*r)),a=typeof l.getContext=="function"?l.getContext("2d"):null}catch{i(null);return}if(!a||typeof l.toDataURL!="function"){i(null);return}let d=new Image,c=!1,u=h=>{c||(c=!0,i(h))};d.onload=()=>{try{a.setTransform(r,0,0,r,0,0),a.drawImage(d,0,0,e,n);let h=l.toDataURL(Dm),p=h.indexOf(","),m=p<0?"":h.slice(p+1);u(h==="data:,"||!m?null:{base64:m,width:l.width,height:l.height,scale:r})}catch{u(null)}},d.onerror=()=>u(null);try{d.src=Gm(t)}catch{u(null)}})}var Kn="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function zm(t){let e=[];for(let n=0;n=55296&&o<=56319){let i=n+1=56320&&i<=57343?(o=65536+(o-55296<<10)+(i-56320),n++):o=65533}else o>=56320&&o<=57343&&(o=65533);o<128?e.push(o):o<2048?e.push(192|o>>6,128|o&63):o<65536?e.push(224|o>>12,128|o>>6&63,128|o&63):e.push(240|o>>18,128|o>>12&63,128|o>>6&63,128|o&63)}return new Uint8Array(e)}function Vm(t){let e=typeof globalThis>"u"?{}:globalThis;if(typeof e.btoa=="function"){let o="";for(let i=0;i>2)+Kn.charAt((i&3)<<4|s>>4),n+=o+1>6):"=",n+=o+2!!o).join("-")+"."+e}function Ta(t){return String(t.workspace.root||"")||"workflow"}function qm(t){return String(t).toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)}function Ra(t,e,n){return{v:1,type:"exportFile",kind:t,name:e,base64:n,data:n,suggestedName:e,scope:"all"}}function Oa(t,e,n){return o=>{if(o.outcome==="done"){t.announce(e(o.name||n));return}if(o.outcome==="cancelled"){t.announce("Export cancelled.");return}let i="Export failed: "+(o.message||"the host did not say why");t.toast(i),t.announce(i)}}function Ha(t,e,n){let o=Ia(e.svg);if(!o){t.toast("This browser could not encode the SVG.");return}let i=e.nodeIds.length,s=e.edgeIds.length;t.announce("Saving SVG…"),t.request(Ra("svg",n,o),Oa(t,r=>"Exported "+i+" cards and "+s+" connections as "+r+".",n))}async function Na(t,e,n){let o=await Um(e);if(!o)return t.toast("Could not draw the PNG here — save the SVG instead."),t.announce("PNG export is not available in this host."),!1;let i=o.width+"×"+o.height;return t.announce("Saving PNG…"),t.request(Ra("png",n,o.base64),Oa(t,s=>"Exported "+i+" PNG as "+s+".",n)),!0}async function Pa(t,e){if(await Xm(e.svg)){let s="SVG copied — "+e.nodeIds.length+" cards, "+e.edgeIds.length+" connections.";return t.toast(s),t.announce(s),!0}let o=e.nodeIds.length,i=e.edgeIds.length;return t.request({v:1,type:"copy",text:e.svg},s=>{if(s.outcome==="done"){let r="SVG copied — "+o+" cards, "+i+" connections.";t.toast(r),t.announce(r)}else t.announce("The SVG could not be copied.")}),!1}async function Um(t){try{return await Aa(t.svg,t.width,t.height,Wm)}catch{return null}}async function Xm(t){let e=typeof navigator<"u"?navigator:null;if(!e||!e.clipboard||typeof e.clipboard.writeText!="function")return!1;try{return await e.clipboard.writeText(t),!0}catch{return!1}}function Ym(t){let e=t.view.scenePlan();return!e||!t.graph?null:{plan:e,palette:ua(t.root,t.themes.kind),theme:t.themes.kind,graph:t.graph,generatedAt:new Date().toISOString().slice(0,10)}}function Fa(t,e){let n={post:s=>t.bridge.post(s),request:(s,r)=>{t.postRequest(s,r)},toast:s=>t.view.toast(s),announce:s=>t.announce(s)},o=Ym(t);if(!o){t.view.toast("Nothing is drawn yet — there is nothing to export.");return}let i=Ca(o);e==="svg"?Ha(n,i,Pi(o,"svg")):e==="png"?Na(n,i,Pi(o,"png")):e==="copy-svg"&&Pa(n,i)}function Zm(t){let e={steps:0,connections:0,findings:0};if(!t.graph)return e;let n=o=>!!o&&o!=="observed";for(let o of t.graph.nodes)n(o.basis)&&e.steps++;for(let o of t.graph.edges)n(o.basis)&&e.connections++;for(let o of t.graph.issues)n(o.basis)&&e.findings++;return e}function Qm(t){let e=pe();if(!t.graph)return e;for(let n of t.graph.issues)e[$(n.severity)]++;return e}function q(t){let e=Zm(t);t.exceptionsOn&&e.steps+e.connections+e.findings===0&&(t.exceptionsOn=!1,t.view.canvasEl.removeAttribute("data-exceptions")),t.chrome.update({graph:t.graph,document:t.graph?t.workflowDocument:null,flowOn:t.flowOn,legendOpen:t.legendOpen,hasSelection:!!t.selection,filters:t.filters.value,visibleCounts:Qm(t),minimapCollapsed:t.view.minimapCollapsed,minimapUnavailable:t.view.minimapUnavailable(),railOpen:t.railOpen,freshness:Ui(t.workflowDocument,t.freshness),checking:t.freshness.checking,notObserved:e,exceptionsOn:t.exceptionsOn,railMode:t.railMode})}function X(t){let e=t.selection,n=t.selectedNodeId(),o=n&&t.index&&t.index.nodeById.get(n)||null,i=e&&e.kind==="edge"&&t.index&&t.index.edgeById.get(e.id)||null,s=e&&e.kind==="issue"&&t.index&&t.index.issueById.get(e.id)||null;t.paneColumns=t.selectionColumns(),t.rail.update({index:t.index,tab:t.railTab,issues:t.graph?t.graph.issues:[],selectedNode:o,selectedEdge:i,selectedIssueId:e&&e.kind==="issue"?e.id:null,selectedIssue:s,collapsed:t.view.collapsed,keep:t.filters.keep,staleReason:r=>t.freshness.reasonOf(r),document:t.graph?t.workflowDocument:null,columns:t.paneColumns})}function Ba(t,e,n,o=!1){t.graph=e;let i=new Dt(e);t.index=i,n&&n.collapsed?t.collapsedState=n.collapsed.slice():!t.collapsedState.length&&t.view.collapsed.size===0&&(t.collapsedState=i.defaultCollapsed());let s=new Set(e.nodes.map(l=>l.id));t.view.setIndex(i),t.view.flow.clear(),t.view.setCollapsed(t.collapsedState.filter(l=>s.has(l)&&i.isGroup(l))),n&&n.selection!==void 0&&(t.selection=n.selection),t.selection&&t.selection.kind==="node"&&!s.has(t.selection.id)&&(t.selection=null),t.selection&&t.selection.kind==="edge"&&!i.edgeById.has(t.selection.id)&&(t.selection=null),t.selection&&t.selection.kind==="issue"&&!i.issueById.has(t.selection.id)&&(t.selection=null),t.view.relayout();let r=n&&n.viewport?n.viewport:null;if(r?t.view.viewport.set(r):t.view.fit(),q(t),X(t),t.view.applySelection(t.selection),o){let l=(d,c,u)=>d+" "+(d===1?c:u),a=e.stats.issues.high;t.announce("Workflow loaded: "+l(e.nodes.length,"step","steps")+", "+l(e.edges.length,"connection","connections")+", "+l(e.issues.length,"finding","findings")+(a?" ("+a+" high severity)":"")+".")}t.saveSoon()}function qn(t){if(!t.graph)return;let e=new Set(t.graph.nodes.map(o=>o.id)),n=t.collapsedState.filter(o=>!e.has(o));t.collapsedState=n.concat(Array.from(t.view.collapsed)).sort()}function ja(t){let e=_l(t.root,t.themes.kind);t.releasePage=El(t.root),t.liveEl=e.live,t.view=new Cn(e,$m(t)),t.chrome=new On({onQuery:n=>t.search.run(n),onSearchKey:n=>t.search.handleKey(n),onSearchOpen:()=>t.focusSearch(),onSeverity:n=>t.applyFilters(()=>t.filters.toggleSeverity(n)),onToggleExceptions:n=>t.setExceptions(n),onAbout:n=>t.showAbout({focus:n}),onZoom:n=>t.view.zoomStep(n),onToggleLegend:n=>t.setLegend(n),onToggleFlow:n=>t.setFlow(n),onToggleMinimap:n=>t.setMinimapCollapsed(n),onToggleRail:()=>t.toggleRail(),onFitWhole:()=>t.view.fitWhole(),onZoomToSelection:()=>t.zoomToSelection(),onExport:n=>Fa(t,n),onShortcuts:()=>t.toggleShortcuts(!0),onMenuOpen:()=>q(t)},e.zoomBar),t.root.appendChild(t.chrome.header),t.notice=new Gn({onWorkspaceHint:n=>t.bridge.post({v:1,type:"workspaceHint",action:n})}),t.root.appendChild(t.notice.root),t.root.appendChild(e.body),e.body.appendChild(e.main),t.search=new jn(t.chrome.searchInput,t.chrome.results,{index:()=>t.index,activate:n=>t.activateHit(n),onQueryChanged:n=>{t.filters.patch({query:n}),t.saveSoon()},blurToCanvas:()=>t.view.canvasEl.focus()}),t.rail=new Fn({onTab:n=>t.setRailTab(n),onClearFilters:()=>t.clearFilters(),onSelectIssue:(n,o)=>{t.focusIssue(n,{fromList:"issues"}),t.doubleClick.arm(o,()=>t.openIssue(n,!1))},onOpenIssue:(n,o)=>t.openIssue(n,o),onSelectNode:(n,o)=>{t.select({kind:"node",id:n},{center:!0,reveal:!0,fromList:"outline"}),t.doubleClick.arm(o,()=>t.select({kind:"node",id:n},{center:!0,reveal:!0,open:!0,fromList:"outline"}))},onOpenNode:(n,o)=>t.select({kind:"node",id:n},{center:!0,reveal:!0,open:!0,focusEditor:o,fromList:"outline"}),onSelectEdge:n=>t.select({kind:"edge",id:n},{reveal:!0,fromList:"outline"}),onShowNode:n=>t.focusNode(n,{center:!0,pulse:!0}),onShowEdge:n=>t.select({kind:"edge",id:n},{reveal:!0}),onShowIssue:n=>t.focusIssue(n),onChallenge:()=>Da(t,"challenge"),onRefine:()=>Da(t,null),onOpen:(n,o)=>t.openLocation(n,o),onResize:n=>t.setRailWidth(n),onSelectLane:n=>t.selectLane(n),onToggleCollapse:n=>{t.index&&t.index.isGroup(n)&&t.view.toggleCollapse(n)},onShowLimitations:()=>t.showAbout({at:"limitations",focus:!0}),onSheetToggle:()=>t.toggleRail(),onSheetCollapse:()=>t.collapseSheet(!0),onSheetResize:n=>t.setSheetFraction(n),sheetFraction:()=>t.sheetFraction,sheetFractionMax:()=>t.sheetFractionMax(),bodyHeight:()=>e.body.getBoundingClientRect().height}),e.body.appendChild(t.rail.root),_(t.rail.root,"pointerdown",()=>{t.railChosen=!0}),_(t.rail.root,"keydown",()=>{t.railChosen=!0}),t.legend=new Bn(n=>t.setLegend(n)),e.canvas.appendChild(t.legend.root),t.sheet=new Dn(()=>t.toggleShortcuts(!1)),t.root.appendChild(t.sheet.root),t.root.appendChild(t.chrome.more.panel),t.root.appendChild(t.chrome.status),t.root.appendChild(t.liveEl),t.setRailWidth(t.railWidth),t.setRailOpen(t.railOpen)}function $m(t){let e=(o,i)=>t.select({kind:"node",id:o},{showClaim:!0,open:!0,focusEditor:i}),n=(o,i)=>t.select({kind:"edge",id:o},{showClaim:!0,open:!0,focusEditor:i});return{keep:t.filters.keep,activateNode:(o,i)=>{t.select({kind:"node",id:o},{showClaim:!0}),t.doubleClick.arm(i,()=>e(o,!1))},activateEdge:(o,i)=>{t.select({kind:"edge",id:o},{showClaim:!0}),t.doubleClick.arm(i,()=>n(o,!1))},openNode:e,openEdge:n,clearFilters:()=>t.clearFilters(),announce:o=>t.announce(o),afterCollapse:()=>{qn(t),t.view.applySelection(t.selection),X(t),t.saveSoon()},onViewportChange:o=>{t.viewportState={x:o.x,y:o.y,zoom:o.zoom},t.saveSoon()},onMinimapCollapsed:()=>{q(t),t.saveSoon()},onKeyDown:o=>t.onKeyDown(o),onBackgroundClick:()=>t.clearSelection(),keptTarget:()=>{let o=t.selection;if(!o)return null;if(o.kind!=="issue")return{kind:o.kind,id:o.id};let i=t.index?t.index.issueById.get(o.id):void 0;return i?i.nodeIds[0]?{kind:"node",id:i.nodeIds[0]}:i.edgeIds.length?{kind:"edge",id:i.edgeIds[0]}:null:null}}}function Da(t,e){let n=t.root.querySelector(".mlv-workflow__refine"),o=t.root.querySelector(".mlv-workflow__composer"),i=t.root.querySelector(".mlv-workflow__intent");!n||!o||!i||(o.hidden||n.click(),n.click(),e&&(i.value=e,i.dispatchEvent(new Event("change",{bubbles:!0}))))}function Ga(t,e,n=!1){if(!t.caps.canOpenSource)return;let o=e.evidenceId?t.freshness.reasonOf(e.file):void 0;if(o){let r=e.file+": "+me[o]+". Not opened"+(o==="elsewhere"?".":"; the cited lines may no longer be there.");t.view.toast(r),t.announce(r);return}t.bridge.saveState(t.getState());let i={v:1,type:"openLocation",file:e.file,absFile:e.absFile,line:e.line,col:e.col,endLine:e.endLine,endCol:e.endCol,preview:!0};e.evidenceId&&(i.evidenceId=e.evidenceId),e.evidenceId&&e.cell!==void 0&&(i.cell=e.cell),n&&(i.focus=!0),t.bridge.post(i);let s=de(e);t.announce(n?"Opening "+s+" in the editor.":"Opening "+s+" beside the diagram. Focus stays here; Alt+Enter moves it to the editor."),!n&&!t.openHintShown&&(t.openHintShown=!0,t.view.toast("Opening the cited lines beside the diagram. Focus stays here; Alt+Enter moves it to the editor."))}var Jm=["about","issues","inspector","outline"];function Un(t){if(!t||typeof t!="object")return null;let{kind:e,id:n}=t;return e!=="node"&&e!=="edge"&&e!=="issue"||typeof n!="string"||!n?null:{kind:e,id:n}}function Xn(t){return typeof t=="string"&&Jm.indexOf(t)>=0?t:null}function za(t){return{focusSearch:()=>t.focusSearch(),escape:()=>t.dismissTopmost(),cycleIssue:e=>{let n=t.visibleIssues();if(!n.length)return!1;let o=t.selectedIssueId(),s=((o?n.findIndex(r=>r.id===o):-1)+(e?-1:1)+n.length)%n.length;return t.focusIssue(n[s].id),!0},zoom:e=>t.zoom(e),fit:()=>t.fit(),toggleFocusMode:()=>t.toggleFocusMode(),zoomToSelection:()=>t.zoomToSelection(),overview:()=>t.overview(),toggleLegend:()=>t.toggleLegend(),toggleFlow:()=>t.toggleFlow(),replayFlow:()=>t.replayFlow(),toggleCollapse:()=>t.collapseSelection(),openSelection:e=>t.openSelection(e),move:e=>t.move(e),toggleSeverity:e=>{let n=Z[e];n&&t.toggleSeverity(n)},toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:()=>t.toggleShortcuts(),cycleConnections:e=>t.cycleConnections(e)}}function Va(t){return{focusSearch:()=>t.focusSearch(),dismissTopmost:()=>{if(t.sheetOpen())t.toggleShortcuts(!1);else{if(t.closeHeaderPanels())return;if(t.legendOpen())t.closeLegend();else{if(t.collapseSheet())return;t.view().isFocusLocked?t.view().toggleFocusMode(t.selection()):t.selection()?(t.view().blurFocusedEdge(),t.clearSelection()):t.view().canvasEl.blur()}}},visibleIssues:()=>t.visibleIssues(),selectedIssueId:()=>{let e=t.selection();return e&&e.kind==="issue"?e.id:null},focusIssue:e=>t.focusIssue(e),zoom:e=>t.view().zoomStep(e),fit:()=>t.view().fit(),toggleFocusMode:()=>t.view().toggleFocusMode(t.selection()),zoomToSelection:()=>t.zoomToSelection(),overview:()=>t.view().overview(),toggleLegend:()=>t.toggleLegend(),toggleFlow:()=>t.toggleFlow(),replayFlow:()=>t.view().flow.replay()?(t.announce("Playing the connection flow again."),!0):!1,collapseSelection:()=>{let e=t.selection(),n=t.index();if(!e||e.kind!=="node"||!n)return!1;let o=n.isGroup(e.id)?e.id:n.parentOf.get(e.id)||null;return!o||!n.isGroup(o)?!1:(t.view().toggleCollapse(o),!0)},openSelection:e=>t.openSelection(e),move:e=>t.move(e),toggleSeverity:e=>t.toggleSeverity(e),toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:()=>t.toggleShortcuts(),cycleConnections:e=>eg(t,e)}}function eg(t,e){let n=t.selection(),o=null;if(n&&n.kind==="node"?o=n.id:n&&n.kind==="edge"&&(o=t.edgeAnchor()),!o)return!1;let i=t.view().incidentRoutes(o);if(!i.length)return t.view().toast("That node has no connections in this view."),!0;let s=n&&n.kind==="edge"?n.id:null,r=-1;for(let a=0;at.view,index:()=>t.index,selection:()=>t.selection,edgeAnchor:()=>t.edgeAnchor,setEdgeAnchor:e=>{t.edgeAnchor=e},select:e=>t.select(e),clearSelection:()=>t.clearSelection(),focusSearch:()=>t.focusSearch(),visibleIssues:()=>ng(t),focusIssue:e=>t.focusIssue(e,{fromList:"issues"}),openSelection:e=>{let n=t.selection;if(!n)return!1;let o=t.locOf(n);return o&&t.openLocation(o,e),!0},zoomToSelection:()=>t.zoomToSelection(),move:e=>og(t,e),toggleSeverity:e=>t.applyFilters(()=>t.filters.toggleSeverity(e)),toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:e=>t.toggleShortcuts(e),sheetOpen:()=>t.sheet.open,closeHeaderPanels:()=>t.closeHeaderPanels(),collapseSheet:()=>t.collapseSheet(!1),announce:e=>t.announce(e),toggleLegend:()=>t.setLegend(!t.legendOpen),legendOpen:()=>t.legendOpen,closeLegend:()=>t.setLegend(!1),toggleFlow:()=>t.setFlow(!t.flowOn)})}function ng(t){return t.graph?t.graph.issues.filter(t.filters.keep):[]}function og(t,e){let n=t.selection,o=t.view.nextSelection(n&&n.kind==="node"?n.id:null,e);if(!o)return;t.select({kind:"node",id:o.id},{center:!o.visible});let i=t.view.nodeElement(o.id);i&&i.focus()}function Yn(t,e,n){if(!e||typeof e!="object")return;e.filters&&t.filters.restore(e.filters);let o=Xn(e.railTab);if(o&&(t.railTab=o),Array.isArray(e.collapsed)){t.collapsedState=e.collapsed.slice();let s=t.index;s&&t.view.setCollapsed(e.collapsed.filter(r=>s.isGroup(r)))}let i=Un(e.selection);n&&i&&(t.selection=i),typeof e.minimapCollapsed=="boolean"&&t.view.setMinimapCollapsed(e.minimapCollapsed),typeof e.flow=="boolean"&&t.setFlow(e.flow),typeof e.legendOpen=="boolean"&&t.setLegend(e.legendOpen),n&&t.index&&(t.view.relayout(),q(t),X(t),t.view.applySelection(t.selection)),e.viewport&&t.view.viewport.set(e.viewport)}function Ka(t){qn(t);let e={viewport:{...t.viewportState},selection:t.selection?{...t.selection}:null,collapsed:t.collapsedState.slice(),filters:t.filters.snapshot(),railTab:t.railTab,minimapCollapsed:t.view.minimapCollapsed};if(t.flowOn||(e.flow=!1),t.legendOpen&&(e.legendOpen=!0),t.graph&&t.workflowRevision){e.workflowRevision=t.workflowRevision;let n=xs(t.root);n&&(e.composer=n),t.railMode==="sheet"&&t.railOpen&&(e.sheetOpen=!0)}return e}function qa(t){try{return t.loadState()}catch{return null}}function Ua(t,e){Xi(e,{init:(n,o)=>{t.caps=o||t.caps,t.setTheme(n),q(t),X(t)},workflow:n=>{n!==t.workflowDocument&&t.setWorkflow(n)},workflowStatus:n=>Ss(t,n),hostNotice:(n,o)=>t.showHostNotice(n,o),stale:n=>t.setStale(n),actionResult:n=>t.onActionResult(n),theme:n=>t.setTheme(n),revealNode:(n,o)=>t.focusNode(n,{center:o,pulse:!0}),revealIssue:n=>t.focusIssue(n),restoreState:n=>Yn(t,n,!0),onUnknown:n=>t.bridge.post({v:1,type:"log",level:"debug",message:"ignored unknown message type: "+n})})}var ig=32,Fi=900,sg=620,rg=.47,Xa=.25,Ya=.75,Zn=class{constructor(e,n){this.graph=null;this.index=null;this.edgeAnchor=null;this.flowOn=!0;this.exceptionsOn=!1;this.filters=new Pt;this.viewportState={x:0,y:0,zoom:1};this.selection=null;this.collapsedState=[];this.railTab="about";this.legendOpen=!1;this.freshness=new Nt;this.openHintShown=!1;this.railOpen=!0;this.railChosen=!1;this.dockedHidden=!1;this.railWidth=360;this.railMode="docked";this.sheetFraction=rg;this.paneColumns=1;this.workflowDocument=null;this.workflowRevision=null;this.restoredView=null;this.restoredComposer=null;this.restoredTab=null;this.restoredSelection=null;this.restoredSheet=null;this.pending=new Map;this.requestSerial=0;this.disposers=[];this.destroyed=!1;this.releasePage=()=>{};this.saveSoon=Di(()=>this.bridge.saveState(this.getState()),250);this.root=e,this.bridge=n,this.caps=n.capabilities,this.themes=new Ft(e,n.theme||"light"),this.doubleClick=new Qt(e),this.disposers.push(()=>this.doubleClick.dispose()),ja(this);let o=qa(n);o&&Yn(this,o,!1),o&&typeof o.workflowRevision=="string"&&o.viewport&&(this.restoredView={revision:o.workflowRevision,viewport:{...o.viewport}});let i=o&&typeof o.workflowRevision=="string"?Es(o.composer):null;o&&i&&(this.restoredComposer={revision:o.workflowRevision,composer:i});let s=o&&typeof o.workflowRevision=="string"?Xn(o.railTab):null;o&&s&&(this.restoredTab={revision:o.workflowRevision,tab:s});let r=o&&typeof o.workflowRevision=="string"?Un(o.selection):null;o&&r&&(this.restoredSelection={revision:o.workflowRevision,selection:r}),o&&typeof o.workflowRevision=="string"&&o.sheetOpen===!0&&(this.restoredSheet=o.workflowRevision),this.disposers.push(n.onMessage(l=>this.onMessage(l))),typeof window<"u"&&this.disposers.push(_(window,"resize",()=>this.onResize())),this.disposers.push(_(e,"keydown",l=>{l.defaultPrevented||!(l.ctrlKey||l.metaKey)||l.altKey||l.key!=="f"&&l.key!=="F"||this.modalOpen(l.target)||(l.preventDefault(),this.focusSearch())})),this.chrome.setWidth(this.rootWidth()),this.view.setPanelWidth(this.rootWidth())}rootWidth(){let e=this.root.getBoundingClientRect().width;return e>0?e:0}onResize(){let e=this.chrome.headerLayout;this.chrome.setWidth(this.rootWidth())!==e&&q(this),this.view.setPanelWidth(this.rootWidth()),this.autoRail(),this.paneColumns!==this.selectionColumns()&&X(this)}selectionColumns(){return this.railMode==="sheet"&&this.rootWidth()>=sg?2:1}railShown(){return this.railOpen}refreshChrome(){q(this)}focusSearch(){this.chrome.setSearchOpen(!0),this.search.focus()}showAbout(e={}){this.railTab="about",this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),X(this),this.saveSoon();let n=e.at==="limitations"?this.rail.revealLimitations():e.focus?this.rail.activePanel():null;if(e.focus&&n)try{n.focus()}catch{}this.announce(e.at==="limitations"?"About: the coverage limitations.":"About this revision shown.")}modalOpen(e){if(this.sheet&&this.sheet.open||bo(this))return!0;let n=e;return!!n&&typeof n.closest=="function"&&!!n.closest(".mlv-sheet, .mlv-workflow__composer")}closeHeaderPanels(){return yo(this,!0)}headerPanelOpen(){return bo(this)}setWorkflow(e,n){let o=n;if(!n||!n.viewport){let s=null;this.graph&&this.workflowRevision===e.revision.id?s={...this.viewportState}:!this.graph&&this.restoredView&&this.restoredView.revision===e.revision.id&&(s={...this.restoredView.viewport}),s&&(o={...n||{},viewport:s})}this.restoredView=null;let i=this.restoredComposer&&this.restoredComposer.revision===e.revision.id?this.restoredComposer.composer:null;if(this.restoredComposer=null,this.workflowRevision!==e.revision.id){let s=!this.graph&&this.restoredTab&&this.restoredTab.revision===e.revision.id?this.restoredTab.tab:null;this.railTab=s||"about"}if(this.restoredTab=null,!this.graph){let s=r=>r===e.revision.id;this.restoredSelection&&s(this.restoredSelection.revision)&&(this.selection=this.restoredSelection.selection),s(this.restoredSheet)&&(this.railChosen=!0,this.railOpen=!0)}this.restoredSelection=null,this.restoredSheet=null,this.workflowDocument=e,this.workflowRevision=e.revision.id,this.autoRail(),Ba(this,Zt(e),o,!0),Ms(this,e,i),this.legendOtherKinds(),this.view.afterChromeChange()}postRequest(e,n){this.requestSerial+=1;let o="";for(let s=0;s<4;s++)o+=Math.floor(Math.random()*36).toString(36);let i="r"+this.requestSerial.toString(36)+"-"+o;for(this.pending.set(i,n);this.pending.size>ig;){let s=this.pending.keys().next().value;if(s===void 0)break;this.pending.delete(s)}return this.bridge.post({...e,requestId:i}),i}onActionResult(e){if(!e||typeof e.requestId!="string")return;let n=this.pending.get(e.requestId);n&&(this.pending.delete(e.requestId),n(e))}openLocation(e,n=!1){Ga(this,e,n)}setStale(e){this.freshness.set(e)&&(this.view.setStale(this.freshness.list()),this.index&&this.view.refresh(this.selection),q(this),X(this))}showHostNotice(e,n){let o=!!n&&n.length===1&&n[0]==="checking";if(this.freshness.checking!==o&&(this.freshness.checking=o,q(this)),o)return;let i=this.notice.root.hidden;this.notice.update(e,n);let s=this.root.querySelector(".mlv-body");s&&this.notice.root.nextSibling!==s&&this.root.insertBefore(this.notice.root,s),this.graph&&i!==this.notice.root.hidden&&this.view.afterChromeChange()}setMinimapCollapsed(e){this.view.setMinimapCollapsed(e),q(this),this.saveSoon(),this.announce("Overview minimap "+(e?"hidden":"shown")+".")}setLegend(e){this.legendOpen=e,this.legend.setOpen(e),q(this),this.saveSoon()}setRailTab(e){this.railTab=e,this.railMode==="sheet"&&!this.railOpen&&(this.railChosen=!0,this.setRailOpen(!0)),X(this),this.saveSoon()}showRailTab(e){this.railTab=e,this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),X(this),this.saveSoon(),this.railMode==="sheet"&&this.rail.focusTab(e)}focusRailTabs(){this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),X(this),this.rail.focusTab(this.railTab)}toggleRail(){this.railChosen=!0,this.railMode==="docked"&&(this.dockedHidden=this.railOpen),this.setRailOpen(!this.railOpen)}collapseSheet(e=!1){if(this.railMode!=="sheet"||!this.railOpen)return!1;if(this.railChosen=!0,this.setRailOpen(!1),this.announce("Bottom panel collapsed."),e)try{this.view.canvasEl.focus()}catch{}return!0}sheetFractionMax(){let e=this.root.querySelector(".mlv-body"),n=e?e.getBoundingClientRect().height:0;if(!(n>0))return Ya;let o=Math.min(240,.45*n);return Math.max(Xa,Math.min(Ya,(n-o)/n))}setSheetFraction(e){let n=Math.max(Xa,Math.min(this.sheetFractionMax(),e));Math.abs(n-this.sheetFraction)<.001||(this.sheetFraction=n,this.rail.setSheetFraction(n),this.railMode==="sheet"&&this.railOpen&&this.graph&&this.view.afterSheetToggle())}autoRail(){let e=this.root.getBoundingClientRect().width;if(!(e>0))return;let n=e-this.railWidth>=Fi?"docked":"sheet",o=this.railOpen;if(n!==this.railMode?o=n==="sheet"?this.railOpen&&this.railChosen:!this.dockedHidden:n==="docked"&&!this.railChosen&&(o=!0),n===this.railMode&&o===this.railOpen)return;let i=n!==this.railMode;this.railMode=n,this.rail.setMode(n,this.sheetFraction),this.setRailOpen(o,!0),i&&(this.saveSoon(),n==="docked"&&this.setRailWidth(this.railWidth),X(this),q(this)),this.graph&&this.view.handleResize()}legendOtherKinds(){let e=new Map,n=0;for(let i of this.graph?.edges||[])if(i.kind==="unknown")n++;else{let s=i.authoredKind||i.kind;e.set(s,(e.get(s)||0)+1)}let o=Array.from(e,([i,s])=>({kind:i,count:s})).sort((i,s)=>s.count-i.count||(i.kinds.kind?1:0));this.legend.setKinds(o,n)}showRailForFinding(){return this.railChosen=!0,this.railOpen?!1:(this.setRailOpen(!0),!0)}setRailOpen(e,n=!1){let o=this.railOpen!==e;this.railOpen=e,e&&this.railMode==="docked"&&(this.dockedHidden=!1),this.rail.setOpen(e),o&&(this.saveSoon(),this.chrome&&q(this),e&&this.rail&&X(this),!n&&this.railMode==="sheet"&&this.graph&&this.view.afterSheetToggle())}toggleShortcuts(e){if(typeof e=="boolean"?e:!this.sheet.open){this.sheet.show();return}this.sheet.hide(this.view.canvasEl)}setRailWidth(e){let n=this.rootWidth(),o=this.railMode==="docked"&&n-Fi>=280?n-Fi:560;this.railWidth=Math.max(280,Math.min(560,o,Math.round(e))),this.railMode==="docked"&&(this.rail.root.style.width=this.railWidth+"px")}setExceptions(e){this.exceptionsOn=e,e?this.view.canvasEl.setAttribute("data-exceptions","on"):this.view.canvasEl.removeAttribute("data-exceptions"),q(this),this.announce(e?"Observed claims faded; inferred and unresolved claims stand out.":"Observed claims shown normally.")}setFlow(e){this.flowOn=e,this.view.flow.setEnabled(e),this.view.flow.syncCanvas(),q(this),this.saveSoon()}select(e,n){e.kind!=="edge"&&(this.edgeAnchor=null),this.selection=e,this.railTab=n&&n.tab?n.tab:this.selectionTab(n&&n.fromList);let o=!1;if(e.kind==="issue"&&(o=this.showRailForFinding()),n&&n.showClaim&&(o=this.showRailForClaim()||o),this.view.applySelection(e),X(this),n&&n.reveal&&e.kind==="edge"?this.view.revealEdge(e.id):n&&n.center&&n.reveal?this.view.revealNode(e.id,!!n.pulse):n&&n.center?this.view.centerOnNode(e.id,!!n.pulse):o&&e.kind!=="issue"&&this.view.keepInView({kind:e.kind,id:e.id}),this.announceSelection(),n&&n.open){let i=this.locOf(e);i&&this.openLocation(i,!!n.focusEditor)}this.saveSoon()}showRailForClaim(){return this.railOpen?(this.railChosen=!0,!1):this.railMode==="docked"&&this.railChosen?!1:(this.railChosen=!0,this.setRailOpen(!0),!0)}selectionTab(e){return e&&this.railTab===e&&this.railShown()?e:"inspector"}clearSelection(){this.selection&&(this.selection=null,this.view.applySelection(null),X(this),this.saveSoon())}locOf(e){if(!this.index)return null;if(e.kind==="node"){let o=this.index.nodeById.get(e.id);return o&&o.loc.file?o.loc:null}if(e.kind==="edge"){let o=this.index.edgeById.get(e.id);return o&&o.loc.file?o.loc:null}let n=this.index.issueById.get(e.id);return n&&n.loc.file?n.loc:null}selectedNodeId(){let e=this.selection;if(!e||!this.index)return null;if(e.kind==="node")return e.id;if(e.kind!=="issue")return null;let n=this.index.issueById.get(e.id);return n&&n.nodeIds.length?n.nodeIds[0]:null}announceSelection(){let e=this.selection;if(!(!e||!this.index)){if(hl()){let n=Kl(this.index,e);n&&this.announce(n);return}if(e.kind==="node"){let n=this.view.labelOf(e.id);n&&this.announce("Selected "+n)}else if(e.kind==="issue"){let n=this.index.issueById.get(e.id);n&&this.announce("Finding "+(n.short?n.short+" ("+n.code+")":n.code)+", "+n.severity+" severity: "+n.title)}}}applyFilters(e){e(),q(this),this.index&&(this.view.render(),this.view.applySelection(this.selection)),X(this),this.saveSoon()}clearFilters(){this.search.clear(),this.applyFilters(()=>this.filters.reset())}selectLane(e){if(!this.index)return;let n=this.index.roots(e);if(!n.length){this.announce("That phase has no steps.");return}this.select({kind:"node",id:n[0]},{center:!0,pulse:!0,fromList:"outline"})}zoomToSelection(){let e=this.selectedNodeId();e&&this.view.zoomToNode(e)}announce(e){this.liveEl.textContent=e}activateHit(e){e.kind==="node"?this.focusNode(e.id,{center:!0,pulse:!0,showClaim:!0}):this.focusIssue(e.id)}onKeyDown(e){Wa(this,e)}onMessage(e){Ua(this,e)}focusNode(e,n){if(this.index){if(!this.index.nodeById.has(e)){this.view.toast("Node not found in this graph");return}this.view.expandAncestors(e),this.select({kind:"node",id:e},{center:n?n.center!==!1:!0,pulse:n?!!n.pulse:!1,showClaim:!!(n&&n.showClaim)})}}focusIssue(e,n){if(!this.index)return;let o=this.index.issueById.get(e);if(o){for(let i of o.nodeIds)this.view.expandAncestors(i);this.select({kind:"issue",id:e},{fromList:n&&n.fromList}),this.view.frameIssue(e)}}openIssue(e,n=!1){if(this.focusIssue(e,{fromList:"issues"}),!this.selection||this.selection.kind!=="issue"||this.selection.id!==e)return;let o=this.locOf({kind:"issue",id:e});o&&this.openLocation(o,n)}setFilters(e){this.applyFilters(()=>this.filters.patch(e))}setTheme(e){this.themes.apply(e)}getState(){return Ka(this)}destroy(){if(!this.destroyed){this.destroyed=!0,this.saveSoon.cancel(),this.pending.clear();for(let e of this.disposers)try{e()}catch{}this.disposers=[],this.themes.destroy(),this.chrome.destroy(),this.view.destroy(),this.releasePage(),H(this.root),this.root.classList.remove("mlv-root")}}};var Tt=null;function lg(){if(Tt)return Tt;let t=typeof window<"u"?window:{};if(typeof t.acquireVsCodeApi=="function")try{Tt=t.acquireVsCodeApi()}catch{Tt=null}return Tt}function ag(){if(typeof document>"u"||!document.body)return"light";let t=document.body.className||"";return t.indexOf("vscode-high-contrast")>=0?"hc":t.indexOf("vscode-dark")>=0?"dark":"light"}function dg(t){if(typeof window>"u")return()=>{};let e=n=>{let o=n&&n.data;o&&typeof o=="object"&&t(o)};return window.addEventListener("message",e),()=>window.removeEventListener("message",e)}function Za(){let t=lg(),e={canOpenSource:!0};return{host:"vscode",theme:ag(),capabilities:e,post(n){t&&typeof t.postMessage=="function"&&t.postMessage(n)},onMessage(n){return dg(n)},saveState(n){t&&typeof t.setState=="function"&&t.setState(n)},loadState(){if(t&&typeof t.getState=="function"){let n=t.getState();return n&&typeof n=="object"?n:null}return null}}}var cg="0.3.0";function ug(t,e,n){if(!t)throw new Error("MLView.mountWorkflow: a root element is required");if(!n)throw new Error("MLView.mountWorkflow: a HostBridge is required");let o=new Zn(t,n);return o.setWorkflow(e),o}var hg={vscode(){return Za()}};return nd(fg);})(); +"use strict";var MLView=(()=>{var No=Object.defineProperty;var xc=Object.getOwnPropertyDescriptor;var Ec=Object.getOwnPropertyNames;var kc=Object.prototype.hasOwnProperty;var _c=(t,e)=>{for(var n in e)No(t,n,{get:e[n],enumerable:!0})},Sc=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ec(e))!kc.call(t,i)&&i!==n&&No(t,i,{get:()=>e[i],enumerable:!(o=xc(e,i))||o.enumerable});return t};var Mc=t=>Sc(No({},"__esModule",{value:!0}),t);var Tb={};_c(Tb,{bridges:()=>Cb,mountWorkflow:()=>Ab,normalizeWorkflow:()=>wn,version:()=>Ib});function ke(t){if(!t||typeof t.evidenceId!="string")return null;let e=t.cell;return typeof e!="number"||!isFinite(e)||e<0||Math.trunc(e)!==e?null:{cell:e,line:t.line}}function dt(t){let e=It(t);return e.head+e.tail}function It(t){let e=ke(t);return e?{head:t.file,tail:" › cell "+e.cell+", line "+e.line}:{head:t.file,tail:":"+t.line}}function en(t){let e=ke(t);return e?t.file+" cell "+e.cell+" line "+e.line:t.file+" line "+t.line}function Ze(t){let e=ke(t);return e?"cell "+e.cell+", counted from 0 as the artifact records it (markdown cells count too); line "+e.line+" of that cell":""}var _e=["http","//www.w3.org/2000/svg"].join(":"),Ns=["http","//www.w3.org/1999/xlink"].join(":");function f(t,e,n){let o=document.createElement(t);return e&&(o.className=e),n!==void 0&&(o.textContent=n),o}function H(t,e){let n=document.createElementNS(_e,t);return e&&Se(n,e),n}function Se(t,e){for(let n of Object.keys(e))t.setAttribute(n,String(e[n]))}function D(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function m(t,e){return t.appendChild(e),e}function U(t,e,n){let o=f("button",t,e);return o.type="button",o.setAttribute("aria-label",n||e),n&&(o.title=n),o}function he(t,e){let n=f("button",t);return n.type="button",n.title=e,n.setAttribute("aria-label",e),n}function _(t,e,n,o){return t.addEventListener(e,n,o),()=>t.removeEventListener(e,n,o)}function Fs(t,e){let n=null,o=((...i)=>{n!==null&&clearTimeout(n),n=setTimeout(()=>{n=null,t(...i)},e)});return o.cancel=()=>{n!==null&&clearTimeout(n),n=null},o}function we(t){return dt(t)}function Qe(t,e,n="span"){let o=f(n,t+(t?" ":"")+"mlv-loc"),i=It(e);m(o,f("span","mlv-loc__file",i.head)),m(o,f("span","mlv-loc__at",i.tail));let s=ke(e);return s&&(o.setAttribute("data-cell",String(s.cell)),o.title=Ze(e)),o}var tn={low:"M9 2A7 7 0 1 0 9 16A7 7 0 0 0 9 2Z",medium:"M9.9 2.75a1.04 1.04 0 0 0-1.8 0L1.5 14.6a1.04 1.04 0 0 0 .9 1.56h13.2a1.04 1.04 0 0 0 .9-1.56Z",high:"M6.05 1.4h5.9L16.6 6.05v5.9L11.95 16.6h-5.9L1.4 11.95v-5.9Z"},Fo={low:"M9 4.3a1.08 1.08 0 1 0 0 2.16A1.08 1.08 0 0 0 9 4.3ZM7.94 7.65h2.12v5.7H7.94Z",medium:"M7.95 6.45h2.1l-.28 4.62h-1.54ZM9 12.1a1.12 1.12 0 1 0 0 2.24A1.12 1.12 0 0 0 9 12.1Z",high:"M7.94 4.5h2.12l-.3 6.05h-1.52ZM9 11.7a1.15 1.15 0 1 0 0 2.3A1.15 1.15 0 0 0 9 11.7Z"},ne=["high","medium","low"],ye={high:"high",medium:"medium",low:"low"};function Lc(t){return t==="low"||t==="medium"||t==="high"}function ee(t){return Lc(t)?t:"low"}function Me(){return{low:0,medium:0,high:0}}function Y(t){return t.low+t.medium+t.high}function oe(t){return t.high>0?"high":t.medium>0?"medium":t.low>0?"low":null}function Z(t,e=18,n){let o=ee(t),i=H("svg",{class:"mlv-glyph mlv-glyph--"+o,viewBox:"0 0 18 18",width:e,height:e,role:"img","aria-label":n||ye[o]+" severity"}),s=document.createElementNS(_e,"path");Se(s,{class:"mlv-glyph__shape",d:tn[o],"vector-effect":"non-scaling-stroke"});let r=document.createElementNS(_e,"path");return Se(r,{class:"mlv-glyph__ink",d:Fo[o]}),i.appendChild(s),i.appendChild(r),i}function Ds(t){let e=Y(t),n=oe(t);return e+(e===1?" finding":" findings")+", highest severity "+(n?ye[n]:"none")}function nn(t){let e=Y(t),n=ne.filter(o=>t[o]>0).map(o=>t[o]+" "+ye[o]);return e+(e===1?" finding":" findings")+": "+n.join(", ")}var Bs=2;function Bo(t){let e=t.slice(0,Bs).join(" "),n=t.length-Bs;return n>0?e+" +"+n:e}function js(t){return t.length?t.length===1?"finding "+t[0]:"findings "+t.slice(0,-1).join(", ")+" and "+t[t.length-1]:""}function Gs(t,e=18,n=[]){let o=oe(t);if(!o)return null;let i=Y(t),s=f("div","mlv-badge mlv-badge--"+o);return s.setAttribute("role","img"),s.setAttribute("aria-label",n.length?js(n).replace(/^f/,"F")+", highest severity "+ye[o]:Ds(t)),s.appendChild(Z(o,e,"")),n.length?s.appendChild(f("span","mlv-badge__ids",Bo(n))):i>1&&s.appendChild(f("span","mlv-badge__count",String(i))),s}function Ce(t,e=14,n){if(Y(t)===0)return null;let o=f("div","mlv-cluster");o.setAttribute("role","img"),o.setAttribute("aria-label",n||Ds(t)),o.title=n||nn(t);for(let i of ne){let s=t[i];if(s<=0)continue;let r=f("span","mlv-cluster__item mlv-cluster__item--"+i);r.appendChild(Z(i,e,"")),r.appendChild(f("span","mlv-cluster__count",String(s))),o.appendChild(r)}return o}function Do(t){return t/2+1.5}function Ws(t,e=16,n=[]){let o=ee(t),i=H("g",{class:"mlv-edge-marker mlv-edge-marker--"+o,role:"img"}),s=n.length?js(n)+" on this connection, highest severity "+ye[o]:ye[o]+" severity finding on this connection";if(i.setAttribute("aria-label",s.charAt(0).toUpperCase()+s.slice(1)),n.length){let l=H("title");l.textContent=i.getAttribute("aria-label")||"",i.appendChild(l)}let r=H("circle",{class:"mlv-edge-marker__disc",r:Do(e),cx:0,cy:0});i.appendChild(r);let a=Z(o,e,"");return a.setAttribute("x",String(-e/2)),a.setAttribute("y",String(-e/2)),a.removeAttribute("role"),a.removeAttribute("aria-label"),a.setAttribute("aria-hidden","true"),i.appendChild(a),i}var Ic=["changed","missing","unreadable","too-large","elsewhere"];var me={changed:"changed since publishing",missing:"file missing",unreadable:"file unreadable","too-large":"file too large to check",elsewhere:"in another folder; see the notice above"};function ae(t){return t.length>0&&t.every(e=>e==="elsewhere")}function zs(t){if(!Array.isArray(t))return[];let e=[],n=new Set;for(let o of t){if(e.length>=2e3)break;if(!o||typeof o!="object")continue;let i=o.path,s=o.reason;typeof i!="string"||!i||i.length>500||n.has(i)||typeof s!="string"||Ic.indexOf(s)<0||(n.add(i),e.push({path:i,reason:s}))}return e}var on=class{constructor(){this.reasons=new Map;this.checking=!1}set(e){let n=new Map(e.map(i=>[i.path,i.reason])),o=n.size===this.reasons.size;if(o)for(let[i,s]of n)this.reasons.get(i)!==s&&(o=!1);return this.reasons=n,!o}reasonOf(e){return e?this.reasons.get(e):void 0}get size(){return this.reasons.size}paths(){return Array.from(this.reasons.keys())}list(){return Array.from(this.reasons,([e,n])=>({path:e,reason:n}))}};function Vs(t,e){let n=t||[],o=0;for(let i of n)i.file&&e(i.file)&&o++;return{stale:o,total:n.length}}function Ac(t){if(ae(t))return"in another folder";let e=new Set(t);return e.size===1&&e.has("changed")?"changed":e.has("changed")?"changed or missing":"missing"}function Cc(t,e){if(!t||e.size===0)return null;let n=Array.from(new Set((t.evidence||[]).map(c=>c.file))),o=n.filter(c=>e.reasonOf(c)),i=e.paths().filter(c=>n.indexOf(c)<0),s=Ac(e.list().map(c=>c.reason)),r;o.length?(r=o.length+" of "+n.length+" cited files "+s,i.length&&(r+=" · "+i.length+" inspected")):r=i.length+" inspected "+(i.length===1?"file":"files")+" "+s;let a=e.list().slice(0,20).map(c=>c.path+" — "+me[c.reason]);e.size>20&&a.push("and "+(e.size-20)+" more");let d=(ae(e.list().map(c=>c.reason))?"Source files that are not under the workspace root but are unchanged in another folder; the notice above names it. Jumps into them are blocked.":"Source files that no longer match the published revision. Jumps into them are blocked.")+` +`+a.join(` +`);return{text:r,title:d}}function qs(t,e){if(!t)return null;let n=Cc(t,e);if(n)return{state:"stale",text:n.text,title:n.title};if(!t.verification)return{state:"unverified",text:"Freshness not checked",title:"This revision was published without source hashes, so MLView cannot tell whether the cited files changed since."};let o=new Set((t.evidence||[]).map(i=>i.file)).size;return{state:"unchanged",text:o+(o===1?" cited file unchanged":" cited files unchanged"),title:"The "+(o===1?"file":o+" files")+" the quotes cite still match the hashes published with revision "+t.revision.id+". Unchanged means the quoted lines still exist as published; it does not mean they support the claims."}}function Ks(t,e){if(!(!t||typeof t!="object"||t.v!==1))switch(t.type){case"init":e.init(t.theme,t.capabilities);return;case"workflow":e.workflow(t.document);return;case"workflowError":{let n=Array.isArray(t.codes)?t.codes.filter(o=>typeof o=="string"):void 0;e.workflowStatus(n),e.hostNotice(typeof t.message=="string"?t.message:"",n);return}case"stale":e.stale(zs(t.files));return;case"actionResult":e.actionResult(t);return;case"theme":e.theme(t.kind);return;case"reveal":{let n=t.id;if(typeof n!="string"||!n)return;t.kind==="node"?e.revealNode(n,!0):t.kind==="edge"?e.revealEdge(n):t.kind==="issue"&&e.revealIssue(n);return}case"revealNode":typeof t.nodeId=="string"&&t.nodeId&&e.revealNode(t.nodeId,t.center!==!1);return;case"revealIssue":typeof t.issueId=="string"&&t.issueId&&e.revealIssue(t.issueId);return;case"restoreState":e.restoreState(t.state);return;default:e.onUnknown(String(t.type))}}function Us(t,e){return!t||typeof t!="object"?{...e,severities:e.severities.slice()}:{severities:Array.isArray(t.severities)?t.severities.slice():e.severities.slice(),query:typeof t.query=="string"?t.query:""}}var Tc=["low","medium","high"];function jo(){return{severities:Tc.slice(),query:""}}var sn=class{constructor(){this.current=jo();this.keep=e=>this.current.severities.indexOf(ee(e.severity))>=0}get value(){return this.current}get query(){return this.current.query}patch(e){let n=this.snapshot();e.severities!==void 0&&(n.severities=e.severities.slice()),e.query!==void 0&&(n.query=String(e.query)),this.current=n}reset(){this.current=jo()}restore(e){this.current=Us(e,jo())}snapshot(){return{severities:this.current.severities.slice(),query:this.current.query}}toggleSeverity(e){let n=this.current.severities.slice(),o=n.indexOf(e);o>=0?n.splice(o,1):n.push(e),this.patch({severities:n})}};function Xs(t){return t==="light"||t==="dark"||t==="hc"}var rn=class{constructor(e,n){this.root=e,this.current=Xs(n)?n:"light"}get kind(){return this.current}apply(e){Xs(e)&&(this.current=e,this.root.setAttribute("data-theme",e))}destroy(){this.root.removeAttribute("data-theme")}};var Rc='a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';function Oc(t){let e=[];for(let n of Array.from(t.querySelectorAll(Rc))){if(n.tabIndex<0)continue;let o=!0;for(let i=n;i&&i!==t.parentElement;i=i.parentElement)if(i.hidden){o=!1;break}o&&e.push(n)}return e}function an(t,e){if(e.key!=="Tab"||e.altKey||e.ctrlKey||e.metaKey)return!1;let n=Oc(t),o=t.ownerDocument?t.ownerDocument.activeElement:null;if(!n.length)return e.preventDefault(),!0;let i=n[0],s=n[n.length-1],r=!!o&&t.contains(o),a=null;if(e.shiftKey&&(!r||o===i)?a=s:!e.shiftKey&&(!r||o===s)&&(a=i),!a)return!1;e.preventDefault();try{a.focus()}catch{}return!0}function At(t,e){let n=i=>!!i&&i.isConnected&&!i.disabled&&typeof i.focus=="function",o=i=>{if(!n(i)||i===i.ownerDocument.body)return!1;try{i.focus()}catch{return!1}return i.ownerDocument.activeElement===i};o(t)||o(e)}var Rt=8;function Ys(t){return(t%Rt+Rt)%Rt}var ln=class{constructor(e){this.nodeById=new Map;this.edgeById=new Map;this.issueById=new Map;this.issueRank=new Map;this.childrenOf=new Map;this.lexicalParentOf=new Map;this.laneChildrenOf=new Map;this.parentOf=new Map;this.rootsByLane=new Map;this.outEdges=new Map;this.inEdges=new Map;this.lanes=[];this.absentStages=[];this.phaseOrder=new Map;this.graph=e;for(let a of e.nodes||[])this.nodeById.set(a.id,a);for(let a of e.edges||[])this.edgeById.set(a.id,a);for(let a of e.issues||[])this.issueById.set(a.id,a),this.issueRank.has(a.id)||this.issueRank.set(a.id,this.issueRank.size);for(let a of e.nodes||[]){let l=a.parent&&this.nodeById.has(a.parent)?a.parent:null;this.lexicalParentOf.set(a.id,l),l&&Tt(this.childrenOf,l,a.id)}for(let a of e.nodes||[])this.hasCycle(a.id)&&this.lexicalParentOf.set(a.id,null);for(let a of e.nodes||[]){let l=this.lexicalParentOf.get(a.id)||null,d=l?this.nodeById.get(l):void 0,c=!!d&&d.stage===a.stage;this.parentOf.set(a.id,c?l:null),c?Tt(this.laneChildrenOf,l,a.id):Tt(this.rootsByLane,a.stage,a.id)}for(let a of e.edges||[])!this.nodeById.has(a.source)||!this.nodeById.has(a.target)||(Tt(this.outEdges,a.source,a),Tt(this.inEdges,a.target,a));let n=new Map;for(let a of e.stages||[])n.set(a.id,a);let o=(e.stages||[]).slice().sort((a,l)=>a.order-l.order||Hc(a.id,l.id));for(let a of o)(this.rootsByLane.get(a.id)||[]).length>0||a.present?this.lanes.push(a):this.absentStages.push(a);let i=new Set(this.lanes.map(a=>a.id)),s=[];for(let a of e.nodes||[])!n.has(a.stage)&&!i.has(a.stage)&&(i.add(a.stage),s.push(a.stage));s.sort();for(let a of o)this.phaseOrder.has(a.id)||this.phaseOrder.set(a.id,this.phaseOrder.size);for(let a of s)this.phaseOrder.set(a,this.phaseOrder.size);let r=this.lanes.length?this.lanes[this.lanes.length-1].order+1:0;for(let a of s)this.lanes.push({id:a,label:a,order:r++,present:!0,nodeCount:(this.rootsByLane.get(a)||[]).length,issueCounts:Me(),maxSeverity:null})}phaseIndexOf(e){return this.phaseOrder.get(e)??0}hasCycle(e){let n=this.lexicalParentOf.get(e)||null,o=0;for(;n;){if(n===e||++o>4096)return!0;n=this.lexicalParentOf.get(n)||null}return!1}children(e){return this.childrenOf.get(e)||[]}laneChildren(e){return this.laneChildrenOf.get(e)||[]}roots(e){return this.rootsByLane.get(e)||[]}ancestors(e){let n=[],o=this.parentOf.get(e)||null;for(;o;)n.push(o),o=this.parentOf.get(o)||null;return n}isGroup(e){return this.laneChildren(e).length>0}issuesOf(e,n){let o=this.nodeById.get(e);if(!o)return[];let i=[];for(let s of o.issueIds){let r=this.issueById.get(s);r&&n(r)&&i.push(r)}return i}issuesOfEdge(e,n){let o=this.edgeById.get(e);if(!o)return[];let i=[];for(let s of o.issueIds){let r=this.issueById.get(s);r&&n(r)&&i.push(r)}return i}issuesOfEdges(e,n){if(e.length===1)return this.issuesOfEdge(e[0],n);let o=new Set,i=[];for(let s of e)Ct(i,o,this.issuesOfEdge(s,n));return this.inDocumentOrder(i)}inDocumentOrder(e){return e.sort((n,o)=>(this.issueRank.get(n.id)??0)-(this.issueRank.get(o.id)??0))}countsFor(e){let n=Me();for(let o of e)n[ee(o.severity)]++;return n}ownCounts(e,n){return this.countsFor(this.issuesOf(e,n))}subtreeIssues(e,n){let o=[],i=l=>{o.push(l);for(let d of this.laneChildren(l))i(d)};i(e);let s=new Set(o),r=new Set,a=[];for(let l of o){Ct(a,r,this.issuesOf(l,n));for(let d of this.outEdges.get(l)||[])!s.has(d.target)||l===e&&d.target===e||Ct(a,r,this.issuesOfEdge(d.id,n))}return this.inDocumentOrder(a)}subtreeCounts(e,n){return this.countsFor(this.subtreeIssues(e,n))}descendantCount(e){let n=0;for(let o of this.laneChildren(e))n+=1+this.descendantCount(o);return n}laneCounts(e,n){let o=new Set,i=[];for(let s of this.graph.nodes||[])s.stage===e&&Ct(i,o,this.issuesOf(s.id,n));for(let s of this.graph.edges||[]){let r=this.nodeById.get(s.source);!r||r.stage!==e||Ct(i,o,this.issuesOfEdge(s.id,n))}return this.countsFor(i)}isHidden(e,n){let o=this.parentOf.get(e)||null;for(;o;){if(n.has(o))return!0;o=this.parentOf.get(o)||null}return!1}visibleRepresentative(e,n){let o=this.ancestors(e);for(let i=o.length-1;i>=0;i--)if(n.has(o[i]))return o[i];return e}defaultCollapsed(){let e=[],n=(this.graph.nodes||[]).length>120,o=this.findingTargetAncestors();for(let i of this.graph.nodes||[]){if(!this.isGroup(i.id)||o.has(i.id))continue;let s=this.descendantCount(i.id);(s>12||n&&s>4)&&e.push(i.id)}return e.sort()}findingTargetAncestors(){let e=new Set;for(let o of this.graph.issues||[]){for(let i of o.nodeIds||[])e.add(i);for(let i of o.edgeIds||[]){let s=this.edgeById.get(i);s&&(e.add(s.source),e.add(s.target))}}let n=new Set;for(let o of e)for(let i of this.ancestors(o))n.add(i);return n}};function Ct(t,e,n){for(let o of n)e.has(o.id)||(e.add(o.id),t.push(o))}function Tt(t,e,n){let o=t.get(e);o?o.push(n):t.set(e,[n])}function Hc(t,e){return te?1:0}function le(t,e){t.setAttribute("data-phase-index",String(e)),t.setAttribute("data-phase-tone",String(Ys(e)))}function Qs(t){return t.length||1}function dn(t){if(t<2)return 1.5;let e=1.5+Math.log2(t)*.75;return Math.round(Math.min(5,e)*100)/100}function Ot(t){return"×"+t}var Zs=13;function cn(t,e,n){if(!n)return{x:t.x,y:t.y};let o=typeof e=="number"&&isFinite(e)?e:0;return{x:t.x+Math.cos(o+Math.PI/2)*Zs,y:t.y+Math.sin(o+Math.PI/2)*Zs}}function un(t){return Math.round(Ot(t).length*5.6+10)}var Te=13;var $s={rankdir:"LR",ranksep:72,nodesep:24,edgesep:12,marginx:16,marginy:16,ranker:"network-simplex"},Pc={data:4,call:2,control:2,config:1};function Js(t){let e=Pc[t];return typeof e=="number"?e:1}var er=26,Go=264,hn=24,Wo=28,zo=2e3,tr=20,Vo=8,Nc=14,ge=8,fn=16,qo=12,Ko=Nc,nr=112,or=4,ir=13,sr=6,Uo=28;var Fc=6.02,gn=14,Bc=1,Dc=4,pn=8,rr=2,ar=20,lr=10,jc=3,Xo=14,Gc=.7,mn=7,Re=96;function Zo(t){return t.label?t.back?"↻ "+t.label:t.label:""}function Wc(t){return Oe(t.length*Fc+2*Dc)}function zc(t){return t.back||t.count>1||Vc.indexOf(t.kind)>=0}var Vc=["data","control","state","loop","output"];function Yo(t,e,n){return t.x-ne.x&&t.y-ne.y}var vn=class{constructor(){this.cells=new Map}add(e){let n=Math.floor(e.x/Re),o=Math.floor((e.x+e.w)/Re),i=Math.floor(e.y/Re),s=Math.floor((e.y+e.h)/Re);for(let r=n;r<=o;r++)for(let a=i;a<=s;a++){let l=r+":"+a,d=this.cells.get(l);d?d.push(e):this.cells.set(l,[e])}}hits(e,n){let o=Math.floor((e.x-n)/Re),i=Math.floor((e.x+e.w+n)/Re),s=Math.floor((e.y-n)/Re),r=Math.floor((e.y+e.h+n)/Re);for(let a=o;a<=i;a++)for(let l=s;l<=r;l++){let d=this.cells.get(a+":"+l);if(d){for(let c of d)if(Yo(e,c,n))return!0}}return!1}};function qc(t){let e=[];for(let n of t.boxes.values()){if(!n.isGroup||n.collapsed){e.push({x:n.x,y:n.y,w:n.w,h:n.h});continue}e.push({x:n.x,y:n.y,w:n.w,h:Math.max(n.headerH,1)})}return e}function Kc(t){let e=[];for(let n of t){let o=n.y+28,i=n.y+n.h-28;i-o=n.top&&e.y+e.h<=n.bottom)return n;return null}function Xc(t,e){return e.x>=0&&e.y>=0&&e.x+e.w<=t.width&&e.y+e.h<=t.height}function Yc(t,e){let n=[];for(let o=1;o0?ge:0,a=o.51){let c=s.x>i.x,u=Math.min(i.x,s.x)+(c?r:a),h=Math.max(i.x,s.x)-(c?a:r);if(h-ug.bottom||n.push({axis:"h",at:p,a:u,b:h,laneId:g.id,score:h-u,order:o})}else if(l<.51&&d>.51){let c=s.y>i.y,u=Math.min(i.y,s.y)+(c?r:a),h=Math.max(i.y,s.y)-(c?a:r);if(h-u.001?e.score-t.score:t.order-e.order}function Qc(t){if(t.length<2)return null;let e=t[0],n=t[1],o=Math.abs(n.x-e.x),i=Math.abs(n.y-e.y);return i<.51&&o>.51?{axis:"h",at:(e.y+n.y)/2,a:Math.min(e.x,n.x),b:Math.max(e.x,n.x),laneId:"",score:o,order:1}:o<.51&&i>.51?{axis:"v",at:(e.x+n.x)/2,a:Math.min(e.y,n.y),b:Math.max(e.y,n.y),laneId:"",score:i,order:1}:null}function $c(t,e){let n=e/2,o=t.axis==="h"?n:gn/2,i=(t.a+t.b)/2,s=t.a+o,r=t.b-o,a=r>s?Qo(i,s,r):i,l=r>s?(r-s)/2:0,d=[0,l*.6,-l*.6,l,-l],c=[];for(let u of d){if(c.length>=lr)break;if(u!==0&&Math.abs(u)<1)continue;let h=a+u;for(let p of[!1,!0]){if(c.length>=lr)break;let g,v;t.axis==="h"?(g=h,v=p?t.at+pn:t.at-pn):(g=p?t.at-pn-n:t.at+pn+n,v=h),c.push({x:Oe(g),y:Oe(v),rect:{x:Oe(g-n),y:Oe(v-Bc-gn/2),w:e,h:gn},flipped:p,shift:Math.abs(Oe(u))})}}return c}function dr(t){return{x:t.x-mn,y:t.y-mn,w:mn*2,h:mn*2}}function Jc(t){let e=0;for(let n=1;n=e||o===t.length-1){let s=i===0?0:Qo((e-n)/i,0,1);return{x:Oe(t[o-1].x+(t[o].x-t[o-1].x)*s),y:Oe(t[o-1].y+(t[o].y-t[o-1].y)*s)}}n+=i}return t[t.length-1]}function tu(t,e,n){if(!n||!Yo(dr(e),n,1))return{point:e,moved:!1};let o=Jc(t),i=o/2;for(let s of[18,-18,30,-30,44,-44]){let r=Qo(i+s,Math.min(8,o),Math.max(o-8,0)),a=eu(t,r);if(!Yo(dr(a),n,1))return{point:a,moved:!0}}return{point:e,moved:!1}}function cr(t,e){let n=new Map,o={labels:0,always:0,placed:0,hidden:0,flipped:0,shifted:0,fallback:0,nudged:0},i=Kc(t.lanes),s=new vn;for(let l of qc(t))s.add(l);let r=new vn,a=[[],[]];for(let l of e){if(!l.label)continue;o.labels++;let d=zc(l);d&&o.always++,a[d?0:1].push(l)}for(let l=0;l({run:I,fallback:!1}));g&&v.push({run:g,fallback:!0});let b=null,x=null,y=!1,k=null,E=null,w=null,S=null,C=0;for(let I of v){if(b||C>=ar)break;for(let F of $c(I.run,h)){if(C>=ar)break;if(C++,!Xc(t,F.rect))continue;let A=Uc(i,F.rect);if(A&&(E||(E=F,w=I.run,S=A),!s.hits(F.rect,rr)&&!r.hits(F.rect,rr))){b=F,x=I.run,y=I.fallback,k=A;break}}}if(!b&&!d&&E&&(b=E,x=w,k=S),!b){o.hidden++,n.set(c.id,{id:c.id,text:u,x:c.mid.x,y:c.mid.y,rect:{x:c.mid.x,y:c.mid.y,w:0,h:0},hidden:!0,flipped:!1,axis:v.length?v[0].run.axis:"h",laneId:null,fallback:y,shifted:0,marker:c.mid,always:d});continue}let L=tu(c.points,c.mid,b.rect);L.moved&&o.nudged++,o.placed++,b.flipped&&o.flipped++,b.shift>0&&o.shifted++,y&&o.fallback++,r.add(b.rect),n.set(c.id,{id:c.id,text:u,x:b.x,y:b.y,rect:b.rect,hidden:!1,flipped:b.flipped,axis:x?x.axis:"h",laneId:k?k.id:null,fallback:y,shifted:b.shift,marker:L.point,always:d})}}return{placements:n,stats:o}}function Qo(t,e,n){return tn?n:t}function Oe(t){return Math.round(t*100)/100}var nu=["data","call","control","config","state","loop","output"],ou={data:"data",dataflow:"data","data flow":"data",flow:"data",control:"control","control flow":"control",branch:"control","conditional call":"control",decision:"control",state:"state","state update":"state",update:"state","state read":"state","state copy":"state",gradient:"state","gradient flow":"state",optimizes:"state",ownership:"state","state ownership":"state",config:"config",configuration:"config","configuration flow":"config",call:"call",calls:"call",loop:"loop",loops:"loop",output:"output",outputs:"output"};function Jo(t){let e=String(t||"").trim();if(!e)return"unknown";let n=e.toLowerCase().replace(/[\s_-]+/g," ");return ou[n]||e}function Ht(t){return t&&t!=="unknown"?t:"kind not specified"}var ur=0;function hr(){return ur++,ur}function iu(t,e){let n="";for(let o=0;o=0?t:"unknown"}function ru(t,e,n,o){let i=H("marker",{id:t,viewBox:"0 0 10 10",refX:8.5,refY:5,markerWidth:9,markerHeight:9,markerUnits:"userSpaceOnUse",orient:"auto"}),s=H("path",{class:e+(o?"":" mlv-arrow--open"),d:n});return o||Se(s,{fill:"none"}),i.appendChild(s),i}var Pt={d:"M0.5 1 L9 5 L0.5 9 Z"},fr="mlv-arrow";function pr(){let t=H("defs");return t.appendChild(ru(fr,"mlv-arrow",Pt.d,!0)),t}var mr=14,ct=Do(mr);function ei(t){return t.placement?t.placement.marker:t.route.mid}function gr(t){let e=t.route,n=su(e.kind),o=t.weight&&t.weight>1?Math.floor(t.weight):1,i=o>=2,s=H("g",{class:"mlv-edge mlv-edge--"+n+(e.count>1?" mlv-edge--merged":"")+(i?" mlv-edge--weighted":"")+(e.back?" mlv-edge--back":""),"data-edge-id":e.id,"data-edge-kind":e.kind});s.setAttribute("data-edge-ids",e.ids.join(" ")),i&&(s.setAttribute("data-edge-weight",String(o)),s.style.setProperty("--mlv-edge-w",dn(o)+"px")),t.stage&&s.setAttribute("data-stage",t.stage),t.phase!==void 0&&le(s,t.phase),t.basis&&s.setAttribute("data-basis",t.basis),t.severity&&(s.setAttribute("data-sev",t.severity),s.classList.add("has-issue")),t.stale&&(s.classList.add("is-stale"),s.setAttribute("data-stale","1"));let r=H("path",{class:"mlv-edge__hit",d:e.d});r.setAttribute("tabindex","-1"),r.setAttribute("role","button");let a=t.staleElsewhere?" Its evidence cites a file in another folder.":" Its evidence cites a changed or missing file.";r.setAttribute("aria-label",du(e,t.sourceLabel,t.targetLabel,t.basis)+(t.stale?a:"")),s.appendChild(r);let l=H("path",{class:"mlv-edge__path",d:e.d});l.setAttribute("marker-end","url(#"+fr+")"),l.setAttribute("id",iu(t.mountSerial,e.id)),s.appendChild(l);let d=t.placement;if(e.label&&d&&d.hidden)s.setAttribute("data-label-hidden","1");else if(e.label){let u=d?d.x:e.mid.x,h=d?d.y:e.mid.y-8,p=H("text",{class:"mlv-edge-label",x:u,y:h});p.textContent=Zo(e),d&&(p.setAttribute("data-label-axis",d.axis),d.flipped&&p.setAttribute("data-label-flipped","1"),d.laneId&&p.setAttribute("data-label-lane",d.laneId)),s.appendChild(p),t.labelVisible&&s.classList.add("has-label")}let c=ei(t);if(t.severity){let u=Ws(t.severity,mr,t.findings||[]);u.setAttribute("transform","translate("+c.x+","+c.y+")"),s.appendChild(u)}else if(e.back){let u=H("path",{class:"mlv-edge__loopmark",d:"M4 -4 L0 0 L4 4",fill:"none",stroke:"var(--mlv-edge)","stroke-width":1.3,"stroke-linecap":"round","stroke-linejoin":"round",transform:"translate("+c.x+","+c.y+")"});s.appendChild(u)}return i&&s.appendChild(lu(o,c,e.midAngle,!!t.severity||e.back)),t.stale&&s.appendChild(au(c,!!t.severity||e.back||i)),s}function au(t,e){let n=t.x+(e?ct+10:0),o=H("g",{class:"mlv-edge__stale",transform:"translate("+bn(n)+","+bn(t.y)+")"});o.setAttribute("data-stale","1"),o.appendChild(H("circle",{class:"mlv-edge__stalebg",r:7.5})),o.appendChild(H("path",{class:"mlv-edge__staleicon",d:"M0 -4.6 4.9 3.9H-4.9ZM0 -1.5v2.6M0 2.7v.1",fill:"none"}));let i=H("title");return i.textContent="This connection cites a file that changed or is missing since publishing. Its jumps are blocked.",o.appendChild(i),o}function lu(t,e,n,o){let i=Ot(t),s=un(t),r=cn(e,n,o),a=H("g",{class:"mlv-edge__weight",transform:"translate("+bn(r.x)+","+bn(r.y)+")"});a.setAttribute("data-weight",String(t)),a.appendChild(H("rect",{class:"mlv-edge__weightbg",x:-s/2,y:-Te/2,width:s,height:Te,rx:Te/2}));let l=H("text",{class:"mlv-edge__weighttext",x:0,y:0});l.textContent=i,a.appendChild(l);let d=H("title");return d.textContent=t+" connections merged into this edge.",a.appendChild(d),a}function bn(t){return Math.round(t*100)/100}function du(t,e,n,o){let i=t.back?"loop-back connection":t.kind&&t.kind!=="unknown"?t.kind+" connection":"connection",s=t.label?" labelled "+t.label:"",r=e&&n?", flows from "+e+" to "+n:"",a=t.count>1?", "+t.count+" merged connections":"";return i+s+r+a+$e(o)+". Press Enter to open the cited source."}function $e(t){return t==="inferred"?", inferred, not observed":t==="unresolved"?", unresolved":""}function vr(t,e,n,o,i){let s=H("g",{class:"mlv-connector-group","data-sev":o}),r=(t.x+e.x)/2,a=(t.y+e.y)/2,l="M "+t.x+" "+t.y+" Q "+r+" "+(a-28)+" "+e.x+" "+e.y;s.appendChild(H("path",{class:"mlv-connector",d:l}));let d=H("circle",{class:"mlv-connector__badge",cx:r,cy:a-14,r:8});s.appendChild(d);let c=H("text",{class:"mlv-connector__label",x:r,y:a-14});c.textContent=String(n),s.appendChild(c);let u=H("title");return u.textContent=i,s.appendChild(u),s}function ut(t,e){let n=e.map(o=>t.get(o)).find(Boolean);return n?{file:n.file,absFile:"",line:n.line,col:0,endLine:n.endLine,endCol:0,snippet:n.quote,cell:n.cell,evidenceId:n.id}:{file:"",absFile:"",line:1,col:0,endLine:1,endCol:0}}function cu(t){return"F"+(t+1)}var uu="unspecified model";function wn(t){if(!t||t.workflowVersion!=="1.0")throw new Error("MLView.mountWorkflow: workflowVersion must be 1.0");let e=new Map((t.evidence||[]).map(u=>[u.id,u])),n=new Map,o=new Map;for(let u of t.findings||[])for(let h of u.nodeIds||[]){let p=n.get(h)||[];p.push(u.id),n.set(h,p)}for(let u of t.findings||[])for(let h of u.edgeIds||[]){let p=o.get(h)||[];p.push(u.id),o.set(h,p)}let i=new Map((t.phases||[]).map(u=>[u.id,u.label])),s=(t.nodes||[]).map(u=>({...typeof u.detail=="string"&&u.detail.trim()?{detail:u.detail}:{},...i.has(u.phase)?{phaseLabel:i.get(u.phase)}:{},id:u.id,kind:u.kind||"unknown",level:u.parent?"op":"unit",stage:u.phase,label:u.label,sublabel:u.detail||"",qualname:u.label,loc:ut(e,u.evidence),parent:u.parent||null,basis:u.basis,evidenceLocs:u.evidence.map(h=>ut(e,[h])).filter(h=>!!h.file),issueIds:n.get(u.id)||[]})),r=(t.edges||[]).map(u=>({id:u.id,kind:Jo(u.kind),source:u.source,target:u.target,...u.kind&&Jo(u.kind)!==u.kind.trim()?{authoredKind:u.kind}:{},label:u.label,loc:ut(e,u.evidence),basis:u.basis,evidenceLocs:u.evidence.map(h=>ut(e,[h])).filter(h=>!!h.file),issueIds:o.get(u.id)||[]})),a=(t.findings||[]).map((u,h)=>{let p=ut(e,u.evidence),g=(u.evidence||[]).map(v=>({item:e.get(v),role:"Supporting evidence"})).concat((u.counterEvidence||[]).map(v=>({item:e.get(v),role:"Counter-evidence"}))).filter(v=>!!v.item).map(({item:v,role:b})=>({...ut(e,[v.id]),role:b}));return{id:u.id,code:u.id,short:cu(h),severity:u.severity,basis:u.basis,title:u.title,message:u.message,fixHint:u.suggestion||"",loc:p,relatedLocs:g,nodeIds:u.nodeIds||[],edgeIds:u.edgeIds||[],stage:s.find(v=>u.nodeIds.includes(v.id))?.stage||""}}),l=new Map,d=new Map;for(let u of s)l.set(u.stage,(l.get(u.stage)||0)+1);for(let u of a){let h=d.get(u.stage)||Me();h[u.severity]++,d.set(u.stage,h)}let c=(t.phases||[]).map((u,h)=>{let p=d.get(u.id)||Me();return{id:u.id,label:u.label,order:h,present:!0,nodeCount:l.get(u.id)||0,issueCounts:p,maxSeverity:["high","medium","low"].find(g=>p[g])||null}});return{schemaVersion:"workflow-view/1",generator:{name:t.producer.host,version:t.producer.model||uu,rendererSha:t.revision.id,generatedAt:t.verification?.publishedAt||""},workspace:{root:t.title,entrypoints:t.request.entrypoints||[],filesAnalyzed:t.coverage.inspectedFiles.length},stages:c,nodes:s,edges:r,issues:a,diagnostics:t.coverage.limitations.map(u=>({kind:"workflow_limitation",message:u})),stats:{nodes:s.length,edges:r.length,issues:a.reduce((u,h)=>(u[h.severity]++,u),Me()),durationMs:0},authoredCoverage:{status:t.coverage.status,limitations:t.coverage.limitations.length}}}var ti=[["explain","Explain"],["expand","Expand"],["challenge","Challenge"],["trace","Trace"],["custom","Custom…"]];function br(t){let e=xr(t);if(!e)return;let n=wr(e.intent);if(!(!e.open&&n==="explain"&&!e.custom))return{open:e.open,intent:n,custom:e.custom}}var wr=t=>ti.find(([e])=>e===t)?.[0]??"explain";function yr(t){if(!t||typeof t!="object")return null;let e=t;return{open:e.open===!0,intent:wr(e.intent),custom:typeof e.custom=="string"?e.custom.slice(0,500):""}}function xr(t){let e=t.querySelector(".mlv-workflow"),n=t.querySelector(".mlv-workflow__composer"),o=t.querySelector(".mlv-workflow__intent"),i=t.querySelector(".mlv-workflow__custom"),s=t.querySelector(".mlv-workflow__refine");if(!e||!n||!o||!i)return null;let r=t.ownerDocument.activeElement,a=null;r&&r===s?a="refine":r&&n.contains(r)&&(a=r===o?"intent":r===i?"custom":"submit");let l=n.getAttribute("data-selection-kind"),d=n.getAttribute("data-selection-id");return{revision:e.getAttribute("data-revision"),open:!n.hidden,intent:o.value,custom:i.value,focus:a,selection:(l==="node"||l==="edge"||l==="issue")&&d?{kind:l,id:d}:void 0}}var hu=new Set(["checking","missing","unreadable","parse","invalid"]);function Er(t,e){if(!e||e.some(o=>hu.has(o)))return;let n=t.root.querySelector(".mlv-workflow__status");!n||!n.textContent||(n.textContent="",t.saveSoon())}function fu(t,e){let n=t.root.querySelector(".mlv-workflow__composer"),o=t.root.querySelector(".mlv-workflow__refine"),i=t.root.querySelector(".mlv-workflow__custom"),s=t.root.querySelector(".mlv-workflow__status");if(!(!n||!o||!i||!s)){if(e.outcome==="done"){n.hidden=!0,o.setAttribute("aria-expanded","false"),i.value="",s.textContent="";let r=He.get(n)||null;He.delete(n),At(r,o),t.saveSoon();return}n.hidden=!1,o.setAttribute("aria-expanded","true"),s.textContent=e.message||"The refinement prompt was not copied.",t.saveSoon()}}var He=new WeakMap;function pu(t,e){if(!e)return"Whole diagram";let n=t.index;if(e.kind==="node"){let i=n?n.nodeById.get(e.id):void 0;return"Step: "+(i?i.label||i.id:e.id)}if(e.kind==="edge"){let i=n?n.edgeById.get(e.id):void 0;if(!i)return"Connection: "+e.id;let s=n.nodeById.get(i.source),r=n.nodeById.get(i.target);return"Connection: "+(i.label||(s?s.label:i.source)+" → "+(r?r.label:i.target))}let o=n?n.issueById.get(e.id):void 0;return"Finding: "+(o?(o.short?o.short+" · ":"")+o.title:e.id)}function kr(t,e,n){t.root.classList.add("mlv-root--workflow"),t.root.setAttribute("data-workflow-revision",e.revision.id);let o=t.root.querySelector(".mlv-workflow");if(!o){o=f("section","mlv-workflow");let w=t.chrome.header;t.root.insertBefore(o,w.nextSibling)}let i=xr(t.root),s=i??(n?{revision:e.revision.id,open:n.open,intent:n.intent,custom:n.custom,focus:null,selection:void 0}:null),r=!i&&!!n,a=o.querySelector(".mlv-workflow__composer"),l=a&&He.get(a)||null;D(o),o.setAttribute("data-revision",e.revision.id),o.setAttribute("aria-label","Refinement");let d=f("button","mlv-btn mlv-btn--primary mlv-workflow__refine","Refine…");d.type="button",d.title="Copy a refinement prompt for your assistant about the selection or the whole diagram. MLView calls no model.",d.setAttribute("aria-expanded","false"),d.setAttribute("aria-haspopup","dialog");let c=t.chrome.refineSlot;D(c),c.appendChild(d),t.chrome.syncRoving();let u=m(o,f("form","mlv-workflow__composer"));u.hidden=!0,u.setAttribute("role","dialog"),u.setAttribute("aria-modal","true"),u.setAttribute("aria-label","Refine"),l&&He.set(u,l);let h=m(u,f("span","mlv-workflow__selection")),p=m(u,f("select","mlv-input mlv-workflow__intent"));p.setAttribute("aria-label","Refinement intent");for(let[w,S]of ti){let C=p.ownerDocument.createElement("option");C.value=w,C.textContent=S,p.appendChild(C)}let g=m(u,f("input","mlv-input mlv-workflow__custom"));g.type="text",g.maxLength=500,g.placeholder="What should the assistant refine?",g.setAttribute("aria-label","Custom refinement intent"),g.hidden=!0;let v=m(u,f("button","mlv-btn","Copy prompt"));v.type="submit";let b=m(u,f("span","mlv-workflow__status"));b.setAttribute("role","status"),m(u,f("p","mlv-workflow__hint","Copies a prompt for your assistant to paste. MLView calls no model."));let x=()=>t.selection?{kind:t.selection.kind,id:t.selection.id}:void 0,y,k=w=>{y=w,h.textContent=pu(t,w),w?(h.title=w.kind+" id: "+w.id,u.setAttribute("data-selection-kind",w.kind),u.setAttribute("data-selection-id",w.id)):(h.removeAttribute("title"),u.removeAttribute("data-selection-kind"),u.removeAttribute("data-selection-id"))},E=()=>k(x());_(d,"click",()=>{if(u.hidden){let w=d.ownerDocument.activeElement;w&&!u.contains(w)&&w!==d.ownerDocument.body?He.set(u,w):He.delete(u)}u.hidden=!u.hidden,d.setAttribute("aria-expanded",u.hidden?"false":"true"),u.hidden||(E(),p.focus()),t.saveSoon()}),_(u,"keydown",w=>{if(an(u,w)){w.stopPropagation();return}w.key==="Escape"&&(w.preventDefault(),w.stopPropagation(),yn(t,!0))}),_(p,"change",()=>{g.hidden=p.value!=="custom",g.hidden||g.focus(),t.saveSoon()}),_(g,"input",()=>t.saveSoon()),_(u,"submit",w=>{w.preventDefault();let S=p.value,C=g.value.trim();if(S==="custom"&&!C){g.focus();return}b.textContent="";let L={v:1,type:"refineWorkflow",revisionId:e.revision.id,intent:S};S==="custom"&&(L.customText=C),y&&(L.selection=y),t.postRequest(L,I=>fu(t,I))}),s&&(ti.some(([w])=>w===s.intent)&&(p.value=s.intent),g.value=s.custom,g.hidden=p.value!=="custom",s.open&&(u.hidden=!1,d.setAttribute("aria-expanded","true"),s.revision===e.revision.id&&!r?k(s.selection):E()),s.focus==="refine"?d.focus():s.focus==="intent"&&!u.hidden?p.focus():s.focus==="custom"&&!u.hidden&&!g.hidden?g.focus():s.focus==="submit"&&!u.hidden&&v.focus())}function ni(t){let e=t.root.querySelector(".mlv-workflow__composer");return!!e&&!e.hidden}function yn(t,e=!1){let n=t.root.querySelector(".mlv-workflow__composer"),o=t.root.querySelector(".mlv-workflow__refine");if(!n||n.hidden)return!1;n.hidden=!0;let i=He.get(n)||null;return He.delete(n),o&&o.setAttribute("aria-expanded","false"),e&&At(i,o),t.saveSoon(),!0}var xn=class{constructor(e){this.armed=null;this.consumed=!1;this.disposers=[];this.disposers.push(_(e,"mousedown",n=>{n.detail>=2&&(this.armed||this.consumed)&&n.preventDefault()},!0),_(e,"click",n=>this.onClick(n),!0),_(e,"dblclick",n=>{this.consumed&&(n.preventDefault(),n.stopPropagation())},!0))}arm(e,n){!e||e.detail!==1||(this.armed=n,this.consumed=!1)}dispose(){for(let e of this.disposers)e();this.disposers=[],this.armed=null}onClick(e){if(e.detail>=2&&(this.armed||this.consumed)){e.preventDefault(),e.stopPropagation();let n=this.armed;this.armed=null,this.consumed=!0,n&&n();return}this.armed=null,this.consumed=!1}};var zr=Object.defineProperty,mu=(t,e,n)=>e in t?zr(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n,gu=(t,e)=>{for(var n in e)zr(t,n,{get:e[n],enumerable:!0})},vu=(t,e,n)=>mu(t,typeof e!="symbol"?e+"":e,n),Vr={};gu(Vr,{Graph:()=>ve,alg:()=>ui});var bu=Object.defineProperty,qr=(t,e)=>{for(var n in e)bu(t,n,{get:e[n],enumerable:!0})},ci=class{constructor(t){this._isDirected=!0,this._isMultigraph=!1,this._isCompound=!1,this._nodes={},this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={},this._nodeCount=0,this._edgeCount=0,this._defaultNodeLabelFn=()=>{},this._defaultEdgeLabelFn=()=>{},t&&(this._isDirected="directed"in t?t.directed:!0,this._isMultigraph="multigraph"in t?t.multigraph:!1,this._isCompound="compound"in t?t.compound:!1),this._isCompound&&(this._parent={},this._children={},this._children["\0"]={})}isDirected(){return this._isDirected}isMultigraph(){return this._isMultigraph}isCompound(){return this._isCompound}setGraph(t){return this._label=t,this}graph(){return this._label}setDefaultNodeLabel(t){return typeof t!="function"?this._defaultNodeLabelFn=()=>t:this._defaultNodeLabelFn=t,this}nodeCount(){return this._nodeCount}nodes(){return Object.keys(this._nodes)}sources(){return this.nodes().filter(t=>Object.keys(this._in[t]).length===0)}sinks(){return this.nodes().filter(t=>Object.keys(this._out[t]).length===0)}setNodes(t,e){return t.forEach(n=>{e!==void 0?this.setNode(n,e):this.setNode(n)}),this}setNode(t,e){return t in this._nodes?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]="\0",this._children[t]={},this._children["\0"][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)}node(t){return this._nodes[t]}hasNode(t){return t in this._nodes}removeNode(t){if(t in this._nodes){let e=n=>this.removeEdge(this._edgeObjs[n]);delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],this.children(t).forEach(n=>{this.setParent(n)}),delete this._children[t]),Object.keys(this._in[t]).forEach(e),delete this._in[t],delete this._preds[t],Object.keys(this._out[t]).forEach(e),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this}setParent(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(e===void 0)e="\0";else{e+="";for(let n=e;n!==void 0;n=this.parent(n))if(n===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this}parent(t){if(this._isCompound){let e=this._parent[t];if(e!=="\0")return e}}children(t="\0"){if(this._isCompound){let e=this._children[t];if(e)return Object.keys(e)}else{if(t==="\0")return this.nodes();if(this.hasNode(t))return[]}return[]}predecessors(t){let e=this._preds[t];if(e)return Object.keys(e)}successors(t){let e=this._sucs[t];if(e)return Object.keys(e)}neighbors(t){let e=this.predecessors(t);if(e){let n=new Set(e),o=this.successors(t);if(o)for(let i of o)n.add(i);return Array.from(n.values())}}isLeaf(t){var e;let n;return this.isDirected()?n=this.successors(t):n=this.neighbors(t),((e=n?.length)!=null?e:0)===0}filterNodes(t){let e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph()),Object.entries(this._nodes).forEach(([i,s])=>{t(i)&&e.setNode(i,s)}),Object.values(this._edgeObjs).forEach(i=>{e.hasNode(i.v)&&e.hasNode(i.w)&&e.setEdge(i,this.edge(i))});let n={},o=i=>{let s=this.parent(i);return!s||e.hasNode(s)?(n[i]=s,s):s in n?n[s]:o(s)};return this._isCompound&&e.nodes().forEach(i=>e.setParent(i,o(i))),e}setDefaultEdgeLabel(t){return typeof t!="function"?this._defaultEdgeLabelFn=()=>t:this._defaultEdgeLabelFn=t,this}edgeCount(){return this._edgeCount}edges(){return Object.values(this._edgeObjs)}setPath(t,e){return t.reduce((n,o)=>(e!==void 0?this.setEdge(n,o,e):this.setEdge(n,o),o)),this}setEdge(t,e,n,o){let i,s,r,a,l=!1;typeof t=="object"&&t!==null&&"v"in t?(i=t.v,s=t.w,r=t.name,arguments.length===2&&(a=e,l=!0)):(i=t,s=e,r=o,arguments.length>2&&(a=n,l=!0)),i=""+i,s=""+s,r!==void 0&&(r=""+r);let d=Nt(this._isDirected,i,s,r);if(d in this._edgeLabels)return l&&(this._edgeLabels[d]=a),this;if(r!==void 0&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(i),this.setNode(s),this._edgeLabels[d]=l?a:this._defaultEdgeLabelFn(i,s,r);let c=wu(this._isDirected,i,s,r);return i=c.v,s=c.w,Object.freeze(c),this._edgeObjs[d]=c,_r(this._preds[s],i),_r(this._sucs[i],s),this._in[s][d]=c,this._out[i][d]=c,this._edgeCount++,this}edge(t,e,n){let o=arguments.length===1?oi(this._isDirected,t):Nt(this._isDirected,t,e,n);return this._edgeLabels[o]}edgeAsObj(t,e,n){let o=arguments.length===1?this.edge(t):this.edge(t,e,n);return typeof o!="object"||o===null?{label:o}:o}hasEdge(t,e,n){return(arguments.length===1?oi(this._isDirected,t):Nt(this._isDirected,t,e,n))in this._edgeLabels}removeEdge(t,e,n){let o=arguments.length===1?oi(this._isDirected,t):Nt(this._isDirected,t,e,n),i=this._edgeObjs[o];if(i){let s=i.v,r=i.w;delete this._edgeLabels[o],delete this._edgeObjs[o],Sr(this._preds[r],s),Sr(this._sucs[s],r),delete this._in[r][o],delete this._out[s][o],this._edgeCount--}return this}inEdges(t,e){return this.isDirected()?this.filterEdges(this._in[t],t,e):this.nodeEdges(t,e)}outEdges(t,e){return this.isDirected()?this.filterEdges(this._out[t],t,e):this.nodeEdges(t,e)}nodeEdges(t,e){if(t in this._nodes)return this.filterEdges({...this._in[t],...this._out[t]},t,e)}_removeFromParentsChildList(t){delete this._children[this._parent[t]][t]}filterEdges(t,e,n){if(!t)return;let o=Object.values(t);return n?o.filter(i=>i.v===e&&i.w===n||i.v===n&&i.w===e):o}};function _r(t,e){t[e]?t[e]++:t[e]=1}function Sr(t,e){t[e]!==void 0&&!--t[e]&&delete t[e]}function Nt(t,e,n,o){let i=""+e,s=""+n;if(!t&&i>s){let r=i;i=s,s=r}return i+""+s+""+(o===void 0?"\0":o)}function wu(t,e,n,o){let i=""+e,s=""+n;if(!t&&i>s){let a=i;i=s,s=a}let r={v:i,w:s};return o&&(r.name=o),r}function oi(t,e){return Nt(t,e.v,e.w,e.name)}var yu={};qr(yu,{read:()=>_u,write:()=>xu});function xu(t){let e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:Eu(t),edges:ku(t)},n=t.graph();return n!==void 0&&(e.value=structuredClone(n)),e}function Eu(t){return t.nodes().map(e=>{let n=t.node(e),o=t.parent(e),i={v:e};return n!==void 0&&(i.value=n),o!==void 0&&(i.parent=o),i})}function ku(t){return t.edges().map(e=>{let n=t.edge(e),o={v:e.v,w:e.w};return e.name!==void 0&&(o.name=e.name),n!==void 0&&(o.value=n),o})}function _u(t){let e=new ci(t.options);return t.value!==void 0&&e.setGraph(t.value),t.nodes.forEach(n=>{e.setNode(n.v,n.value),n.parent&&e.setParent(n.v,n.parent)}),t.edges.forEach(n=>{e.setEdge({v:n.v,w:n.w,name:n.name},n.value)}),e}var ui={};qr(ui,{CycleException:()=>kn,bellmanFord:()=>Kr,components:()=>Lu,dijkstra:()=>En,dijkstraAll:()=>Cu,findCycles:()=>Tu,floydWarshall:()=>Ou,isAcyclic:()=>Pu,postorder:()=>Fu,preorder:()=>Bu,prim:()=>Du,shortestPaths:()=>ju,tarjan:()=>Xr,topsort:()=>Yr});var Su=()=>1;function Kr(t,e,n,o){return Mu(t,String(e),n||Su,o||function(i){var s;return(s=t.outEdges(i))!=null?s:[]})}function Mu(t,e,n,o){let i={},s,r=0,a=t.nodes(),l=function(u){let h=i[u.v],p=i[u.w];if(!h||!p)return;let g=n(u);h.distance+gt.key)}has(t){return t in this._keyIndices}priority(t){let e=this._keyIndices[t];if(e!==void 0)return this._arr[e].priority}min(){if(this.size()===0)throw new Error("Queue underflow");return this._arr[0].key}add(t,e){let n=this._keyIndices,o=String(t);if(!(o in n)){let i=this._arr,s=i.length;return n[o]=s,i.push({key:o,priority:e}),this._decrease(s),!0}return!1}removeMin(){if(this.size()===0)throw new Error("Queue underflow");this._swap(0,this._arr.length-1);let t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key}decrease(t,e){let n=this._keyIndices[t];if(n===void 0)throw new Error(`Key not found: ${t}`);let o=this._arr[n].priority;if(e>o)throw new Error(`New priority is greater than current priority. Key: ${t} Old: ${o} New: ${e}`);this._arr[n].priority=e,this._decrease(n)}_heapify(t){let e=this._arr,n=2*t,o=n+1,i=t;n>1,!(e[o].priority1;function En(t,e,n,o){let i=function(s){var r;return(r=t.outEdges(s))!=null?r:[]};return Au(t,String(e),n||Iu,o||i)}function Au(t,e,n,o){let i={},s=new Ur,r,a,l=function(d){let c=d.v!==r?d.v:d.w,u=i[c];if(!u)return;let h=n(d),p=a.distance+h;if(h<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+d+" Weight: "+h);p0;){r=s.removeMin();let d=i[r];if(!d||d.distance===Number.POSITIVE_INFINITY)break;a=d,o(r).forEach(l)}return i}function Cu(t,e,n){return t.nodes().reduce(function(o,i){return o[i]=En(t,i,e,n),o},{})}function Xr(t){let e=0,n=[],o={},i=[];function s(r){var a;let l=o[r]={onStack:!0,lowlink:e,index:e++};if(n.push(r),(a=t.successors(r))==null||a.forEach(function(d){if(d in o){let c=o[d];c!=null&&c.onStack&&(l.lowlink=Math.min(l.lowlink,c.index))}else{s(d);let c=o[d];c&&(l.lowlink=Math.min(l.lowlink,c.lowlink))}}),l.lowlink===l.index){let d=[],c;do{c=n.pop();let u=o[c];u&&(u.onStack=!1),d.push(c)}while(r!==c);i.push(d)}}return t.nodes().forEach(function(r){r in o||s(r)}),i}function Tu(t){return Xr(t).filter(function(e){var n;let o=e[0];return o?e.length>1||e.length===1&&((n=t.outEdges(o,o))!=null?n:[]).length>0:!1})}var Ru=()=>1;function Ou(t,e,n){return Hu(t,e||Ru,n||function(o){var i;return(i=t.outEdges(o))!=null?i:[]})}function Hu(t,e,n){let o={},i=t.nodes();return i.forEach(function(s){let r={};o[s]=r,r[s]={distance:0,predecessor:""},i.forEach(function(a){s!==a&&(r[a]={distance:Number.POSITIVE_INFINITY,predecessor:""})}),n(s).forEach(function(a){let l=a.v===s?a.w:a.v,d=e(a);r[l]={distance:d,predecessor:s}})}),i.forEach(function(s){let r=o[s];r&&i.forEach(function(a){let l=o[a];l&&i.forEach(function(d){let c=l[s],u=r[d],h=l[d];if(c&&u&&h){let p=c.distance+u.distance;p{var l;return(l=t.isDirected()?t.successors(a):t.neighbors(a))!=null?l:[]}),r={};return e.forEach(function(a){if(!t.hasNode(a))throw new Error("Graph does not have node: "+a);i=Zr(t,a,n==="post",r,s,o,i)}),i}function Zr(t,e,n,o,i,s,r){return e in o||(o[e]=!0,n||(r=s(r,e)),i(e).forEach(function(a){r=Zr(t,a,n,o,i,s,r)}),n&&(r=s(r,e))),r}function Qr(t,e,n){return Nu(t,e,n,function(o,i){return o.push(i),o},[])}function Fu(t,e){return Qr(t,e,"post")}function Bu(t,e){return Qr(t,e,"pre")}function Du(t,e){var n;let o=new ci,i={},s=new Ur,r;function a(c){let u=c.v===r?c.w:c.v,h=s.priority(u);if(h!==void 0){let p=e(c);p0;){if(r=s.removeMin(),r in i)o.setEdge(r,i[r]);else{if(d)throw new Error("Input graph is not connected: "+t);d=!0}(n=t.nodeEdges(r))==null||n.forEach(a)}return o}function ju(t,e,n,o){return Gu(t,e,n,o??(i=>{var s;return(s=t.outEdges(i))!=null?s:[]}))}function Gu(t,e,n,o){if(n===void 0)return En(t,e,n,o);let i=!1,s=t.nodes();for(let r=0;re.setNode(n,t.node(n))),t.edges().forEach(n=>{let o=e.edge(n.v,n.w)||{weight:0,minlen:1},i=t.edge(n);e.setEdge(n.v,n.w,{weight:o.weight+i.weight,minlen:Math.max(o.minlen,i.minlen)})}),e}function $r(t){let e=new ve({multigraph:t.isMultigraph()}).setGraph(t.graph());return t.nodes().forEach(n=>{t.children(n).length||e.setNode(n,t.node(n))}),t.edges().forEach(n=>{e.setEdge(n,t.edge(n))}),e}function Mr(t,e){let n=t.x,o=t.y,i=e.x-n,s=e.y-o,r=t.width/2,a=t.height/2;if(!i&&!s)throw new Error("Not possible to find intersection inside of the rectangle");let l,d;return Math.abs(s)*r>Math.abs(i)*a?(s<0&&(a=-a),l=a*i/s,d=a):(i<0&&(r=-r),l=r,d=r*s/i),{x:n+l,y:o+d}}function Bt(t){let e=Ft(ea(t)+1).map(()=>[]);return t.nodes().forEach(n=>{let o=t.node(n),i=o.rank;i!==void 0&&(e[i]||(e[i]=[]),e[i][o.order]=n)}),e}function zu(t){let e=t.nodes().map(o=>{let i=t.node(o).rank;return i===void 0?Number.MAX_VALUE:i}),n=xe(Math.min,e);t.nodes().forEach(o=>{let i=t.node(o);Object.hasOwn(i,"rank")&&(i.rank-=n)})}function Vu(t){let e=t.nodes().map(r=>t.node(r).rank).filter(r=>r!==void 0),n=xe(Math.min,e),o=[];t.nodes().forEach(r=>{let a=t.node(r).rank-n;o[a]||(o[a]=[]),o[a].push(r)});let i=0,s=t.graph().nodeRankFactor;Array.from(o).forEach((r,a)=>{r===void 0&&a%s!==0?--i:r!==void 0&&i&&r.forEach(l=>t.node(l).rank+=i)})}function Lr(t,e,n,o){let i={width:0,height:0};return arguments.length>=4&&(i.rank=n,i.order=o),ft(t,"border",i,e)}function qu(t,e=Jr){let n=[];for(let o=0;oJr){let n=qu(e);return t(...n.map(o=>t(...o)))}else return t(...e)}function ea(t){let e=t.nodes().map(n=>{let o=t.node(n).rank;return o===void 0?Number.MIN_VALUE:o});return xe(Math.max,e)}function Ku(t,e){let n={lhs:[],rhs:[]};return t.forEach(o=>{e(o)?n.lhs.push(o):n.rhs.push(o)}),n}function Uu(t,e){let n=Date.now();try{return e()}finally{console.log(t+" time: "+(Date.now()-n)+"ms")}}function ta(t,e){return e()}var Xu=0;function hi(t){let e=++Xu;return t+(""+e)}function Ft(t,e,n=1){e==null&&(e=t,t=0);let o=s=>seo[e]:n=e,Object.entries(t).reduce((o,[i,s])=>(o[i]=n(s,i),o),{})}function Yu(t,e){return t.reduce((n,o,i)=>(n[o]=e[i],n),{})}var Mn="\0";function na(t,e,n){var o,i,s,r,a,l;if(!(t&&e&&n&&e.dummy==="edge"&&n.dummy==="edge"&&e.edgeObj&&n.edgeObj&&t[e.edgeObj.v]&&t[n.edgeObj.v]&&t[e.edgeObj.w]&&t[n.edgeObj.w]))return 0;let d=!0;e.edgeObj.w===n.edgeObj.w&&(d=!1);let c=d?(i=(o=t[e.edgeObj.v])==null?void 0:o.rank)!=null?i:NaN+1:(r=(s=t[e.edgeObj.w])==null?void 0:s.rank)!=null?r:NaN-1,u=Object.entries(t).find(v=>{var b,x;return((b=v[1].edgeObj)==null?void 0:b.v)===e.edgeObj.v&&((x=v[1].edgeObj)==null?void 0:x.w)===e.edgeObj.w&&v[1].rank===c}),h=Object.entries(t).find(v=>{var b,x;return((b=v[1].edgeObj)==null?void 0:b.v)===n.edgeObj.v&&((x=v[1].edgeObj)==null?void 0:x.w)===n.edgeObj.w&&v[1].rank===c});if(!u||!h)return 0;let p=(a=u[1].order)!=null?a:NaN,g=(l=h[1].order)!=null?l:NaN;return isNaN(p-g)?0:p-g}var Zu="3.1.1",Qu=class{constructor(){vu(this,"_sentinel");let t={};t._next=t._prev=t,this._sentinel=t}dequeue(){let t=this._sentinel,e=t._prev;if(e!==t)return Ir(e),e}enqueue(t){let e=this._sentinel;t._prev&&t._next&&Ir(t),t._next=e._next,e._next._prev=t,e._next=t,t._prev=e}toString(){let t=[],e=this._sentinel,n=e._prev;for(;n!==e;)t.push(JSON.stringify(n,$u)),n=n._prev;return"["+t.join(", ")+"]"}};function Ir(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function $u(t,e){if(t!=="_next"&&t!=="_prev")return e}var Ju=Qu,eh=()=>1;function th(t,e){if(t.nodeCount()<=1)return[];let n=oh(t,e||eh);return nh(n.graph,n.buckets,n.zeroIdx).flatMap(o=>t.outEdges(o.v,o.w)||[])}function nh(t,e,n){var o;let i=[],s=e[e.length-1],r=e[0],a;for(;t.nodeCount();){for(;a=r.dequeue();)ii(t,e,n,a);for(;a=s.dequeue();)ii(t,e,n,a);if(t.nodeCount()){for(let l=e.length-2;l>0;--l)if(a=(o=e[l])==null?void 0:o.dequeue(),a){i=i.concat(ii(t,e,n,a,!0)||[]);break}}}return i}function ii(t,e,n,o,i){let s=[],r=i?s:void 0;return(t.inEdges(o.v)||[]).forEach(a=>{let l=t.edge(a),d=t.node(a.v);i&&s.push({v:a.v,w:a.w}),d.out-=l,di(e,n,d)}),(t.outEdges(o.v)||[]).forEach(a=>{let l=t.edge(a),d=a.w,c=t.node(d);c.in-=l,di(e,n,c)}),t.removeNode(o.v),r}function oh(t,e){let n=new ve,o=0,i=0;t.nodes().forEach(a=>{n.setNode(a,{v:a,in:0,out:0})}),t.edges().forEach(a=>{let l=n.edge(a.v,a.w)||0,d=e(a),c=l+d;n.setEdge(a.v,a.w,c);let u=n.node(a.v),h=n.node(a.w);i=Math.max(i,u.out+=d),o=Math.max(o,h.in+=d)});let s=ih(i+o+3).map(()=>new Ju),r=o+1;return n.nodes().forEach(a=>{di(s,r,n.node(a))}),{graph:n,buckets:s,zeroIdx:r}}function di(t,e,n){var o,i,s;n.out?n.in?(s=t[n.out-n.in+e])==null||s.enqueue(n):(i=t[t.length-1])==null||i.enqueue(n):(o=t[0])==null||o.enqueue(n)}function ih(t){let e=[];for(let n=0;n{let i=t.edge(o);t.removeEdge(o),i.forwardName=o.name,i.reversed=!0,t.setEdge(o.w,o.v,i,hi("rev"))});function n(o){return i=>o.edge(i).weight}}function rh(t,e){let n=[],o={},i={};function s(l){Object.hasOwn(i,l)||(i[l]=!0,o[l]=!0,t.outEdges(l).forEach(d=>{Object.hasOwn(o,d.w)?n.push(d):s(d.w)}),delete o[l])}function r(l){var d;Object.hasOwn(i,l)||(i[l]=!0,o[l]=!0,(d=t.outEdges(l))==null||d.forEach(c=>{var u,h;Object.hasOwn(o,c.w)||((u=e.node(l))==null?void 0:u.rank)>((h=e.node(c.w))==null?void 0:h.rank)&&lh(t,c.w,c)?n.push(c):r(c.w)}),delete o[l])}let a=s;return e&&typeof e.node=="function"&&(a=r),t.sources().forEach(a),t.nodes().forEach(a),n}function ah(t){t.edges().forEach(e=>{let n=t.edge(e);if(n.reversed){t.removeEdge(e);let o=n.forwardName;delete n.reversed,delete n.forwardName,t.setEdge(e.w,e.v,n,o)}})}function lh(t,e,n){let o=new Set;function i(s){var r;if(t.sources().includes(s))return!0;o.add(s);for(let a of(r=t.inEdges(s))!=null?r:[])if(!(a.v===n.v&&a.w===n.w)&&!o.has(a.v)&&i(a.v))return!0;return!1}return i(e)}function dh(t){t.graph().dummyChains=[],t.edges().forEach(e=>ch(t,e))}function ch(t,e){let n=e.v,o=t.node(n).rank,i=e.w,s=t.node(i).rank,r=e.name,a=t.edge(e),l=a.labelRank;if(s===o+1)return;t.removeEdge(e);let d,c,u;for(u=0,++o;o{let n=t.node(e),o=n.edgeLabel,i;for(t.setEdge(n.edgeObj,o);n.dummy;)i=t.successors(e)[0],t.removeNode(e),o.points.push({x:n.x,y:n.y}),n.dummy==="edge-label"&&(o.x=n.x,o.y=n.y,o.width=n.width,o.height=n.height),e=i,n=t.node(e)})}function fi(t){let e={};function n(o){let i=t.node(o);if(Object.hasOwn(e,o))return i.rank;e[o]=!0;let s=t.outEdges(o),r=s?s.map(l=>l==null?Number.POSITIVE_INFINITY:n(l.w)-t.edge(l).minlen):[],a=xe(Math.min,r);return a===Number.POSITIVE_INFINITY&&(a=0),i.rank=a}t.sources().forEach(n)}function ht(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}var oa=hh;function hh(t){let e=new ve({directed:!1}),n=t.nodes();if(n.length===0)throw new Error("Graph must have at least one node");let o=n[0],i=t.nodeCount();e.setNode(o,{});let s,r;for(;fh(e,t){let r=s.v,a=o===r?s.w:r;!t.hasNode(a)&&!ht(e,s)&&(t.setNode(a,{}),t.setEdge(o,a,{}),n(a))})}return t.nodes().forEach(n),t.nodeCount()}function ph(t,e){return e.edges().reduce((n,o)=>{let i=Number.POSITIVE_INFINITY;return t.hasNode(o.v)!==t.hasNode(o.w)&&(i=ht(e,o)),ie.node(o).rank+=n)}var{preorder:gh,postorder:vh}=ui,bh=Je;Je.initLowLimValues=mi;Je.initCutValues=pi;Je.calcCutValue=ia;Je.leaveEdge=ra;Je.enterEdge=aa;Je.exchangeEdges=la;function Je(t){t=Wu(t),fi(t);let e=oa(t);mi(e),pi(e,t);let n,o;for(;n=ra(e);)o=aa(e,t,n),la(e,t,n,o)}function pi(t,e){let n=vh(t,t.nodes());n=n.slice(0,n.length-1),n.forEach(o=>wh(t,e,o))}function wh(t,e,n){let o=t.node(n).parent,i=t.edge(n,o);i.cutvalue=ia(t,e,n)}function ia(t,e,n){let o=t.node(n).parent,i=!0,s=e.edge(n,o),r=0;s||(i=!1,s=e.edge(o,n)),r=s.weight;let a=e.nodeEdges(n);return a&&a.forEach(l=>{let d=l.v===n,c=d?l.w:l.v;if(c!==o){let u=d===i,h=e.edge(l).weight;if(r+=u?h:-h,xh(t,n,c)){let p=t.edge(n,c).cutvalue;r+=u?-p:p}}}),r}function mi(t,e){arguments.length<2&&(e=t.nodes()[0]),sa(t,{},1,e)}function sa(t,e,n,o,i){let s=n,r=t.node(o);e[o]=!0;let a=t.neighbors(o);return a&&a.forEach(l=>{Object.hasOwn(e,l)||(n=sa(t,e,n,l,o))}),r.low=s,r.lim=n++,i?r.parent=i:delete r.parent,n}function ra(t){return t.edges().find(e=>t.edge(e).cutvalue<0)}function aa(t,e,n){let o=n.v,i=n.w;e.hasEdge(o,i)||(o=n.w,i=n.v);let s=t.node(o),r=t.node(i),a=s,l=!1;return s.lim>r.lim&&(a=r,l=!0),e.edges().filter(d=>l===Ar(t,t.node(d.v),a)&&l!==Ar(t,t.node(d.w),a)).reduce((d,c)=>ht(e,c)!t.node(i).parent);if(!n)return;let o=gh(t,[n]);o=o.slice(1),o.forEach(i=>{let s=t.node(i).parent,r=e.edge(i,s),a=!1;r||(r=e.edge(s,i),a=!0),e.node(i).rank=e.node(s).rank+(a?r.minlen:-r.minlen)})}function xh(t,e,n){return t.hasEdge(e,n)}function Ar(t,e,n){return n.low<=e.lim&&e.lim<=n.lim}var Eh=kh;function kh(t){let e=t.graph().ranker;if(typeof e=="function")return e(t);switch(e){case"network-simplex":Cr(t);break;case"tight-tree":Sh(t);break;case"longest-path":_h(t);break;case"none":break;default:Cr(t)}}var _h=fi;function Sh(t){fi(t),oa(t)}function Cr(t){bh(t)}var Mh=Lh;function Lh(t){let e=Ah(t),n=t.graph();Array.isArray(n.dummyChains)&&n.dummyChains.forEach(o=>{let i=t.node(o),s=i.edgeObj,r=Ih(t,e,s.v,s.w),a=r.path,l=r.lca,d=0,c=a[d],u=!0;for(;o!==s.w;){if(i=t.node(o),u){for(;(c=a[d])!==l&&t.node(c).maxRankr||a>e[l].lim));let d=l,c=o;for(;(c=t.parent(c))!==d;)s.push(c);return{path:i.concat(s.reverse()),lca:d}}function Ah(t){let e={},n=0;function o(i){let s=n;t.children(i).forEach(o),e[i]={low:s,lim:n++}}return t.children(Mn).forEach(o),e}function Ch(t){let e=ft(t,"root",{},"_root"),n=Th(t),o=Object.values(n),i=xe(Math.max,o)-1,s=2*i+1;t.graph().nestingRoot=e,t.edges().forEach(a=>t.edge(a).minlen*=s);let r=Rh(t)+1;t.children(Mn).forEach(a=>{da(t,e,s,r,i,n,a)}),t.graph().nodeRankFactor=s}function da(t,e,n,o,i,s,r){var a;let l=t.children(r);if(!l.length){r!==e&&t.setEdge(e,r,{weight:0,minlen:n});return}let d=Lr(t,"_bt"),c=Lr(t,"_bb"),u=t.node(r);t.setParent(d,r),u.borderTop=d,t.setParent(c,r),u.borderBottom=c,l.forEach(h=>{var p;da(t,e,n,o,i,s,h);let g=t.node(h),v=g.borderTop?g.borderTop:h,b=g.borderBottom?g.borderBottom:h,x=g.borderTop?o:2*o,y=v!==b?1:i-((p=s[r])!=null?p:0)+1;t.setEdge(d,v,{weight:x,minlen:y,nestingEdge:!0}),t.setEdge(b,c,{weight:x,minlen:y,nestingEdge:!0})}),t.parent(r)||t.setEdge(e,d,{weight:0,minlen:i+((a=s[r])!=null?a:0)})}function Th(t){let e={};function n(o,i){let s=t.children(o);s&&s.length&&s.forEach(r=>n(r,i+1)),e[o]=i}return t.children(Mn).forEach(o=>n(o,1)),e}function Rh(t){return t.edges().reduce((e,n)=>e+t.edge(n).weight,0)}function Oh(t){let e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,t.edges().forEach(n=>{t.edge(n).nestingEdge&&t.removeEdge(n)})}var Hh=Ph;function Ph(t){function e(n){let o=t.children(n),i=t.node(n);if(o.length&&o.forEach(e),i&&Object.hasOwn(i,"minRank")){i.borderLeft=[],i.borderRight=[];for(let s=i.minRank,r=i.maxRank+1;sRr(t.node(e))),t.edges().forEach(e=>Rr(t.edge(e)))}function Rr(t){let e=t.width;t.width=t.height,t.height=e}function Bh(t){t.nodes().forEach(e=>si(t.node(e))),t.edges().forEach(e=>{var n;let o=t.edge(e);(n=o.points)==null||n.forEach(si),Object.hasOwn(o,"y")&&si(o)})}function si(t){t.y=-t.y}function Dh(t){t.nodes().forEach(e=>ri(t.node(e))),t.edges().forEach(e=>{var n;let o=t.edge(e);(n=o.points)==null||n.forEach(ri),Object.hasOwn(o,"x")&&ri(o)})}function ri(t){let e=t.x;t.x=t.y,t.y=e}function jh(t,e=null){let n={},o=t.nodes().filter(d=>!t.children(d).length),i=o.map(d=>t.node(d).rank),s=xe(Math.max,i),r=Ft(s+1).map(()=>[]);function a(d){if(n[d])return;n[d]=!0;let c=t.node(d);r[c.rank].push(d);let u=t.successors(d);u&&[...u].sort((h,p)=>l(h,p)).forEach(a)}o.sort((d,c)=>t.node(d).rank-t.node(c).rank).forEach(a);function l(d,c){let u=t.node(d),h=t.node(c);return na(e,u,h)}return r}function Gh(t,e){let n=0;for(let o=1;oc)),i=e.flatMap(d=>{let c=t.outEdges(d);return c?c.map(u=>({pos:o[u.w],weight:t.edge(u).weight})).sort((u,h)=>u.pos-h.pos):[]}),s=1;for(;s{let c=d.pos+s;a[c]+=d.weight;let u=0;for(;c>0;)c%2&&(u+=a[c+1]),c=c-1>>1,a[c]+=d.weight;l+=d.weight*u}),l}function zh(t,e=[]){return e.map(n=>{let o=t.inEdges(n);if(!o||!o.length)return{v:n};{let i=o.reduce((s,r)=>{let a=t.edge(r),l=t.node(r.v);return{sum:s.sum+a.weight*l.order,weight:s.weight+a.weight}},{sum:0,weight:0});return{v:n,barycenter:i.sum/i.weight,weight:i.weight}}})}function Vh(t,e){let n={};t.forEach((i,s)=>{let r={indegree:0,in:[],out:[],vs:[i.v],i:s};i.barycenter!==void 0&&(r.barycenter=i.barycenter,r.weight=i.weight),n[i.v]=r}),e.edges().forEach(i=>{let s=n[i.v],r=n[i.w];s!==void 0&&r!==void 0&&(r.indegree++,s.out.push(r))});let o=Object.values(n).filter(i=>!i.indegree);return qh(o)}function qh(t){let e=[];function n(i){return s=>{s.merged||(s.barycenter===void 0||i.barycenter===void 0||s.barycenter>=i.barycenter)&&Kh(i,s)}}function o(i){return s=>{s.in.push(i),--s.indegree===0&&t.push(s)}}for(;t.length;){let i=t.pop();e.push(i),i.in.reverse().forEach(n(i)),i.out.forEach(o(i))}return e.filter(i=>!i.merged).map(i=>_n(i,["vs","i","barycenter","weight"]))}function Kh(t,e){let n=0,o=0;t.weight&&(n+=t.barycenter*t.weight,o+=t.weight),e.weight&&(n+=e.barycenter*e.weight,o+=e.weight),t.vs=e.vs.concat(t.vs),t.barycenter=n/o,t.weight=o,t.i=Math.min(e.i,t.i),e.merged=!0}function Uh(t,e,n,o,i){let s={},r=null,a=null,l=i;typeof e=="boolean"?(l=e,s={}):e&&(s=e,r=n??null,a=o??null);let d=Ku(t,x=>Object.hasOwn(x,"barycenter")),c=d.lhs,u=d.rhs.sort((x,y)=>y.i-x.i),h=[],p=0,g=0,v=0;c.sort(Xh(a,r,!!l));for(let[x,y]of Object.entries(s)){let k=c.findIndex(E=>E.vs[0]===x);c.splice(k+1,0,y)}v=Or(h,u,v),c.forEach(x=>{v+=x.vs.length,h.push(x.vs),p+=x.barycenter*x.weight,g+=x.weight,v=Or(h,u,v)});let b={vs:h.flat(1)};return g&&(b.barycenter=p/g,b.weight=g),b}function Or(t,e,n){let o;for(;e.length&&(o=e[e.length-1]).i<=n;)e.pop(),t.push(o.vs),n++;return n}function Xh(t,e,n){return(o,i)=>{if(o.barycenteri.barycenter)return 1;if(t&&(typeof o.vs[0]=="string"||typeof i.vs[0]=="string")){let s=t.node(o.vs[0]),r=t.node(i.vs[0]),a=na(e,s,r);if(a!==0)return a}return n?i.i-o.i:o.i-i.i}}function ua(t,e,n,o,i){var s,r,a,l,d,c,u,h,p,g,v;let b=null,x=i;typeof o=="boolean"?(x=o,b=null):o!==void 0&&(b=o);let y=t.children(e),k=t.node(e),E=k?k.borderLeft:void 0,w=k?k.borderRight:void 0,S={};E&&(y=y.filter(P=>P!==E&&P!==w));let C=zh(t,y);C.forEach(P=>{if(t.children(P.v).length){let{result:N}=ua(t,P.v,n,b,x);S[P.v]=N,Object.hasOwn(N,"barycenter")&&Zh(P,N)}});let L=Vh(C,n);Yh(L,S);let I={},F=!1;for(let P=0;P{n.vs=n.vs.flatMap(o=>e[o]?e[o].vs:o)})}function Zh(t,e){t.barycenter!==void 0?(t.barycenter=(t.barycenter*t.weight+e.barycenter*e.weight)/(t.weight+e.weight),t.weight+=e.weight):(t.barycenter=e.barycenter,t.weight=e.weight)}function Qh(t,e,n,o){o||(o=t.nodes());let i=$h(t),s=new ve({compound:!0}).setGraph({root:i}).setDefaultNodeLabel(r=>t.node(r));return o.forEach(r=>{let a=t.node(r),l=t.parent(r);if(a.rank===e||a.minRank<=e&&e<=a.maxRank){s.setNode(r),s.setParent(r,l||i);let d=t[n](r);d&&d.forEach(c=>{let u=c.v===r?c.w:c.v,h=s.edge(u,r),p=h!==void 0?h.weight:0;s.setEdge(u,r,{weight:t.edge(c).weight+p})}),Object.hasOwn(a,"minRank")&&s.setNode(r,{borderLeft:a.borderLeft[e],borderRight:a.borderRight[e]})}}),s}function $h(t){let e;for(;t.hasNode(e=hi("_root")););return e}function Jh(t,e,n){let o={},i;n.forEach(s=>{let r=t.parent(s),a,l;for(;r;){if(a=t.parent(r),a?(l=o[a],o[a]=r):(l=i,i=r),l&&l!==r){e.setEdge(l,r);return}r=a}})}function ha(t,e={},n=null){if(typeof e.customOrder=="function"){e.customOrder(t,ha);return}let o=ea(t),i=Hr(t,Ft(1,o+1),"inEdges"),s=Hr(t,Ft(o-1,-1,-1),"outEdges"),r=jh(t,n);if(Pr(t,r),e.disableOptimalOrderHeuristic)return;let a=Number.POSITIVE_INFINITY,l,d=e.constraints||[];for(let c=0,u=0;u<4;++c,++u){ef(c%2?i:s,c%4>=2,d,n),r=Bt(t);let h=Gh(t,r);h{o.has(s)||o.set(s,[]),o.get(s).push(r)};for(let s of t.nodes()){let r=t.node(s);if(typeof r.rank=="number"&&i(r.rank,s),typeof r.minRank=="number"&&typeof r.maxRank=="number")for(let a=r.minRank;a<=r.maxRank;a++)a!==r.rank&&i(a,s)}return e.map(function(s){return Qh(t,s,n,o.get(s)||[])})}function ef(t,e,n,o){let i=!0,s=new ve;t.forEach(function(r){n.forEach(c=>s.setEdge(c.left,c.right));let a=r.graph().root,{result:l,usedBias:d}=ua(r,a,s,o,i);e&&d&&(i=!i),l.vs.forEach((c,u)=>r.node(c).order=u),Jh(r,s,l.vs)})}function Pr(t,e){Object.values(e).forEach(n=>n.forEach((o,i)=>t.node(o).order=i))}function tf(t,e){let n={};function o(i,s){let r=0,a=0,l=i.length,d=s[s.length-1];return s.forEach((c,u)=>{let h=of(t,c),p=h?t.node(h).order:l;(h||c===d)&&(s.slice(a,u+1).forEach(g=>{let v=t.predecessors(g);v&&v.forEach(b=>{let x=t.node(b),y=x.order;(y{let u=s[c];if(u!==void 0&&t.node(u).dummy){let h=t.predecessors(u);h&&h.forEach(p=>{if(p===void 0)return;let g=t.node(p);g.dummy&&(g.orderd)&&fa(n,p,u)})}})}function i(s,r){let a=-1,l=-1,d=0;return r.forEach((c,u)=>{if(t.node(c).dummy==="border"){let h=t.predecessors(c);if(h&&h.length){let p=h[0];if(p===void 0)return;l=t.node(p).order,o(r,d,u,a,l),d=u,a=l}}o(r,d,r.length,l,s.length)}),r}return e.length&&e.reduce(i),n}function of(t,e){if(t.node(e).dummy){let n=t.predecessors(e);if(n)return n.find(o=>t.node(o).dummy)}}function fa(t,e,n){if(e>n){let i=e;e=n,n=i}let o=t[e];o||(t[e]=o={}),o[n]=!0}function sf(t,e,n){if(e>n){let i=e;e=n,n=i}let o=t[e];return o!==void 0&&Object.hasOwn(o,n)}function rf(t,e,n,o,i){let s={},r={},a={};return e.forEach(l=>{l.forEach((d,c)=>{s[d]=d,r[d]=d,a[d]=c})}),e.forEach(l=>{let d=-1,c=-1,u=!1,h=l,p=l.findIndex(g=>i?.includes(g)||Nr(g,t,i));p>0&&(h=[l[p],...l.slice(0,p),...l.slice(p+1)],u=!0),h.forEach(g=>{var v;let b=o(g);if(b&&b.length){i!=null&&i.includes(g)&&(b=b.filter(k=>Nr(k,t,i)));let x=b.sort((k,E)=>{let w=a[k],S=a[E];return(w!==void 0?w:0)-(S!==void 0?S:0)}),y=(x.length-1)/2;for(let k=Math.floor(y),E=Math.ceil(y);k<=E;++k){let w=x[k];if(w===void 0)continue;let S=a[w];if(S!==void 0&&r[g]===g&&d{var x;let y=(x=s[b.v])!=null?x:0,k=r.edge(b);return Math.max(v,y+(k!==void 0?k:0))},0):s[p]=0}function c(p){let g=r.outEdges(p),v=Number.POSITIVE_INFINITY;g&&(v=g.reduce((x,y)=>{let k=s[y.w],E=r.edge(y);return Math.min(x,(k!==void 0?k:0)-(E!==void 0?E:0))},Number.POSITIVE_INFINITY));let b=t.node(p);v!==Number.POSITIVE_INFINITY&&b.borderType!==a&&(s[p]=Math.max(s[p]!==void 0?s[p]:0,v))}function u(p){return r.predecessors(p)||[]}function h(p){return r.successors(p)||[]}return l(d,u),l(c,h),Object.keys(o).forEach(p=>{var g;let v=n[p];v!==void 0&&(s[p]=(g=s[v])!=null?g:0)}),s}function lf(t,e,n,o){let i=new ve,s=t.graph(),r=ff(s.nodesep,s.edgesep,o);return e.forEach(a=>{let l;a.forEach(d=>{let c=n[d];if(c!==void 0){if(i.setNode(c),l!==void 0){let u=n[l];if(u!==void 0){let h=i.edge(u,c);i.setEdge(u,c,Math.max(r(t,d,l),h||0))}}l=d}})}),i}function df(t,e){return Object.values(e).reduce((n,o)=>{let i=Number.NEGATIVE_INFINITY,s=Number.POSITIVE_INFINITY;Object.entries(o).forEach(([a,l])=>{let d=pf(t,a)/2;i=Math.max(l+d,i),s=Math.min(l-d,s)});let r=i-s;return r{["l","r"].forEach(r=>{let a=s+r,l=t[a];if(!l||l===e)return;let d=Object.values(l),c=o-xe(Math.min,d);r!=="l"&&(c=i-xe(Math.max,d)),c&&(t[a]=Sn(l,u=>u+c))})})}function uf(t,e=void 0){let n=t.ul;return n?Sn(n,(o,i)=>{var s,r;if(e){let l=e.toLowerCase(),d=t[l];if(d&&d[i]!==void 0)return d[i]}let a=Object.values(t).map(l=>{let d=l[i];return d!==void 0?d:0}).sort((l,d)=>l-d);return(((s=a[1])!=null?s:0)+((r=a[2])!=null?r:0))/2}):{}}function hf(t,e){let n=Bt(t),o=Object.assign(tf(t,n),nf(t,n)),i={},s;["u","d"].forEach(a=>{s=a==="u"?n:Object.values(n).reverse(),["l","r"].forEach(l=>{l==="r"&&(s=s.map(u=>Object.values(u).reverse()));let d=rf(t,s,o,u=>(a==="u"?t.predecessors(u):t.successors(u))||[],e),c=af(t,s,d.root,d.align,l==="r");l==="r"&&(c=Sn(c,u=>-u)),i[a+l]=c})});let r=df(t,i);return cf(i,r),uf(i,t.graph().align)}function ff(t,e,n){return(o,i,s)=>{let r=o.node(i),a=o.node(s),l=0,d;if(l+=r.width/2,Object.hasOwn(r,"labelpos"))switch(r.labelpos.toLowerCase()){case"l":d=-r.width/2;break;case"r":d=r.width/2;break}if(d&&(l+=n?d:-d),d=void 0,l+=(r.dummy?e:t)/2,l+=(a.dummy?e:t)/2,l+=a.width/2,Object.hasOwn(a,"labelpos"))switch(a.labelpos.toLowerCase()){case"l":d=a.width/2;break;case"r":d=-a.width/2;break}return d&&(l+=n?d:-d),l}}function pf(t,e){return t.node(e).width}function Nr(t,e,n){var o;if(!n)return!1;let i=(o=e.node(t))==null?void 0:o.edgeObj;if(!i||e.node(t).edgeLabel.reversed)return!1;let s=n.indexOf(i?.v),r=n.indexOf(i?.w);return s!==-1&&r!==-1&&s===(r+1)%n.length||s===(r-1)%n.length}function mf(t,e){t=$r(t),gf(t),Object.entries(hf(t,e)).forEach(([n,o])=>t.node(n).x=o)}function gf(t){let e=Bt(t),n=t.graph(),o=n.ranksep,i=n.rankalign,s=0;e.forEach(r=>{let a=r.reduce((l,d)=>{var c;let u=(c=t.node(d).height)!=null?c:0;return l>u?l:u},0);r.forEach(l=>{let d=t.node(l);i==="top"?d.y=s+d.height/2:i==="bottom"?d.y=s+a-d.height/2:d.y=s+a/2}),s+=a+o})}var Fr=new WeakMap;function vf(t,e={}){return pa(t,ta,e),t}function Br(t,e,n){let o=e;for(;o!==void 0;){let i=t.parent(o);if(i===n)return o;o=i}}function pa(t,e,n){var o;let i=t.nodes().filter(y=>t.children(y).length),s={};i.forEach(y=>{let k=t.node(y);if(k&&k.rankdir){let E=new ve({multigraph:!0,compound:!0});E.setGraph({rankdir:k.rankdir});let w=t.children(y);w.forEach(z=>{let j={...t.node(z)};E.setNode(z,j);let V=t.parent(z);V&&V!==y&&w.includes(V)&&E.setParent(z,V)});let S=new Set;t.edges().forEach(z=>{let j=Br(t,z.v,y),V=Br(t,z.w,y);if(j&&V&&j!==V){let $=`${j}\0${V}`;S.has($)||(S.add($),E.setEdge(j,V,{...t.edge(z)}))}}),pa(E,e,n);let C=Wr(E);Dr(C,e,n,null),jr(E,C);let L=1/0,I=1/0,F=-1/0,A=-1/0;E.nodes().forEach(z=>{if(z===y)return;let j=E.node(z);j&&typeof j.x=="number"&&typeof j.y=="number"&&typeof j.width=="number"&&typeof j.height=="number"&&(L=Math.min(L,j.x-j.width/2),F=Math.max(F,j.x+j.width/2),I=Math.min(I,j.y-j.height/2),A=Math.max(A,j.y+j.height/2))}),(!isFinite(L)||!isFinite(I)||!isFinite(F)||!isFinite(A))&&(L=I=0,F=A=0);let P=F-L,N=A-I;s[y]={minX:L,minY:I,maxX:F,maxY:A,width:P,height:N,offsetX:L,offsetY:I},k._dagreClusterSubgraph=E}});let r=[],a=y=>{let k=[],E=(t.children(y)||[]).filter(w=>w!==y);for(;E.length>0;){let w=E.shift();k.push(w),(t.children(w)||[]).filter(S=>S!==w).forEach(S=>E.push(S))}return k},l=new Map;i.forEach(y=>{let k=t.node(y);k&&k.rankdir&&s[y]&&l.set(y,(t.children(y)||[]).filter(E=>E!==y))});let d=new Set([...l.values()].flat()),c=new Map;l.forEach((y,k)=>{d.has(k)||c.set(k,a(k))});let u=new Set([...c.values()].flat()),h=y=>{for(let[k,E]of c)if(E.includes(y))return k;return y},p=[];t.edges().forEach(y=>{(u.has(y.v)||u.has(y.w))&&p.push({edge:y,label:t.edge(y)})});let g=new Map;u.forEach(y=>{let k=t.parent(y);g.set(y,typeof k=="string"?k:void 0)}),c.forEach((y,k)=>{let E=t.node(k),w=[];y.forEach(L=>{let I=t.node(L);I&&(w.push({id:L,node:I,parent:g.get(L)}),t.removeNode(L))});let S=p.filter(({edge:L})=>y.includes(L.v)||y.includes(L.w)),C=s[k];E&&(r.push({clusterId:k,subgraph:E._dagreClusterSubgraph,bounds:C,children:y,removedNodes:w,removedEdges:S}),E.width=C.width,E.height=C.height)});let v=new Set;p.forEach(({edge:y,label:k})=>{let E=h(y.v),w=h(y.w);if(E!==w&&t.hasNode(E)&&t.hasNode(w)){let S=`${E}\0${w}`;v.has(S)||(v.add(S),t.setEdge(E,w,{...k,width:0,height:0}))}});let b=Wr(t),x=Dr(b,e,n,(o=Fr.get(t))!=null?o:null);Fr.set(t,x),jr(t,b),v.forEach(y=>{let k=y.indexOf("\0"),E=y.slice(0,k),w=y.slice(k+1);t.hasEdge(E,w)&&t.removeEdge(E,w)}),r.forEach(({clusterId:y,subgraph:k,bounds:E,removedNodes:w,removedEdges:S})=>{var C,L;let I=t.node(y),F=(C=I?.x)!=null?C:0,A=(L=I?.y)!=null?L:0,P=(E.minX+E.maxX)/2,N=(E.minY+E.maxY)/2;w.forEach(({id:z,node:j,parent:V})=>{t.setNode(z,j),V!==void 0&&t.setParent(z,V)}),S.forEach(({edge:z,label:j})=>{t.setEdge(z,j)}),k.nodes().forEach(z=>{if(z===y)return;let j=k.node(z),V=t.node(z);V&&j&&typeof j.x=="number"&&typeof j.y=="number"&&(V.x=F+(j.x-P),V.y=A+(j.y-N))}),delete I._dagreClusterSubgraph}),i.forEach(y=>{var k,E;let w=t.node(y),S=s[y];if(w&&w.rankdir&&w._dagreClusterSubgraph&&S){let C=w._dagreClusterSubgraph,L=(k=w.x)!=null?k:0,I=(E=w.y)!=null?E:0,F=(S.minX+S.maxX)/2,A=(S.minY+S.maxY)/2;C.nodes().forEach(P=>{if(P===y)return;let N=C.node(P),z=t.node(P);if(z&&N&&typeof N.x=="number"&&typeof N.y=="number"){let j=N.x-F,V=N.y-A;z.x=L+j,z.y=I+V}}),delete w._dagreClusterSubgraph}})}function Dr(t,e,n,o=null){var i,s;let r=n?.useDynamic!==!1,a=r&&(i=o?.graph)!=null?i:null,l=r&&(s=o?.rawNodes)!=null?s:null;e(" makeSpaceForEdgeLabels",()=>Sf(t)),e(" removeSelfEdges",()=>Hf(t)),e(" acyclic",()=>sh(t,a)),e(" nestingGraph.run",()=>Ch(t)),e(" rank",()=>Eh($r(t))),e(" injectEdgeLabelProxies",()=>Mf(t)),e(" removeEmptyRanks",()=>Vu(t)),e(" nestingGraph.cleanup",()=>Oh(t)),e(" normalizeRanks",()=>zu(t)),e(" assignRankMinMax",()=>Lf(t)),e(" removeEdgeLabelProxies",()=>If(t)),e(" normalize.run",()=>dh(t)),e(" parentDummyChains",()=>Mh(t)),e(" addBorderSegments",()=>Hh(t)),e(" order",()=>ha(t,n,l)),e(" insertSelfEdges",()=>Pf(t)),e(" adjustCoordinateSystem",()=>Nh(t)),e(" position",()=>mf(t,n.corePath)),e(" positionSelfEdges",()=>Nf(t));let d=JSON.parse(JSON.stringify(t._nodes));return e(" removeBorderNodes",()=>Of(t)),e(" normalize.undo",()=>uh(t)),e(" fixupEdgeLabelCoords",()=>Tf(t)),e(" undoCoordinateSystem",()=>Fh(t)),e(" translateGraph",()=>Af(t)),e(" assignNodeIntersects",()=>Cf(t)),e(" reversePoints",()=>Rf(t)),e(" acyclic.undo",()=>ah(t)),{graph:t,rawNodes:d}}function jr(t,e){t.nodes().forEach(n=>{let o=t.node(n),i=e.node(n);o&&(o.x=i.x,o.y=i.y,o.order=i.order,o.rank=i.rank,e.children(n).length&&(o.width=i.width,o.height=i.height))}),t.edges().forEach(n=>{let o=t.edge(n),i=e.edge(n);o.points=i.points,Object.hasOwn(i,"x")&&(o.x=i.x,o.y=i.y)}),t.graph().width=e.graph().width,t.graph().height=e.graph().height}var bf=["nodesep","edgesep","ranksep","marginx","marginy"],wf={ranksep:50,edgesep:20,nodesep:50,rankdir:"TB",rankalign:"center"},yf=["acyclicer","ranker","rankdir","align","rankalign"],xf=["width","height","rank"],Gr={width:0,height:0},Ef=["minlen","weight","width","height","labeloffset"],kf={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},_f=["labelpos"];function Wr(t){let e=new ve({multigraph:!0,compound:!0}),n=li(t.graph());return e.setGraph(Object.assign({},wf,ai(n,bf),_n(n,yf))),t.nodes().forEach(o=>{let i=li(t.node(o)),s=ai(i,xf);Object.keys(Gr).forEach(a=>{s[a]===void 0&&(s[a]=Gr[a])}),e.setNode(o,s);let r=t.parent(o);r!==void 0&&e.setParent(o,r)}),t.edges().forEach(o=>{let i=li(t.edge(o));e.setEdge(o,Object.assign({},kf,ai(i,Ef),_n(i,_f)))}),e}function Sf(t){let e=t.graph();e.ranksep/=2,t.edges().forEach(n=>{var o;let i=t.edge(n);i.minlen*=2,((o=i.labelpos)!=null?o:"r").toLowerCase()!=="c"&&(e.rankdir==="TB"||e.rankdir==="BT"?i.width+=i.labeloffset:i.height+=i.labeloffset)})}function Mf(t){t.edges().forEach(e=>{let n=t.edge(e);if(n.width&&n.height){let o=t.node(e.v),i={rank:(t.node(e.w).rank-o.rank)/2+o.rank,e};ft(t,"edge-proxy",i,"_ep")}})}function Lf(t){let e=0;t.nodes().forEach(n=>{let o=t.node(n);o.borderTop&&(o.minRank=t.node(o.borderTop).rank,o.maxRank=t.node(o.borderBottom).rank,e=Math.max(e,o.maxRank))}),t.graph().maxRank=e}function If(t){t.nodes().forEach(e=>{let n=t.node(e);if(n.dummy==="edge-proxy"){let o=n;t.edge(o.e).labelRank=n.rank,t.removeNode(e)}})}function Af(t){let e=Number.POSITIVE_INFINITY,n=0,o=Number.POSITIVE_INFINITY,i=0,s=t.graph(),r=s.marginx||0,a=s.marginy||0;function l(d){let c=d.x,u=d.y,h=d.width,p=d.height;e=Math.min(e,c-h/2),n=Math.max(n,c+h/2),o=Math.min(o,u-p/2),i=Math.max(i,u+p/2)}t.nodes().forEach(d=>l(t.node(d))),t.edges().forEach(d=>{let c=t.edge(d);Object.hasOwn(c,"x")&&l(c)}),e-=r,o-=a,t.nodes().forEach(d=>{let c=t.node(d);c.x-=e,c.y-=o}),t.edges().forEach(d=>{let c=t.edge(d);c.points.forEach(u=>{u.x-=e,u.y-=o}),Object.hasOwn(c,"x")&&(c.x-=e),Object.hasOwn(c,"y")&&(c.y-=o)}),s.width=n-e+r,s.height=i-o+a}function Cf(t){t.edges().forEach(e=>{if(e.v===e.w)return;let n=t.edge(e),o=t.node(e.v),i=t.node(e.w),s,r;n.points?(s=n.points[0],r=n.points[n.points.length-1]):(n.points=[],s=i,r=o),n.points.unshift(Mr(o,s)),n.points.push(Mr(i,r))})}function Tf(t){t.edges().forEach(e=>{let n=t.edge(e);if(Object.hasOwn(n,"x"))switch((n.labelpos==="l"||n.labelpos==="r")&&(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset;break}})}function Rf(t){t.edges().forEach(e=>{let n=t.edge(e);n.reversed&&n.points.reverse()})}function Of(t){t.nodes().forEach(e=>{if(t.children(e).length){let n=t.node(e),o=t.node(n.borderTop),i=t.node(n.borderBottom),s=t.node(n.borderLeft[n.borderLeft.length-1]),r=t.node(n.borderRight[n.borderRight.length-1]);n.width=Math.abs(r.x-s.x),n.height=Math.abs(i.y-o.y),n.x=s.x+n.width/2,n.y=o.y+n.height/2}}),t.nodes().forEach(e=>{t.node(e).dummy==="border"&&t.removeNode(e)})}function Hf(t){t.edges().forEach(e=>{if(e.v===e.w){let n=t.node(e.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e,label:t.edge(e)}),t.removeEdge(e)}})}function Pf(t){Bt(t).forEach(e=>{let n=0;e.forEach((o,i)=>{let s=t.node(o);typeof s.rank!="number"&&(s.rank=0),s.order=i+n,(s.selfEdges||[]).forEach(r=>{ft(t,"selfedge",{width:r.label.width,height:r.label.height,rank:s.rank,order:i+ ++n,e:r.e,edgeLabel:r.label},"_se"),(!Array.isArray(r.label.points)||r.label.points.length!==7)&&(r.label.points=[{x:0,y:-10},{x:0,y:-10},{x:0,y:0},{x:0,y:10},{x:0,y:10},{x:0,y:0},{x:0,y:0}])}),delete s.selfEdges})})}function Nf(t){t.nodes().forEach(e=>{let n=t.node(e),o=i=>typeof i=="number"&&isFinite(i);if(n.dummy==="selfedge"){let i=n,s=t.node(i.e.v),r=o(s?.x)?s.x:0,a=o(s?.y)?s.y:0,l=o(s?.width)?s.width:0,d=o(s?.height)?s.height:0,c=o(n.x)?n.x:r,u=o(n.y)?n.y:a,h=l/2,p=d/2;i.edgeLabel.points=[{x:c+h,y:u-p},{x:c+h,y:u-p},{x:c,y:u},{x:c-h,y:u+p},{x:c-h,y:u+p},{x:c,y:u},{x:c,y:u}],i.edgeLabel.x=c,i.edgeLabel.y=u,t.setEdge(i.e,i.edgeLabel),t.removeNode(e)}else n&&Array.isArray(n.selfEdges)&&n.selfEdges.forEach(i=>{if(!Array.isArray(i.label.points)||i.label.points.length!==7){let s=o(n.x)?n.x:0,r=o(n.y)?n.y:0,a=o(n.width)?n.width:0,l=o(n.height)?n.height:0,d=a/2,c=l/2;i.label.points=[{x:s+d,y:r-c},{x:s+d,y:r-c},{x:s,y:r},{x:s-d,y:r+c},{x:s-d,y:r+c},{x:s,y:r},{x:s,y:r}]}})})}function ai(t,e){return Sn(_n(t,e),Number)}function li(t){let e={};return t&&Object.entries(t).forEach(([n,o])=>{typeof n=="string"&&(n=n.toLowerCase()),e[n]=o}),e}function Ff(t){let e=Bt(t),n=new ve({compound:!0,multigraph:!0}).setGraph({});return t.nodes().forEach(o=>{n.setNode(o,{label:o}),n.setParent(o,"layer"+t.node(o).rank)}),t.edges().forEach(o=>n.setEdge(o.v,o.w,{},o.name)),e.forEach((o,i)=>{let s="layer"+i;n.setNode(s,{rank:"same"}),o.reduce((r,a)=>(n.setEdge(r,a,{style:"invis"}),a))}),n}var Bf={graphlib:Vr,version:Zu,layout:vf,debug:Ff,util:{time:Uu,notime:ta}},gi=Bf;var zf=58,Vf=13*.55;function wi(t,e,n=3){let o=e+"|"+n+"|"+t,i=Ln.get(o);if(i)return i;let s=Kf(t,e,n);return Ln.size>=qf&&Ln.clear(),Ln.set(o,s),s}var qf=8192,Ln=new Map;function Kf(t,e,n){let o=Math.max(4,Math.floor((e-zf)/Vf)),i=String(t||"").trim().split(/\s+/).filter(Boolean),s=[],r="",a=!1;for(let l=0;lo;){let u=r?o-r.length-1:o;if(u>=4&&(r=r?r+" "+d.slice(0,u):d.slice(0,u),d=d.slice(u)),s.push(r),r="",s.length===n)break}if(s.length===n){a=!0;break}let c=r?r+" "+d:d;if(c.length<=o){r=c;continue}if(s.push(r),r=d,s.length===n){a=!0;break}}if(!a&&r)s.push(r);else if(a&&s.length){let l=s[s.length-1];s[s.length-1]=(l.length>=o?l.slice(0,o-1):l)+"…"}return s.length>n&&(s.length=n),s.length?s:[""]}function yi(t,e){return wi(t.label||t.qualname||t.id,e).length}function Uf(t=!1){return t}function xi(t){let e=t.loc;return!!(e&&(e.file||e.line))}function Xf(t,e=!1){let n=xi(t)||e?72:60,o=(yi(t,ga(t,e))-1)*18;return n+o+(Uf(e)?26:0)}function ga(t,e=!1){return e?248:t.kind==="model"||t.kind==="entrypoint"||t.level==="stage"?248:t.kind==="layer"?176:216}function Ei(t,e=!1){return{w:ga(t,e),h:Xf(t,e)}}function va(t,e){let n=[];for(let o of e){let i=t.node(o);i&&isFinite(i.x)&&isFinite(i.y)&&n.push({id:o,n:i})}return n}function ba(t){let e=new Map;for(let n of t){let o=Math.round(n.n.x*100)/100,i=e.get(o);i?i.push(n):e.set(o,[n])}return{keys:Array.from(e.keys()).sort((n,o)=>n-o),columns:e}}function wa(t,e){let n=va(t,e);if(n.length<2)return;let{keys:o,columns:i}=ba(n),s=0;for(let r of o){let a=i.get(r);for(let v of a)v.n.x+=s;if(a.length<2)continue;a.sort((v,b)=>v.n.y-b.n.y||(v.idb.id?1:0));let l=0,d=0;for(let v of a)l+=v.n.height,d=Math.max(d,v.n.width);if(l+=(a.length-1)*hn,l<=Go)continue;let c=Math.ceil(l/Go),u=Math.max(1,Math.ceil(a.length/c)),h=Math.min.apply(null,a.map(v=>v.n.y-v.n.height/2)),p=0,g=h;for(let v=0;v0&&v%u===0&&(p++,g=h);let b=a[v];b.n.x=r+s+p*(d+Wo),b.n.y=g+b.n.height/2,g+=b.n.height+hn}s+=p*(d+Wo)}}function ya(t,e){let n=va(t,e);if(n.length<2)return!1;let{keys:o,columns:i}=ba(n);if(o.length<2)return!1;let s=[];for(let h of o){let p=i.get(h),g=1/0,v=-1/0;for(let b of p)g=Math.min(g,b.n.x-b.n.width/2),v=Math.max(v,b.n.x+b.n.width/2);s.push({members:p,left:g,right:v})}let r=s[0].left;if(s[s.length-1].right-r<=zo)return!1;let a=[],l=[],d=r;for(let h of s)l.length>0&&h.right-d>zo&&(a.push(l),l=[],d=h.left),l.push(h);if(l.length>0&&a.push(l),a.length<2)return!1;let c=1/0;for(let h of n)c=Math.min(c,h.n.y-h.n.height/2);let u=c;for(let h of a){let p=1/0;for(let x of h)for(let y of x.members)p=Math.min(p,y.n.y-y.n.height/2);let g=r-h[0].left,v=u-p,b=u;for(let x of h)for(let y of x.members)y.n.x+=g,y.n.y+=v,b=Math.max(b,y.n.y+y.n.height/2);u=b+hn}return!0}function mt(t){return t.kind==="control"&&t.subkind==="back"}function _a(t,e){let n=new Map,o=[],i=new Map,s=new Map;for(let w of t.graph.nodes||[])s.set(w.id,w.stage);let r=(t.graph.edges||[]).filter(w=>!mt(w)),a=new Set;for(let w of t.graph.edges||[]){if(!mt(w))continue;let S=t.nodeById.get(w.source);S&&a.add(S.stage);let C=t.nodeById.get(w.target);C&&a.add(C.stage)}let l=new Set;{let w=new Map;t.lanes.forEach((S,C)=>w.set(S.id,C));for(let S of t.graph.edges||[]){if(mt(S))continue;let C=t.nodeById.get(S.source),L=t.nodeById.get(S.target);if(!C||!L)continue;let I=w.get(C.stage),F=w.get(L.stage);I===void 0||F===void 0||Math.abs(I-F)>1&&l.add(C.stage+">"+L.stage)}}let d=l.size,c=d>0,u=fn+d*Ko+qo,h=c?Math.min(u,nr):0,p=Math.max(0,h-fn-qo),g=c?Math.max(or,Math.min(Ko,Jf(p/Math.max(1,d)))):0,v=c?Math.max(1,Math.min(d,Math.floor(p/g+1e-6))):0,b=32+h,x=32,y=320;t.lanes.forEach((w,S)=>{i.set(w.id,S);let C=t.roots(w.id).filter(N=>!t.isHidden(N,e)),L=Sa(t,e,C,0,w.id,r,n),I=Math.max(L.w,264),F=(a.has(w.id)?er:0)+tr,A=Math.max(L.h,40)+F,P=28+A+28;L.place(b+28,x+28),o.push({id:w.id,label:w.label||w.id,order:w.order,index:S,x:b,y:x,w:I+56,h:P,headerH:28,nodeCount:$f(t,w.id)}),y=Math.max(y,I+56),x+=P+40});let k=b+y+32,E=Math.max(x-40+32,64);return{lanes:o,laneIndex:i,boxes:n,width:k,height:E,channelX:32+fn,hasChannel:c,channelPairs:d,channelSlots:v,channelStep:g,channelW:h}}function $f(t,e){let n=0;for(let o of t.graph.nodes||[])o.stage===e&&n++;return n}function Sa(t,e,n,o,i,s,r){if(n.length===0)return{w:0,h:0,place:()=>{}};let a=new Set(n),l=new Map,d=new Map,c=new gi.graphlib.Graph;c.setGraph({...$s}),c.setDefaultEdgeLabel(()=>({}));for(let k of n){let E=t.nodeById.get(k);if(!E)continue;let w=t.isGroup(k),S=e.has(k);if(w&&!S){let L=t.laneChildren(k),I=Sa(t,e,L,o+1,i,s,r);l.set(k,I);let F=Math.max(200,I.w+32),A=Math.max(56,34+I.h+16);d.set(k,{w:F,h:A})}else w&&S?d.set(k,Ei(E,!0)):d.set(k,Ei(E,!1));let C=d.get(k);c.setNode(k,{width:C.w,height:C.h})}let u=new Map;for(let k of s){let E=ka(t,k.source,a),w=ka(t,k.target,a);if(!E||!w||E===w)continue;let S=E+"\0"+w,L=(u.get(S)||0)+Js(k.kind);u.set(S,L),c.setEdge(E,w,{weight:L,minlen:1})}gi.layout(c),wa(c,n),ya(c,n);let h=1/0,p=1/0,g=-1/0,v=-1/0,b=new Map;for(let k of n){let E=c.node(k);if(!E)continue;let w=E.width,S=E.height,C=E.x-w/2,L=E.y-S/2;b.set(k,{x:C,y:L,w,h:S}),h=Math.min(h,C),p=Math.min(p,L),g=Math.max(g,C+w),v=Math.max(v,L+S)}isFinite(h)||(h=0,p=0,g=0,v=0);let x=Math.max(0,g-h),y=Math.max(0,v-p);return{w:x,h:y,place(k,E){for(let w of n){let S=b.get(w);if(!S)continue;let C=Cn(k+(S.x-h)),L=Cn(E+(S.y-p)),I=t.isGroup(w),F=e.has(w);r.set(w,{id:w,x:C,y:L,w:S.w,h:S.h,laneId:i,depth:o,isGroup:I,collapsed:I&&F,headerH:I&&!F?34:0});let A=l.get(w);if(A){let P=C+Math.max(16,(S.w-A.w)/2);A.place(Cn(P),Cn(L+34))}}}}}function ka(t,e,n){if(n.has(e))return e;let o=t.parentOf.get(e)||null,i=0;for(;o;){if(n.has(o))return o;if(++i>4096)return null;o=t.parentOf.get(o)||null}return null}function Cn(t){return Math.round(t*100)/100}function Jf(t){return Math.floor(t*100)/100}function Dt(t,e){let n=t.lanes[e],o=t.lanes[e+1];return n&&o?(n.y+n.h+o.y)/2:n?n.y+n.h+12:o?o.y-12:0}function La(t,e,n=ir,o=sr){if(e<=1)return 0;let i=Math.min(o,2*n/(e-1));return Tn((t-(e-1)/2)*i)}function ep(t,e,n){let o=Math.abs(t.exitY-t.entryY),i=Math.abs(e.exitY-e.entryY);if(Math.abs(o-i)>.5)return i-o;let s=n.get(t.key)??0,r=n.get(e.key)??0;return Math.abs(s-r)>.5?s-r:t.keye.key?1:0}function Ia(t,e){let n=new Map,o=new Map,i=new Map,s=new Map,r=[];for(let c of e){let u=c.sBox.laneId+">"+c.tBox.laneId,h=s.get(u);h?h.push(c):(s.set(u,[c]),r.push(u))}let a=new Map,l=[];for(let c of r){let u=s.get(c);u.sort((w,S)=>Si(w.tBox)-Si(S.tBox)||Ma(w.tBox)-Ma(S.tBox)||tp(w.key,S.key));let h=u[0],p=t.laneIndex.get(h.sBox.laneId)??0,g=t.laneIndex.get(h.tBox.laneId)??0,v=g>p,b=Math.abs(g-p)===1,x=Tn(b?Dt(t,Math.min(p,g)):v?Dt(t,p):Dt(t,p-1)),y=Tn(b?x:v?Dt(t,g-1):Dt(t,g)),k={key:c,sourceLane:h.sBox.laneId,targetLane:h.tBox.laneId,adjacent:b,down:v,members:u.map(w=>w.key),trunkX:t.channelX,entryY:x,exitY:y,slot:0};n.set(c,k);let E=0;for(let w of u)E+=Si(w.tBox);a.set(c,u.length?E/u.length:0),u.forEach((w,S)=>{o.set(w.key,k),i.set(w.key,S)}),b||l.push(k)}let d=Math.max(1,t.channelSlots);return l.sort((c,u)=>ep(c,u,a)),l.forEach((c,u)=>{c.slot=u,c.trunkX=Tn(t.channelX+Math.min(u,d-1)*t.channelStep)}),{pairs:n,pairOf:o,indexOf:i}}function Ma(t){return t.x+t.w/2}function Si(t){return t.y+t.h/2}function tp(t,e){return te?1:0}function Tn(t){return Math.round(t*100)/100}function Ie(t,e){if(t.length<2)return"";let n=["M "+t[0].x+" "+t[0].y];for(let i=1;i=o||i===t.length-1){let r=s===0?0:(o-n)/s,a=Mi(t[i-1],t[i],Math.max(0,Math.min(1,r))),l=Math.atan2(t[i].y-t[i-1].y,t[i].x-t[i-1].x)*180/Math.PI;return{point:{x:gt(a.x),y:gt(a.y)},angle:l}}n+=s}return{point:t[t.length-1],angle:0}}function gt(t){return Math.round(t*100)/100}function Rn(t,e){return Math.hypot(e.x-t.x,e.y-t.y)}function Mi(t,e,n){return{x:t.x+(e.x-t.x)*n,y:t.y+(e.y-t.y)*n}}function np(t,e){let n=new Map,o=[],i=new Map;for(let r of e.boxes.values()){o.push(r);let a=n.get(r.laneId);a?a.push(r):n.set(r.laneId,[r]);let l=new Set([r.id]);for(let d of t.ancestors(r.id))l.add(d);i.set(r.id,l)}for(let r of n.values())r.sort((a,l)=>a.x-l.x||hp(a.id,l.id));let s=new Map;for(let r of e.lanes){let a=r.y+r.headerH;for(let l of n.get(r.id)||[])a=Math.max(a,l.y+l.h);s.set(r.id,se(Math.min(a+9,r.y+r.h-5)))}return{index:t,frame:e,laneBoxes:n,ancestry:i,band:s,all:o}}function Ca(t,e,n,o){let i=t.ancestry.get(e.id);if(!i||i.has(n)||i.has(o))return!1;let s=t.ancestry.get(n);if(s&&s.has(e.id))return!1;let r=t.ancestry.get(o);return!(r&&r.has(e.id))}function op(t,e){return{x0:Math.min(t.x,e.x),x1:Math.max(t.x,e.x),y0:Math.min(t.y,e.y),y1:Math.max(t.y,e.y)}}function ip(t,e){return t.x1>e.x+1&&t.x0e.y+1&&t.y0=d||Ca(t,v,i,s)&&c.push({a:v.x-Vo,b:v.x+v.w+Vo});c.sort((v,b)=>v.a-b.a);let u=a?a.x+4:0,h=a?a.x+a.w-4:u,p=[],g=u;for(let v of c)if(!(v.b<=g)&&(v.a>g&&p.push({a:g,b:Math.min(v.a,h)}),g=Math.max(g,v.b),g>=h))break;return gv.b-v.a>=10&&v.b>u&&v.a=10&&l.push(se((h+Math.min(u.b,h+40))/2))}else{let h=Math.min(u.b,e.x-4);h-u.a>=10&&l.push(se((Math.max(u.a,h-40)+h)/2))}l.sort((u,h)=>n?u-h:h-u);let d=G(e),c=n?e.x+e.w:e.x;for(let u of l)if(!Ae(t,[T(c,d),T(u,d)],s,r))return u;return null}function vt(t,e,n,o,i,s,r){let a=n>=q(e),l=Ai(t,e,a,o,i,s,r);return l!==null?l:Ai(t,e,!a,o,i,s,r)}function Ta(t,e,n){let o=np(t,e),i=new Map,s=[];for(let h of t.graph.edges||[]){let p=t.visibleRepresentative(h.source,n),g=t.visibleRepresentative(h.target,n);if(p===g&&!(h.source===h.target&&p===h.source)||!e.boxes.has(p)||!e.boxes.has(g))continue;let v=p+"\0"+g+"\0"+h.kind+"\0"+(h.subkind||""),b=i.get(v);b||(b={edges:[],s:p,t:g},i.set(v,b),s.push(v)),b.edges.push(h)}let r=[];for(let h of s){let p=i.get(h);if(mt(p.edges[0]))continue;let g=e.boxes.get(p.s),v=e.boxes.get(p.t);g.laneId!==v.laneId&&(t.ancestors(p.s).indexOf(p.t)>=0||t.ancestors(p.t).indexOf(p.s)>=0||r.push({key:h,sBox:g,tBox:v}))}let a=Ia(e,r),l=new Map,d=new Map,c=new Map,u=[];for(let h of s){let p=i.get(h),g=p.edges[0],v=e.boxes.get(p.s),b=e.boxes.get(p.t),x=mt(g),y=v.laneId!==b.laneId,k=t.ancestors(p.s).indexOf(p.t)>=0,E=t.ancestors(p.t).indexOf(p.s)>=0,w,S;if(p.s===p.t)w=up(v,Ii(c,p.s));else if(x){let I=Ii(l,v.laneId);w=cp(o,t,e,v,b,I)}else if(E||k)w=rp(o,v,b,E);else if(y){let I=a.pairOf.get(h),F=a.indexOf.get(h)||0,A=dp(o,v,b,I,F);w=A.points,A.joinFrom>=0&&A.joinTo>A.joinFrom&&(S={key:I.key,sourceLane:I.sourceLane,targetLane:I.targetLane,index:F,size:I.members.length,axis:I.adjacent?"x":"y",channelX:I.trunkX,entryY:I.entryY,exitY:I.adjacent?I.entryY:I.exitY,joinFrom:A.joinFrom,joinTo:A.joinTo})}else{let I=Ii(d,"in:"+v.laneId);w=ap(o,e,v,b,I)}let C=p.edges.length>1?"×"+p.edges.length:g.label||"",L=Li(w);u.push({id:g.id,ids:p.edges.map(I=>I.id),kind:g.kind,subkind:g.subkind,source:p.s,target:p.t,label:C,points:w,d:Ie(w,ge),mid:L.point,midAngle:L.angle,crossLane:y,back:x,count:p.edges.length,trunk:S})}return u}function Ii(t,e){let n=(t.get(e)||0)+1;return t.set(e,n),n-1}function q(t){return t.x+t.w/2}function G(t){return t.y+t.h/2}function rp(t,e,n,o){let i=o?e:n,s=o?n:e,r=se(i.x+12),a=i.y+Math.min(28,i.h/2),l=o?[T(r,a),T(r,G(s)),T(s.x,G(s))]:[T(s.x,G(s)),T(r,G(s)),T(r,a)];if(!Ae(t,l,e.id,n.id))return l;let d=Ai(t,s,!1,G(s),G(s),e.id,n.id),c=d===null?r:d,u=d===null||d<=s.x?s.x:s.x+s.w,h=o?[T(r,a),T(r,G(s)),T(c,G(s)),T(u,G(s))]:[T(u,G(s)),T(c,G(s)),T(r,G(s)),T(r,a)];return Ae(t,h,e.id,n.id)?l:Hn(h)}function ap(t,e,n,o,i){let s=n.x+n.w,r=Aa(n,i),a=o.x,l=Aa(o,i),d;if(a>=s+16){let u=se((s+a)/2);d=Math.abs(r-l)<.5?[T(s,r),T(a,l)]:[T(s,r),T(u,r),T(u,l),T(a,l)]}else{let u=e.lanes[e.laneIndex.get(n.laneId)??0],h=Math.max(n.y+n.h,o.y+o.h)+14+i*6,p=u?u.y+u.h-6:h,g=se(Math.min(h,p));d=[T(q(n),n.y+n.h),T(q(n),g),T(q(o),g),T(q(o),o.y+o.h)]}if(!Ae(t,d,n.id,o.id))return d;let c=lp(t,n,o,i);return c&&!Ae(t,c,n.id,o.id)?c:d}function lp(t,e,n,o){let i=se((t.band.get(e.laneId)??Math.max(e.y+e.h,n.y+n.h)+12)+o*5),s=vt(t,e,q(n),G(e),i,e.id,n.id),r=vt(t,n,q(e),i,G(n),e.id,n.id);if(s===null||r===null)return null;let a=s>=q(e)?e.x+e.w:e.x,l=r>=q(n)?n.x+n.w:n.x;return Hn([T(a,G(e)),T(s,G(e)),T(s,i),T(r,i),T(r,G(n)),T(l,G(n))])}function dp(t,e,n,o,i){let s=o.down,r=La(i,o.members.length),a=s?e.y+e.h:e.y,l=s?n.y:n.y+n.h,d=se(o.entryY+r),c=d,u=se(o.exitY+r),h=o.trunkX,p=o.adjacent?[T(q(e),a),T(q(e),d),T(q(n),d),T(q(n),l)]:[T(q(e),a),T(q(e),c),T(h,c),T(h,u),T(q(n),u),T(q(n),l)],g=o.adjacent?[p[1],p[2]]:[p[2],p[3]];if(!Ae(t,p,e.id,n.id))return On(p,g);let v=o.adjacent?d:c,b=o.adjacent?d:u,x=vt(t,e,q(n),G(e),v,e.id,n.id),y=vt(t,n,q(e),b,G(n),e.id,n.id);if(x===null||y===null)return On(p,g);let k=x>=q(e)?e.x+e.w:e.x,E=y>=q(n)?n.x+n.w:n.x,w=o.adjacent?[T(k,G(e)),T(x,G(e)),T(x,d),T(y,d),T(y,G(n)),T(E,G(n))]:[T(k,G(e)),T(x,G(e)),T(x,c),T(h,c),T(h,u),T(y,u),T(y,G(n)),T(E,G(n))],S=o.adjacent?[w[2],w[3]]:[w[3],w[4]];return Ae(t,w,e.id,n.id)?On(p,g):On(Hn(w),S)}function On(t,e){return{points:t,joinFrom:t.indexOf(e[0]),joinTo:t.indexOf(e[1])}}function cp(t,e,n,o,i,s){let r=n.lanes[n.laneIndex.get(o.laneId)??0],a=Math.max(o.y+o.h,i.y+i.h),l=new Set(e.ancestors(o.id)),d=new Set(e.ancestors(i.id));for(let y of n.boxes.values()){if(y.laneId!==o.laneId||y.id!==o.id&&y.id!==i.id&&(l.has(y.id)||d.has(y.id)))continue;let k=Math.min(q(o),q(i)),E=Math.max(q(o),q(i));y.x+y.wE||(a=Math.max(a,y.y+y.h))}let c=se(Math.min(a+12+s*7,r?r.y+r.h-6:a+12)),u=d.has(o.id)||l.has(i.id)?se(i.x+Math.min(28,i.w/3)):q(i),h=[T(q(o),o.y+o.h),T(q(o),c),T(u,c),T(u,i.y+i.h)];if(!Ae(t,h,o.id,i.id))return h;let p=vt(t,o,q(i),G(o),c,o.id,i.id),g=vt(t,i,q(o),c,G(i),o.id,i.id);if(p===null||g===null)return h;let v=p>=q(o)?o.x+o.w:o.x,b=g>=q(i)?i.x+i.w:i.x,x=Hn([T(v,G(o)),T(p,G(o)),T(p,c),T(g,c),T(g,G(i)),T(b,G(i))]);return Ae(t,x,o.id,i.id)?h:x}function up(t,e){let n=14+e*6,o=t.x+t.w,i=t.y+t.h,s=i-Math.min(14+e*6,t.h/2),r=o-Math.min(28+e*6,t.w/2);return[T(o,s),T(o+n,s),T(o+n,i+n),T(r,i+n),T(r,i)]}function Aa(t,e){if(e===0)return se(G(t));let n=Math.min(t.h-16,40),o=n/4,i=(e+1>>1)*(e%2===1?1:-1);return se(G(t)+Math.max(-n/2,Math.min(n/2,i*o)))}function Hn(t){let e=[];for(let n of t){let o=e[e.length-1];o&&Math.abs(o.x-n.x)<.01&&Math.abs(o.y-n.y)<.01||e.push(n)}return e.length>=2?e:t}function hp(t,e){return te?1:0}function T(t,e){return{x:se(t),y:se(e)}}function se(t){return Math.round(t*100)/100}function Ci(t){let e=Array.from(t.boxes.values());return e.length?e.slice().sort((n,o)=>n.y-o.y||n.x-o.x)[0]:null}function Ra(t,e,n,o){let i=Array.from(e.boxes.values()),s=e.boxes.get(n);if(!s||!i.length)return Ci(e);let r=t.parentOf.get(n)||null,a=i.filter(u=>(t.parentOf.get(u.id)||null)===r&&u.laneId===s.laneId);a.length<2&&(a=i.filter(u=>u.laneId===s.laneId));let l=o==="ArrowRight"||o==="ArrowLeft",d=o==="ArrowRight"||o==="ArrowDown",c=u=>{let h=u.slice().sort((g,v)=>l?g.x-v.x||g.y-v.y:g.y-v.y||g.x-v.x),p=h.findIndex(g=>g.id===n);return p<0?null:h[p+(d?1:-1)]||null};return c(a)||c(i)}function Oa(t,e,n,o){let i=e&&t.boxes.get(e)||null,s=n&&t.boxes.get(n)||null;if(!i||!s)return i||s;let r=o==="ArrowRight"||o==="ArrowLeft",a=o==="ArrowRight"||o==="ArrowDown",l=c=>r?c.x+c.w/2:c.y+c.h/2,d=l(s)-l(i);return d===0?a?s:i:d>0===a?s:i}var fp=["is-hover","is-selected","is-lit","is-flowing","is-traced"];function pp(t){let e=t.bundle,n=t.sourceLabel||e.sourceLane,o=t.targetLabel||e.targetLane;return(e.edgeCount===1?"1 connection":e.edgeCount+" connections")+" from "+n+" to "+o+". Hover to separate them."}function Ha(t){let e=t.bundle,n=e.kinds.length===1?e.kinds[0]:"mixed",o=H("g",{class:"mlv-bundle mlv-bundle--"+n,"data-bundle-id":e.id,"data-count":String(e.edgeCount),"data-members":String(e.count),"data-axis":e.axis});o.setAttribute("data-lane-pair",e.sourceLane+">"+e.targetLane),t.stage&&o.setAttribute("data-stage",t.stage),t.phase!==void 0&&le(o,t.phase),t.severity&&(o.setAttribute("data-sev",t.severity),o.classList.add("has-issue"));let i=H("title");i.textContent=pp(t),o.appendChild(i);for(let d of e.spurs)o.appendChild(H("path",{class:"mlv-bundle__spur",d:d.d}));o.appendChild(H("path",{class:"mlv-bundle__trunk",d:e.d}));let s=String(e.edgeCount),r=15+6*s.length,a=H("g",{class:"mlv-bundle__badge",transform:"translate("+e.badge.x+","+e.badge.y+")"});a.appendChild(H("rect",{class:"mlv-bundle__badge-box",x:-r/2,y:-8,width:r,height:16,rx:8}));let l=H("text",{class:"mlv-bundle__badge-text",x:0,y:0});return l.textContent=s,a.appendChild(l),o.appendChild(a),o}var Pn=class{constructor(){this.els=new Map;this.memberOf=new Map}adopt(e,n){this.els=e,this.memberOf=new Map;for(let o of n)for(let i of o.memberIds)this.memberOf.set(i,o.id)}sync(e){if(this.els.size===0)return;let n=new Set;for(let[o,i]of this.memberOf){let s=e.get(o);s&&mp(s)&&n.add(i)}for(let[o,i]of this.els)i.classList.toggle("is-expanded",n.has(o));for(let[o,i]of this.memberOf){let s=e.get(o);s&&s.classList.toggle("is-bundled",!n.has(i))}}};function mp(t){let e=t.classList;for(let n of fp)if(e.contains(n))return!0;return!1}var Ti={entrypoint:"M5 3.2 12.4 8 5 12.8Z",config:"M2.2 4.5h11.6M2.2 8h11.6M2.2 11.5h11.6M5.6 3.1v2.8M10.4 6.6v2.8M7 10.1v2.8",dataset:"M3 4.3c0-1 2.24-1.8 5-1.8s5 .8 5 1.8-2.24 1.8-5 1.8-5-.8-5-1.8ZM3 4.3v3.4c0 1 2.24 1.8 5 1.8s5-.8 5-1.8V4.3M3 7.7v3.4c0 1 2.24 1.8 5 1.8s5-.8 5-1.8V7.7",dataloader:"M2 4.2h2.6l6.8 7.6H14M2 11.8h2.6l6.8-7.6H14M11.6 2.4 14 4.2l-2.4 1.8M11.6 10 14 11.8l-2.4 1.8",split:"M2 8h3.6l3.4-4.2H14M9 12.2 5.6 8H2M11.8 2 14 3.8l-2.2 1.8M11.8 10.4 14 12.2l-2.2 1.8",transform:"M2.4 13.6 10 6M8.4 4.4l3.2 3.2M12 1.8v2.8M10.6 3.2h2.8M4 2v1.8M3.1 2.9h1.8",augment:"M5.2 2.2 6.1 5 8.9 5.9 6.1 6.8 5.2 9.6 4.3 6.8 1.5 5.9 4.3 5ZM11.4 8.2l.7 1.9 1.9.7-1.9.7-.7 1.9-.7-1.9-1.9-.7 1.9-.7Z",model:"M8 2 14 5 8 8 2 5ZM2 8.2 8 11.2 14 8.2M2 11.2 8 14.2 14 11.2",layer:"M2.4 4.6h11.2v2.4H2.4ZM2.4 9h11.2v2.4H2.4Z",loss:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM8 5.4A2.6 2.6 0 1 0 8 10.6 2.6 2.6 0 0 0 8 5.4ZM8 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2Z",optimizer:"M2.6 12.2a6.4 6.4 0 1 1 10.8 0M8 8.4 11.2 5.2M7.2 12.2h1.6",scheduler:"M2 12.4 5.6 8.6l2.4 2.2L13.4 4.6M9.6 4.6h3.8v3.8",scaler:"M2.4 4h11.2v8H2.4ZM5.4 4v2.4M8 4v3.6M10.6 4v2.4",train_loop:"M13.2 8A5.2 5.2 0 1 1 11.5 4.1M13.6 2.6v3.2h-3.2",eval_loop:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM5.2 8.1 7.2 10.2 11 6",metric:"M3 13V8.4M6.4 13V4.4M9.8 13v-3.4M13.2 13V6.4M2 13.8h12",checkpoint:"M2.6 3h7.6l3.2 3.2v7.4H2.6ZM5.6 3v3.4h4.6V3M5.6 13.6v-3.6h4.6v3.6",tracker:"M8 6.8A1.2 1.2 0 1 0 8 9.2 1.2 1.2 0 0 0 8 6.8ZM5.2 5.2a4 4 0 0 0 0 5.6M10.8 5.2a4 4 0 0 1 0 5.6M3.1 3.1a7 7 0 0 0 0 9.8M12.9 3.1a7 7 0 0 1 0 9.8",predict:"M9.2 1.6 4 8.6h3.3l-.7 5.8L12 7.2H8.5Z",function:"M6.6 2.8c-1.7 0-2.1.9-2.1 2.3v1.1c0 1-.5 1.6-1.5 1.6 1 0 1.5.6 1.5 1.6v1.1c0 1.4.4 2.3 2.1 2.3M9.4 2.8c1.7 0 2.1.9 2.1 2.3v1.1c0 1 .5 1.6 1.5 1.6-1 0-1.5.6-1.5 1.6v1.1c0 1.4-.4 2.3-2.1 2.3",class:"M3 2.6h10v10.8H3ZM3 6.2h10M5.4 8.8h5.2M5.4 11h3.2",artifact:"M8 2 14 5v6l-6 3-6-3V5ZM2 5l6 3 6-3M8 8v6",external:"M9 2.8h4.2V7M13.2 2.8 7.4 8.6M11.6 9.4v3.8H2.8V4.4h3.8",unknown:"M8 2.2A5.8 5.8 0 1 0 8 13.8 5.8 5.8 0 0 0 8 2.2ZM6.2 6.3a1.85 1.85 0 1 1 2.7 1.7c-.6.3-.9.8-.9 1.5M8 11.2v.9"},gp={step:"M8 5.6A2.4 2.4 0 1 0 8 10.4 2.4 2.4 0 0 0 8 5.6Z",group:"M2.6 2.6h4.4V7H2.6ZM9 2.6h4.4V7H9ZM2.6 9h4.4v4.4H2.6ZM9 9h4.4v4.4H9Z"},bt={operation:"function",data:"dataset",model:"model",state:"layer",objective:"loss",optimizer:"optimizer",evaluation:"eval_loop",metric:"metric",output:"external",config:"config",loop:"train_loop",branch:"split",group:"group",entrypoint:"entrypoint",artifact:"artifact",op:"function",step:"function",function:"function",method:"function",compute:"function",computation:"function",process:"function",construction:"function",initialization:"function",hook:"function",callback:"function",forward:"function",class:"class",component:"class",dataset:"dataset","data source":"dataset",datasource:"dataset",input:"dataset",inputs:"dataset",batch:"dataset",dataloader:"dataloader","data loader":"dataloader",loader:"dataloader",batching:"dataloader",sampler:"dataloader","data pipeline":"dataloader",split:"split","data split":"split",decision:"split",conditional:"split",condition:"split",selection:"split",control:"split","control flow":"split",preprocessing:"transform",preprocess:"transform",postprocessing:"transform",transform:"transform",transformation:"transform",augmentation:"augment",augment:"augment",network:"model",module:"model",architecture:"model",layer:"layer","state update":"layer",update:"layer","state init":"layer",variable:"layer",buffer:"layer",parameters:"layer",weights:"layer",loss:"loss",criterion:"loss",optimiser:"optimizer",optimization:"optimizer",optimisation:"optimizer","optimizer step":"optimizer","optimization step":"optimizer",scheduler:"scheduler",schedule:"scheduler","learning rate":"scheduler",scaler:"scaler","training loop":"train_loop","train loop":"train_loop","optimization loop":"train_loop",epoch:"train_loop","training run":"train_loop",eval:"eval_loop",validation:"eval_loop",test:"eval_loop","evaluation loop":"eval_loop",metrics:"metric",score:"metric",scoring:"metric",log:"tracker",logging:"tracker",logger:"tracker",tracker:"tracker",history:"tracker",checkpoint:"checkpoint",checkpointing:"checkpoint",persistence:"checkpoint",save:"checkpoint",outputs:"external",result:"external",return:"external",display:"external",external:"external",configuration:"config",settings:"config",hyperparameters:"config",arguments:"config",args:"config",environment:"config","entry point":"entrypoint",entry:"entrypoint",launcher:"entrypoint","api entry":"entrypoint",main:"entrypoint",script:"entrypoint",file:"artifact",inference:"predict",predict:"predict",prediction:"predict",sampling:"predict",generation:"predict",phase:"group",stage:"group"};function vp(t){return String(t||"").trim().toLowerCase().replace(/[\s_-]+/g," ")}function bp(t){let e=vp(t||"");if(!e||e==="unknown")return"step";if(bt[e])return bt[e];let n=e.split(/[^a-z0-9]+/).filter(Boolean),o=n[n.length-1];if(o&&bt[o])return bt[o];let i=n[0];return i&&bt[i]?bt[i]:"step"}function wt(t,e){return e?"group":bp(t.kind)}function Nn(t){return Ti[t]||gp[t]||Ti.unknown}function Ri(t){return Object.prototype.hasOwnProperty.call(Ti,t)}function Oi(t,e=16){let n=H("svg",{class:"mlv-icon",viewBox:"0 0 16 16",width:e,height:e,"aria-hidden":"true",focusable:"false"}),o=document.createElementNS(_e,"path");return Se(o,{d:Nn(t),fill:"none",stroke:"currentColor","stroke-width":"1.3","stroke-linecap":"round","stroke-linejoin":"round","vector-effect":"non-scaling-stroke"}),n.appendChild(o),n}var Pa={search:"M7 2.6a4.4 4.4 0 1 0 0 8.8 4.4 4.4 0 0 0 0-8.8ZM10.2 10.2 13.6 13.6",more:"M3.4 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2ZM8 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2ZM12.6 7.4a.6.6 0 1 0 0 1.2.6.6 0 0 0 0-1.2Z",close:"M3.6 3.6 12.4 12.4M12.4 3.6 3.6 12.4",chevron:"M6 3.6 10.4 8 6 12.4",plus:"M8 3.2v9.6M3.2 8h9.6",minus:"M3.2 8h9.6",fit:"M2.6 6V2.6H6M10 2.6h3.4V6M13.4 10v3.4H10M6 13.4H2.6V10",rail:"M2.4 3h11.2v10H2.4ZM9.6 3v10",open:"M9 2.8h4.2V7M13.2 2.8 7.4 8.6M11.6 9.4v3.8H2.8V4.4h3.8",target:"M8 2.6v2.2M8 11.2v2.2M2.6 8h2.2M11.2 8h2.2M8 5.2A2.8 2.8 0 1 0 8 10.8 2.8 2.8 0 0 0 8 5.2Z",warning:"M8 2.2 14.4 13.4H1.6ZM8 6.4v3.4M8 11.6v.1",info:"M8 2.2a5.8 5.8 0 1 0 0 11.6A5.8 5.8 0 0 0 8 2.2ZM8 7.2v4M8 5v.1",notobserved:"M2.6 5V2.6H5M7 2.6h2M11 2.6h2.4V5M13.4 7v2M13.4 11v2.4H11M9 13.4H7M5 13.4H2.6V11M2.6 9V7",flow:"M2.4 8h8.4M8.4 5.2 11.6 8l-3.2 2.8M13.2 6.4v3.2",legend:"M2.4 3.6h2.4v2.4H2.4ZM2.4 10h2.4v2.4H2.4ZM6.8 4.8h6.8M6.8 11.2h6.8",copy:"M5.8 5.8h7.6v7.6H5.8ZM2.6 10.2V2.6h7.6v3.2",phases:"M2.6 2.2h10.8v3H2.6ZM2.6 6.5h10.8v3H2.6ZM2.6 10.8h10.8v3H2.6ZM4.6 5.2v1.3M4.6 9.5v1.3",phaseindex:"M2.4 3.4h2v2h-2ZM2.4 7h2v2h-2ZM2.4 10.6h2v2h-2ZM6.4 4.4h7.2M6.4 8h7.2M6.4 11.6h7.2",image:"M2.4 3.4h11.2v9.2H2.4ZM2.4 10.6 5.8 7.4l2.4 2.2 2.2-2 3.2 3M10.3 5.3a1.05 1.05 0 1 0 0 2.1 1.05 1.05 0 0 0 0-2.1Z",review:"M2.4 3.2 4.8 5.4 2.4 7.6M7.2 5.4h6.4M7.2 9.4h6.4M2.6 13h11",keyboard:"M1.8 4.2h12.4v7.6H1.8ZM4.2 6.6h.1M6.7 6.6h.1M9.2 6.6h.1M11.7 6.6h.1M4.8 9.4h6.4"};function B(t,e=14){let n=H("svg",{class:"mlv-uicon",viewBox:"0 0 16 16",width:e,height:e,"aria-hidden":"true",focusable:"false"}),o=document.createElementNS(_e,"path");return Se(o,{d:Pa[t]||Pa.close,fill:"none",stroke:"currentColor","stroke-width":"1.4","stroke-linecap":"round","stroke-linejoin":"round"}),n.appendChild(o),n}var wp=240;function fe(t){return t==="inferred"?"inferred":t==="unresolved"?"? unresolved":""}function yp(t,e){return t==="inferred"?"Inferred, not observed: reasoned from the cited code and stated assumptions; the quotes do not show all of it.":"Unresolved: the evidence does not settle this claim. It does not mean the "+e+" is missing."}function yt(t,e="step"){let n=fe(t);if(!n||!t)return null;let o=f("span","mlv-basis-tag",n);return o.setAttribute("data-basis",t),o.setAttribute("aria-hidden","true"),o.title=yp(t,e),o}function Na(t){if(!t.stale)return"";let e=t.staleQuotes,n=t.staleElsewhere?"a file in another folder":"a changed or missing file";return e?e.stale+" of "+e.total+(e.total===1?" quote cites ":" quotes cite ")+n:"cites "+n}function Fa(t){return"mlv-n-"+t.replace(/[^A-Za-z0-9-]/g,e=>"_"+e.charCodeAt(0).toString(16)+"_")}function Hi(t){return t.stage||"unknown"}function Bn(t){return t.sublabel?t.sublabel:t.kind&&t.kind!=="unknown"&&Ri(t.kind)?t.kind.replace(/_/g," "):""}function xp(t){return t.kind==="unknown"||!Ri(t.kind)?"step":t.kind.replace(/_/g," ")}function jt(t){let e=t.node,n=[];n.push(xp(e)+" "+e.label),n.push(e.phaseLabel?e.phaseLabel+" phase":Hi(e)+" stage"),e.loc.file&&n.push(en(e.loc));let o=$e(e.basis);o&&n.push(o.slice(2));let i=Y(t.counts),s=oe(t.counts);i>0&&n.push(i+(i===1?" finding":" findings")+", highest severity "+s),t.descendants>0&&n.push(t.descendants+(t.descendants===1?" nested step":" nested steps")),t.stale&&n.push(Na(t));let r=e.detail?Dn(e.detail):"";return n.join(", ")+"."+(r?" "+r:"")}var Fn=160,Ep=new Set(["etc","vs","cf","approx","incl","esp","resp","fig","figs","eq","eqs","al"]);function kp(t,e){if(t[e]!==".")return!1;let n=/[A-Za-z.]*$/.exec(t.slice(0,e)),o=(n?n[0]:"").toLowerCase();return o?/^(?:[a-z]\.)+[a-z]$/.test(o)?!0:Ep.has(o):!1}function Dn(t){let e=t.replace(/\s+/g," ").trim();if(!e)return"";let n=/[.!?](?=\s|$)/g;for(let s=n.exec(e);s&&s.indexFn/2?o.slice(0,i):o).replace(/[\s,;:]+$/,"")+"…"}function Pi(t){return t.detail&&t.detail.trim()&&xi(t)?2:1}function Ba(t,e){let n=t.node,o=e,i=f("div","mlv-node");i.id=Fa(n.id),i.setAttribute("role","button"),i.setAttribute("tabindex","-1"),i.setAttribute("data-node-id",n.id),i.setAttribute("data-stage",Hi(n)),i.setAttribute("data-kind",n.kind),i.setAttribute("data-level",n.level),n.basis&&i.setAttribute("data-basis",n.basis),t.phase!==void 0&&le(i,t.phase);let s=oe(t.counts);s&&i.setAttribute("data-sev",s),i.setAttribute("aria-label",jt(t)),i.style.left=t.box.x+"px",i.style.top=t.box.y+"px",i.style.width=t.box.w+"px",i.style.height=t.box.h+"px",s&&i.classList.add("has-issues"),t.stale&&i.classList.add("is-stale"),o&&i.classList.add("is-collapsed-group"),m(i,f("div","mlv-node__rail"));let r=m(i,f("div","mlv-node__main"));m(r,f("div","mlv-node__iconbox")).appendChild(Oi(wt(n,o)));let l=m(r,f("div","mlv-node__text")),d=n.label||n.qualname||n.id;m(l,f("div","mlv-node__title mlv-node__title--wrap",d)).setAttribute("data-lines",String(yi(n,t.box.w)));let u=o?1:Pi(n),h=m(l,f("div","mlv-node__sub",Bn(n).slice(0,wp)));if(u===2?h.setAttribute("data-lines","2"):n.loc.file&&m(l,Qe("mlv-node__loc",n.loc,"div")),o){let g=m(l,f("div","mlv-node__chips"));m(g,f("span","mlv-chip",et(t.descendants)))}t.stale&&i.appendChild(ja(t));let p=yt(n.basis,o?"group":"step");if(p&&i.appendChild(p),o){let g=Ce(t.counts,14);g&&(g.classList.add("mlv-node__cluster"),g.style.position="absolute",g.style.top="-8px",g.style.right="-6px",g.style.padding="1px 6px",g.style.borderRadius="var(--mlv-r-pill)",g.style.background="var(--mlv-surface)",g.style.boxShadow="var(--mlv-sh-1)",i.appendChild(g))}else{let g=Gs(t.counts,18,t.findings||[]);g&&i.appendChild(g)}return i}function et(t){return t+(t===1?" step":" steps")}function Pe(t){return t+(t===1?" finding touches this phase":" findings touch this phase")}function Gt(t){let e=Y(t),n=oe(t);return e+(e===1?" finding touches":" findings touch")+" this phase, highest severity "+(n||"none")+". A finding that cites steps or connections in several phases counts in each of them."}function Da(t){let e=t.node,n=f("div","mlv-group");n.id=Fa(e.id),n.setAttribute("data-node-id",e.id),n.setAttribute("data-group","1"),n.setAttribute("data-stage",Hi(e)),t.phase!==void 0&&le(n,t.phase),e.basis&&n.setAttribute("data-basis",e.basis),n.setAttribute("data-depth",String(Math.min(2,t.box.depth)));let o=oe(t.counts);o&&n.setAttribute("data-sev",o),n.style.left=t.box.x+"px",n.style.top=t.box.y+"px",n.style.width=t.box.w+"px",n.style.height=t.box.h+"px";let i=f("div","mlv-group__header");i.setAttribute("role","button"),i.setAttribute("tabindex","-1"),i.setAttribute("aria-expanded","true"),i.setAttribute("aria-label",jt(t)+" Group, expanded. Double-click to collapse.");let s=f("button","mlv-group__chevron-btn");s.type="button",s.tabIndex=-1,s.setAttribute("aria-label","Collapse "+(e.label||e.qualname));let r=B("chevron",12);r.setAttribute("class","mlv-uicon mlv-group__chevron"),s.appendChild(r),i.appendChild(s),i.appendChild(Oi(wt(e,!1),14)),m(i,f("span","mlv-group__name",e.label||e.qualname));let a=yt(e.basis,"group");a&&i.appendChild(a),m(i,f("span","mlv-group__count",et(t.descendants)));let l=Ce(t.counts,13);return l&&i.appendChild(l),t.stale&&(n.classList.add("is-stale"),i.appendChild(ja(t))),n.appendChild(i),n}function ja(t){let e=f("span","mlv-node__stale");return e.setAttribute("data-stale","1"),e.setAttribute("aria-hidden","true"),e.title=Na(t)+(t.staleElsewhere?". Its jumps are blocked; the notice above says which folder to add.":" since publishing. Its jumps are blocked."),e.appendChild(B("warning",12)),e}function Ga(t,e,n,o){let i=f("div","mlv-lane");i.setAttribute("data-lane-id",t.id),i.setAttribute("data-stage",t.id),o!==void 0&&le(i,o),i.style.left=t.x+"px",i.style.top=t.y+"px",i.style.width=t.w+"px",i.style.height=t.h+"px",i.style.setProperty("--mlv-lane-header-h",t.headerH+"px"),n&&i.classList.add("is-absent");let s=m(i,f("div","mlv-lane__header"));if(o!==void 0){let d=m(s,f("span","mlv-lane__num",String(o+1)));d.title="Phase "+(o+1)}let r=m(s,f("span","mlv-lane__label",t.label));r.title=o!==void 0?"Phase "+(o+1)+": "+t.label:t.label,m(s,f("span","mlv-lane__count",et(t.nodeCount))),m(s,f("span","mlv-lane__spacer"));let a=Gt(e),l=Ce(e,13,a);if(l){s.appendChild(l);let d=m(s,f("span","mlv-lane__unit",Pe(Y(e))));d.setAttribute("aria-hidden","true"),d.title=a}return i}function za(t){let e=new Map,n=[];for(let i of t){if(!_p(i))continue;let s=i.trunk.key,r=e.get(s);r?r.push(i):(e.set(s,[i]),n.push(s))}let o=[];for(let i of n){let s=e.get(i);if(s.length<2)continue;s.sort((a,l)=>a.trunk.index-l.trunk.index||Ip(a.id,l.id));let r=s[0].trunk.axis==="y"?Sp(i,s):Mp(i,s);for(let a of r)o.push(a)}return o}function _p(t){let e=t.trunk;return!e||e.joinFrom<1||e.joinTo<=e.joinFrom?!1:e.joinTo+1<=t.points.length-1}function Sp(t,e){let n=e[0].trunk,o=n.channelX,i=1/0,s=1/0;for(let d of e)i=Math.min(i,d.points[d.trunk.joinFrom-1].x),s=Math.min(s,d.points[d.trunk.joinTo+1].x);if(Math.abs(n.exitY-n.entryY){let l=r.points[r.trunk.joinFrom].x,d=r.points[r.trunk.joinTo].x;return{i:a,lo:Math.min(l,d),hi:Math.max(l,d)}}),i=[],s=0;for(let r of Lp(o)){if(r.length<2)continue;let a=1/0,l=-1/0;for(let h of r)a=Math.min(a,h.lo),l=Math.max(l,h.hi);if(!(l-a>=Uo))continue;let d=r.slice().sort((h,p)=>h.i-p.i).map(h=>e[h.i]),c=[de(a,n.entryY),de(l,n.entryY)],u=[];for(let h of d){let p=h.trunk.joinFrom,g=Wt(h.points.slice(0,p+1).concat([de(h.points[p].x,n.entryY)]));u.push({id:h.id,side:"in",points:g,d:Ie(g,ge)})}for(let h of d){let p=h.trunk.joinTo,g=Wt([de(h.points[p].x,n.entryY)].concat(h.points.slice(p)));u.push({id:h.id,side:"out",points:g,d:Ie(g,ge)})}i.push(Va(t,s++,d,n,c,u,de((a+l)/2,n.entryY)))}return i}function Lp(t){let e=t.slice().sort((s,r)=>s.lo-r.lo||s.hi-r.hi||s.i-r.i),n=[],o=[],i=-1/0;for(let s of e)o.length&&s.lo>i&&(n.push(o),o=[]),o.push(s),i=o.length===1?s.hi:Math.max(i,s.hi);return o.length&&n.push(o),n}function Va(t,e,n,o,i,s,r){let a=[],l=0;for(let d of n)l+=d.ids.length,a.indexOf(d.kind)<0&&a.push(d.kind);return{id:"bundle:"+t+(e?"#"+e:""),sourceLane:o.sourceLane,targetLane:o.targetLane,part:e,memberIds:n.map(d=>d.id),count:n.length,edgeCount:l,axis:o.axis,trunk:i,d:Ie(i,ge),spurs:s.filter(d=>d.points.length>1),badge:r,kinds:a}}function Wt(t){let e=[];for(let n of t){let o=e[e.length-1];o&&Math.abs(o.x-n.x)<.01&&Math.abs(o.y-n.y)<.01||e.push(n)}return e}function Ip(t,e){return te?1:0}function de(t,e){return{x:Wa(t),y:Wa(e)}}function Wa(t){return Math.round(t*100)/100}function jn(t){let{index:e,frame:n,routes:o,keep:i}=t,s=[];for(let h of n.lanes)s.push({lane:h,counts:e.laneCounts(h.id,i),phase:e.phaseIndexOf(h.id)});let r=[],a=Array.from(n.boxes.values()).sort((h,p)=>h.depth-p.depth||Cp(h.id,p.id));for(let h of a){let p=e.nodeById.get(h.id);if(!p)continue;let g=h.isGroup&&!h.collapsed,b=g||h.collapsed?e.subtreeIssues(h.id,i):e.issuesOf(h.id,i);r.push({expandedGroup:g,visual:{node:p,box:h,counts:e.countsFor(b),findings:b.map(x=>x.short).filter(Boolean),phase:e.phaseIndexOf(p.stage),descendants:e.descendantCount(h.id),...Ka(p.evidenceLocs,p.loc,t.staleFiles)}})}let l=[];for(let h of o){let p=e.issuesOfEdges(h.ids,i),g=e.nodeById.get(h.source),v=e.nodeById.get(h.target);l.push({route:h,severity:oe(e.countsFor(p)),findings:p.map(b=>b.short).filter(Boolean),phase:g?e.phaseIndexOf(g.stage):void 0,labelVisible:h.back,stage:g?g.stage:void 0,sourceLabel:g?g.label||g.qualname:void 0,targetLabel:v?v.label||v.qualname:void 0,mountSerial:t.mountSerial,placement:t.labels?t.labels.get(h.id):void 0,weight:Qs(h.ids),...Tp(h.ids,e,t.staleFiles),basis:Ap(h.ids,e)})}let d=new Map;for(let h of l)d.set(h.route.id,h.severity);let c=new Map;for(let h of n.lanes)c.set(h.id,h.label);let u=[];for(let h of za(o)){let p=null;for(let g of h.memberIds){let v=d.get(g)||null;v&&(!p||ne.indexOf(v)e?1:0}function Ka(t,e,n){if(!n.size)return{stale:!1};let o=t||(e.file?[e]:[]),i=[];for(let r of o){let a=r.file?n.get(r.file):void 0;a&&i.push(a)}if(!i.length)return{stale:!1};let s={stale:!0,staleQuotes:{stale:i.length,total:o.length}};return ae(i)&&(s.staleElsewhere=!0),s}function Tp(t,e,n){if(!n.size)return{};let o=!1,i=!0;for(let s of t){let r=e.edgeById.get(s),a=r?Ka(r.evidenceLocs,r.loc,n):null;!a||!a.stale||(o=!0,a.staleElsewhere||(i=!1))}return o?i?{stale:!0,staleElsewhere:!0}:{stale:!0}:{}}function Ua(t,e){let n=jn(e),o=n.frame;D(t.lanes),D(t.nodes),D(t.bundleGroup),D(t.edgeGroup),D(t.connectors),t.world.style.width=o.width+"px",t.world.style.height=o.height+"px",t.edgesSvg.setAttribute("width",String(o.width)),t.edgesSvg.setAttribute("height",String(o.height)),t.edgesSvg.setAttribute("viewBox","0 0 "+o.width+" "+o.height);for(let a of n.lanes)t.lanes.appendChild(Ga(a.lane,a.counts,!1,a.phase));let i=new Map;for(let a of n.nodes){let l=a.visual,d=a.expandedGroup?Da(l):Ba(l,l.box.collapsed);e.wireNode(d,l.node.id,a.expandedGroup),i.set(l.node.id,d),a.expandedGroup?t.lanes.appendChild(d):t.nodes.appendChild(d)}let s=new Map;for(let a of n.bundles){let l=Ha(a);t.bundleGroup.appendChild(l),s.set(a.bundle.id,l)}let r=new Map;for(let a of n.edges){let l=gr(a);e.wireEdge(l,a.route),t.edgeGroup.appendChild(l),r.set(a.route.id,l)}return{nodeEls:i,edgeEls:r,bundleEls:s,plan:n}}var Xa="(prefers-reduced-motion: reduce)",Rp="vscode-reduce-motion",Ya="vscode-using-screen-reader";function Gn(){return Hp(Xa)||Op()?"reduced":"full"}function Za(){return Ni(Ya)}function Op(){return Ni(Rp)||Ni(Ya)}function Ni(t){try{let e=typeof document<"u"?document.body:null;return!!e&&e.classList.contains(t)}catch{return!1}}function Hp(t){try{if(typeof window>"u"||!window.matchMedia)return!1;let e=window.matchMedia(t);return!!(e&&e.matches)}catch{return!1}}var Wn=class{constructor(e){this.stop=null;this.current=Gn();let n=()=>{let i=Gn();i!==this.current&&(this.current=i,e(i))},o=[];try{let i=typeof window<"u"&&window.matchMedia?window.matchMedia(Xa):null;i&&typeof i.addEventListener=="function"&&(i.addEventListener("change",n),o.push(()=>i.removeEventListener("change",n)))}catch{}try{let i=typeof document<"u"?document.body:null;if(i&&typeof MutationObserver=="function"){let s=new MutationObserver(n);s.observe(i,{attributes:!0,attributeFilter:["class"]}),o.push(()=>s.disconnect())}}catch{}o.length&&(this.stop=()=>{for(let i of o)try{i()}catch{}})}get mode(){return this.current}destroy(){this.stop&&this.stop(),this.stop=null}};var Ne=.15,tt=2.5,Pp=1.2;function el(t,e,n,o,i=24){let s=(n-i*2)/Math.max(1,t),r=(o-i*2)/Math.max(1,e);return{zoom:Fe(Math.min(s,r),Ne,Pp)}}var Be=.62,Qa=[.15,.2,.25,.32,.4,.5,.62,.8,1,1.25,1.6,2,2.5];function Np(t){if(!(t>0))return 1;let e=Qa[0],n=1/0;for(let o of Qa){let i=Math.abs(Math.log(t/o));i=Be||!a)return{zoom:r,x:(e-i*r)/2,y:Math.max(o,(n-s*r)/2),mode:"whole"};let l=a.x-Math.max(0,t.channelW||0),d=Math.max(1,a.x+a.w-l),c=Math.max(1,a.h),u=Math.min((e-o*2)/d,(n-o*2)/c),h=u>=tl,p=Fe(h?Math.min(u,De):De,Ne,tt),g=i*p<=e?(e-i*p)/2:o-l*p,v=s*p<=n?(n-s*p)/2:o-a.y*p;return{zoom:p,x:g,y:v,mode:h?"phase-fit":"phase-anchor"}}var Bp=240;function nl(t,e,n,o,i=24){let s=t.lanes[e];if(!s)return null;let r=Math.max(1,t.width),a=Math.max(1,t.height),l=s.x-Math.max(0,t.channelW||0),d=Math.max(1,s.x+s.w-l),c=Math.max(1,s.h),u=Math.min((n-i*2)/d,(o-i*2)/c),h=u>=tl,p=Fe(h?Math.min(u,De):De,Ne,tt),g=r*p<=n?(n-r*p)/2:i-l*p,v;if(a*p<=o)v=(o-a*p)/2;else{v=i-s.y*p;let b=o-i-a*p;c*p+2*i<=o&&v=0&&s.push({dx:r,dy:0});let a=e.y-i-(t.y+t.h);t.y+a>=0&&s.push({dx:0,dy:a});let l=e.x+e.w+i-t.x;t.x+t.w+l<=n&&s.push({dx:l,dy:0});let d=e.y+e.h+i-t.y;return t.y+t.h+d<=o&&s.push({dx:0,dy:d}),s.length?(s.sort((c,u)=>Math.abs(c.dx)+Math.abs(c.dy)-(Math.abs(u.dx)+Math.abs(u.dy))),s[0]):null}var $a=80,Ja=.1,zt=.45,Dp=1,zn=class{constructor(e,n,o){this.vp={x:0,y:0,zoom:1};this.contentW=1;this.contentH=1;this.lod="full";this.zBucket=0;this.fitted=!1;this.fitSize=null;this.frame=null;this.coveredFn=null;this.animToken=0;this.animLive=0;this.canvas=e,this.world=n,this.onChange=o}setContent(e,n,o=null){this.contentW=Math.max(1,e),this.contentH=Math.max(1,n),this.frame=o}apply(){let{x:e,y:n,zoom:o}=this.vp;this.world.style.transform="translate("+e+"px,"+n+"px) scale("+o+")";let i=o=$a&&o>=this.fitSize.w*Ja||i>=$a&&i>=this.fitSize.h*Ja)return this.applyFit(this.fitSize.padding,this.fitSize.whole),!0}return this.apply(),!1}visibleArea(){return this.size()}acceptResize(){if(!this.fitted||!this.fitSize)return;let{w:e,h:n}=this.size();this.fitSize={...this.fitSize,w:e,h:n}}fitsAt(e,n,o=48){let{w:i,h:s}=this.visibleArea();return Math.min((i-o)/Math.max(1,e.w),(s-o)/Math.max(1,e.h))>=n}frameRect(e,n,o,i=48){let{w:s,h:r}=this.visibleArea(),a=Math.min((s-i)/Math.max(1,e.w),(r-i)/Math.max(1,e.h)),l=this.vp.zoom;if(l>=Be&&a>=l){this.centerOn(e),this.keepClear(n);return}let d=l>=Be?Math.max(Dp,l):o,c=Math.max(zt,Math.min(d,a));this.centerOn(a>=zt?e:n,c),this.keepClear(n)}keepClear(e){let n=this.covered();if(!n)return;let{w:o,h:i}=this.visibleArea(),s=this.vp.zoom,r=Fi({x:e.x*s+this.vp.x,y:e.y*s+this.vp.y,w:e.w*s,h:e.h*s},n,o,i);r&&this.panBy(r.dx,r.dy)}centerOn(e,n){this.animToken++,this.fitted=!1;let{w:o,h:i}=this.visibleArea();typeof n=="number"&&(this.vp.zoom=Fe(n,Ne,tt));let s=this.vp.zoom;this.vp.x=o/2-(e.x+e.w/2)*s,this.vp.y=i/2-(e.y+e.h/2)*s;let r=this.covered();if(r){let a=Fi({x:e.x*s+this.vp.x,y:e.y*s+this.vp.y,w:e.w*s,h:e.h*s},r,o,i);a&&(this.vp.x+=a.dx,this.vp.y+=a.dy)}this.apply()}revealRect(e,n=16){let{w:o,h:i}=this.visibleArea(),s=this.vp.zoom,r=(c,u,h)=>{let p=Math.max(0,Math.min(n,(h-u)/2));return u>h?-c:c<0?p-c:c+u>h?h-p-(c+u):0},a=r(e.x*s+this.vp.x,e.w*s,o),l=r(e.y*s+this.vp.y,e.h*s,i),d=this.covered();if(d){let c=Fi({x:e.x*s+this.vp.x+a,y:e.y*s+this.vp.y+l,w:e.w*s,h:e.h*s},d,o,i,Math.min(n,8));c&&(a+=c.dx,l+=c.dy)}(a||l)&&this.panBy(a,l)}zoomToBox(e,n=80){let{w:o,h:i}=this.visibleArea(),s=Fe(Math.min((o-n)/Math.max(e.w,1),(i-n)/Math.max(e.h,1)),Ne,tt);this.centerOn(e,s)}isVisible(e,n=this.visibleArea()){let{w:o,h:i}=n,s=e.x*this.vp.zoom+this.vp.x,r=e.y*this.vp.zoom+this.vp.y,a=e.w*this.vp.zoom,l=e.h*this.vp.zoom;if(!(s>=0&&r>=0&&s+a<=o&&r+l<=i))return!1;let d=this.covered();return!d||!ol({x:s,y:r,w:a,h:l},d)}setCovered(e){this.coveredFn=e}covered(){if(!this.coveredFn)return null;try{let e=this.coveredFn();return e&&e.w>0&&e.h>0?e:null}catch{return null}}animateTo(e,n=Bp,o){let i=++this.animToken;this.fitted=!1;let s={x:e.x,y:e.y,zoom:Fe(e.zoom,Ne,tt)},r=typeof window<"u"&&typeof window.requestAnimationFrame=="function"?window.requestAnimationFrame.bind(window):null,a=()=>{this.animLive=0,this.vp={...s},this.apply(),o&&o()};if(!r||!(n>0)||Gn()==="reduced"){a();return}let l={...this.vp},{w:d,h:c}=this.size(),u={x:(d/2-l.x)/l.zoom,y:(c/2-l.y)/l.zoom},h={x:(d/2-s.x)/s.zoom,y:(c/2-s.y)/s.zoom},p=Date.now();this.animLive=i;let g=()=>{if(i!==this.animToken)return;let v=Math.min(1,(Date.now()-p)/n);if(v>=1){a();return}let b=1-Math.pow(1-v,3),x=l.zoom*Math.pow(s.zoom/l.zoom,b);this.vp={x:d/2-(u.x+(h.x-u.x)*b)*x,y:c/2-(u.y+(h.y-u.y)*b)*x,zoom:x},this.apply(),r(g)};r(g)}get animating(){return this.animLive!==0&&this.animLive===this.animToken}};function Fe(t,e,n){return tn?n:t}var il=360,jp=26,Gp=24,Wp=4,Vn=28,zp=360,Vp=12,sl=4;function qp(t,e,n,o){let i=[],s=null,r=0,a=Math.max(1,n*o);for(let l of t){let d=Math.max(0,l.rect.x*e.zoom+e.x),c=Math.max(0,l.rect.y*e.zoom+e.y),u=Math.min(n,(l.rect.x+l.rect.w)*e.zoom+e.x),h=Math.min(o,(l.rect.y+l.rect.h)*e.zoom+e.y),p=Math.max(0,u-d)*Math.max(0,h-c);if(p<=0)continue;i.push(l.id);let g=Math.max(1,l.rect.w*l.rect.h*e.zoom*e.zoom),v=p/a+p/g;v>r+1e-9&&(r=v,s=l.id)}return{ids:i,current:s}}function Bi(t){let e=Y(t);return e?e+(e===1?" finding touches this phase":" findings touch this phase"):""}var Di=t=>t+(t===1?" step":" steps");function rl(t){let e=t.length;for(let n of t)e=Math.max(e,n.index+1);return e}var qn=class{constructor(e){this.rowsData=[];this.rowEls=new Map;this.mode={roomy:!0,folded:!1,hidden:!1,suppressed:!1};this.popover=!1;this.inViewKey="";this.currentId=null;this.disposers=[];this.cb=e,this.root=f("nav","mlv-phaseindex"),this.root.setAttribute("aria-label","Phase index"),this.root.hidden=!0,this.panel=m(this.root,f("div","mlv-phaseindex__panel"));let n=m(this.panel,f("div","mlv-phaseindex__head"));m(n,f("span","mlv-phaseindex__title","Phases")),this.headSteps=m(n,f("span","mlv-phaseindex__total")),this.foldBtn=m(n,f("button","mlv-btn mlv-btn--icon mlv-phaseindex__fold")),this.foldBtn.type="button",this.foldBtn.tabIndex=-1,this.foldBtn.setAttribute("aria-label","Fold the phase index"),this.foldBtn.title="Fold the phase index to one line",this.foldBtn.appendChild(B("chevron",12)),_(this.foldBtn,"click",s=>{s.preventDefault(),s.stopPropagation(),this.cb.fold(!0)}),this.rowsEl=m(this.panel,f("ol","mlv-phaseindex__rows")),this.pill=m(this.root,f("button","mlv-phaseindex__pill")),this.pill.type="button",this.pill.tabIndex=-1,this.pill.setAttribute("aria-expanded","false"),this.pillNum=m(this.pill,f("span","mlv-phaseindex__pillnum")),this.pillLabel=m(this.pill,f("span","mlv-phaseindex__pilllabel"));let o=B("chevron",12);o.setAttribute("class","mlv-uicon mlv-phaseindex__pillchev"),this.pill.appendChild(o),_(this.pill,"click",s=>{s.preventDefault(),s.stopPropagation(),this.mode.roomy&&this.mode.folded?this.cb.fold(!1):this.setPopover(!this.popover)}),this.disposers.push(_(this.root,"keydown",s=>{(s.key==="Enter"||s.key===" ")&&s.stopPropagation()})),this.disposers.push(_(this.panel,"wheel",s=>s.stopPropagation()));let i=typeof document>"u"?null:document;i&&this.disposers.push(_(i,"pointerdown",s=>{if(!this.popover)return;let r=s.target;r&&this.root.contains(r)||this.setPopover(!1)}))}setRows(e){this.rowsData=e,this.rowEls.clear(),D(this.rowsEl);let n=e.reduce((i,s)=>i+s.steps,0);this.headSteps.textContent=Di(n);let o=rl(e);for(let i of e){let s=m(this.rowsEl,f("li","mlv-phaseindex__item")),r=m(s,f("button","mlv-phaseindex__row"));r.type="button",r.tabIndex=-1,r.setAttribute("data-phase-id",i.id),le(r,i.index),m(r,f("span","mlv-phaseindex__num",String(i.index+1))).setAttribute("aria-hidden","true");let a=m(r,f("span","mlv-phaseindex__label",i.label));a.title="Phase "+(i.index+1)+": "+i.label;let l=Ce(i.counts,11,Bi(i.counts));l&&(l.classList.add("mlv-phaseindex__sev"),l.setAttribute("aria-hidden","true"),l.title=Bi(i.counts)+" ("+nn(i.counts)+")",r.appendChild(l)),m(r,f("span","mlv-phaseindex__steps",Di(i.steps))).setAttribute("aria-hidden","true"),r.setAttribute("data-name","Phase "+(i.index+1)+" of "+o+": "+i.label+", "+Di(i.steps)+(Y(i.counts)?", "+Bi(i.counts)+" ("+nn(i.counts).replace(/^\d+ findings?: /,"")+")":"")),r.title="Go to this phase",_(r,"click",d=>{d.preventDefault(),d.stopPropagation(),this.setPopover(!1),this.cb.go(i.id)}),this.rowEls.set(i.id,r)}this.inViewKey="\0",this.applyMode()}get rows(){return this.rowsData}setMode(e){this.mode={...this.mode,...e},this.applyMode()}get available(){return this.rowsData.length>=2}get form(){return this.root.hidden?"none":this.root.getAttribute("data-form")==="list"?"list":"pill"}get popoverOpen(){return this.popover&&this.form==="pill"}closePopover(){return this.popoverOpen?(this.setPopover(!1),!0):!1}setPopover(e){this.popover=e,this.applyMode()}applyMode(){let e=this.mode,n=e.hidden||e.suppressed||!this.available;this.root.hidden=n;let o=e.roomy&&!e.folded;(o||n)&&(this.popover=!1),this.root.setAttribute("data-form",o?"list":"pill"),this.popover?this.root.setAttribute("data-open","true"):this.root.removeAttribute("data-open"),this.panel.hidden=!o&&!this.popover,this.pill.hidden=o,this.pill.setAttribute("aria-expanded",this.popover?"true":"false");let i=e.roomy&&e.folded;this.pill.title=i?"Unfold the phase index":this.popover?"Close the phase list":"Show every phase",this.syncPill()}setInView(e,n,o){let i=qp(this.rowsData,e,n,o),s=i.ids.join("\0")+""+(i.current||"");if(s===this.inViewKey)return;this.inViewKey=s,this.currentId=i.current;let r=new Set(i.ids);for(let[a,l]of this.rowEls){let d=r.has(a);d?l.setAttribute("data-in-view","true"):l.removeAttribute("data-in-view"),l.setAttribute("aria-label",(l.getAttribute("data-name")||"")+(d?", in view":""))}this.syncPill()}currentPhase(){return this.currentId}inView(){let e=[];for(let[n,o]of this.rowEls)o.getAttribute("data-in-view")==="true"&&e.push(n);return e}syncPill(){let e=this.rowsData,n=rl(e),o=e.find(i=>i.id===this.currentId)||null;this.pillNum.textContent=o?o.index+1+"/"+n:String(e.length),this.pillLabel.textContent=o?o.label:e.length===1?"phase":"phases",this.pill.setAttribute("aria-label",(o?"Phase "+(o.index+1)+" of "+n+" in view: "+o.label:e.length+" phases")+". "+(this.mode.roomy&&this.mode.folded?"Unfold the phase index.":this.popover?"Close the phase list.":"Show every phase."))}coveredRect(e,n,o=0,i=0){if(this.root.hidden||!(e>0)||!(n>0))return null;let s=[];for(let l of[this.panel,this.pill]){if(l.hidden)continue;let d=l.getBoundingClientRect();d.width>0&&d.height>0&&s.push({x:d.left-o,y:d.top-i,w:d.width,h:d.height})}if(!s.length){let l=Vp,d=jp+this.rowsData.length*Gp+2*Wp;if(this.root.getAttribute("data-form")==="list"){let c=Math.min(il,e-2*l),u=Math.min(d,n-2*l);s.push({x:e-l-c,y:n-l-u,w:c,h:u})}else{let c=Math.min(zp,e-2*l);if(s.push({x:e-l-c,y:n-l-Vn,w:c,h:Vn}),this.popover){let u=Math.min(il,e-2*l),h=Math.min(d,Math.max(0,n-2*l-Vn-sl));s.push({x:e-l-u,y:n-l-Vn-sl-h,w:u,h})}}}let r=Math.min(...s.map(l=>l.x)),a=Math.min(...s.map(l=>l.y));return{x:r,y:a,w:Math.max(...s.map(l=>l.x+l.w))-r,h:Math.max(...s.map(l=>l.y+l.h))-a}}destroy(){for(let e of this.disposers)try{e()}catch{}this.disposers=[]}};function Un(t,e){let n=[],o=new Map;for(let u of t.lanes)o.set(u.id,n.length),n.push({id:u.id,label:u.label||u.id,index:t.phaseIndexOf(u.id),steps:[],findings:t.laneCounts(u.id,e),inferred:0,unresolved:0});let i=new Set,s=0,r=(u,h)=>{if(i.has(u))return;let p=t.nodeById.get(u);if(!p)return;i.add(u);let g=o.get(p.stage);if(g===void 0)s++;else{let v=n[g],b=p.basis||"";b==="inferred"?v.inferred++:b==="unresolved"&&v.unresolved++,v.steps.push({id:u,title:p.label||p.qualname||u,basis:b,depth:h,tags:t.issuesOf(u,e).map(x=>({short:x.short||x.id,id:x.id,severity:ee(x.severity)}))})}for(let v of t.laneChildren(u))r(v,h+1)};for(let u of t.lanes)for(let h of t.roots(u.id))r(h,0);for(let u of t.graph.nodes||[])r(u.id,0);let a=new Map,l=0,d=0;for(let u of t.graph.edges||[]){let h=t.nodeById.get(u.source),p=t.nodeById.get(u.target),g=h?o.get(h.stage):void 0,v=p?o.get(p.stage):void 0;if(g===void 0||v===void 0)d++;else if(g===v)l++;else{let b=g+">"+v,x=a.get(b);x?x.count++:a.set(b,{from:g,to:v,count:1})}}let c=Array.from(a.values()).sort((u,h)=>u.from-h.from||u.to-h.to);return{phases:n,links:c,inside:l,unplacedConnections:d,unplacedSteps:s,totalSteps:(t.graph.nodes||[]).length,totalConnections:(t.graph.edges||[]).length}}var je=(t,e,n)=>t+" "+(t===1?e:n);function Vt(t){return t.to===t.from+1}function Kp(t){let e=0,n=0;for(let o of t.links)Vt(o)?e+=o.count:n+=o.count;return{inside:t.inside,next:e,jumps:n,unplaced:t.unplacedConnections}}function Up(t){let e=Kp(t),n=[je(e.inside,"inside a phase","inside a phase"),je(e.next,"to the next phase","to the next phase"),je(e.jumps,"skips ahead or goes back","skip ahead or go back")];return e.unplaced&&n.push(je(e.unplaced,"with an end the document lacks","with an end the document lacks")),je(t.phases.length,"phase","phases")+" · "+je(t.totalSteps,"step","steps")+" · "+je(t.totalConnections,"connection","connections")+": "+n.join(", ")+"."}var Xp="Arrows go to the next phase; brackets count connections that skip ahead (solid) or back (dashed). ◌ inferred, ? unresolved.",Yp="◌ inferred, ? unresolved. Brackets count connections that skip ahead (solid) or go back (dashed).";function zi(t){let e=[];return t.inferred&&e.push(t.inferred+" inferred"),t.unresolved&&e.push(t.unresolved+" unresolved"),je(t.steps.length,"step","steps")+(e.length?" ("+e.join(", ")+")":"")}var Zp=16,Qp=12,$p=620,Jp=600,em=980,tm=32,nm=50,al=18,om=8,ll=12,dl=16,cl=12,ji=26,Kn=12,im=12,sm=7,Gi=14,rm=3,am=24,lm=17,dm=8,ul=12,cm=6.8;function pl(t,e,n){let o=Math.max(1,Math.floor(e)),i=Math.max(1,Math.floor(n)),s=o<$p,r=s?Qp:Zp,a=t.phases.length,l=t.links.filter(O=>!Vt(O)),d=l.map((O,W)=>({link:O,i:W})).sort((O,W)=>hl(O.link)-hl(W.link)||Math.min(O.link.from,O.link.to)-Math.min(W.link.from,W.link.to)||O.i-W.i),c=new Map,u=[];for(let{link:O}of d){let W=Math.min(O.from,O.to),J=Math.max(O.from,O.to),te=0;for(;teW<=be&&ce<=J);)te++;te===u.length&&u.push([]),u[te].push([W,J]),c.set(O,te)}let h=u.length,p=Math.max(60,Math.min(160,Math.round(o*.2))),g=im;h&&Kn+h*g+Gi>p&&(g=Math.max(sm,(p-Kn-Gi)/h));let v=h?Math.ceil(Kn+(h-1)*g+Gi):0,b=Math.max(120,Math.min(em,o-2*r-v)),x=!s&&b>=Jp,y=x?2:1,k=!x,E=k?nm:tm,w=Up(t),S=s?Yp:Xp,C=Math.max(80,o-2*r),L=O=>Math.max(1,Math.ceil(O.length*cm/C)),I=L(w)+(S?L(S):0),F={x:r,y:r,w:o-2*r,h:am+I*lm+dm,lines:I,summary:w,key:S},A=t.phases.map(O=>Math.ceil(O.steps.length/y)),P=A.slice(),N=t.phases.map(O=>Math.ceil(Math.min(O.steps.length,rm)/y)),z=O=>E+(O>0?O*al:0)+om,j=()=>F.y+F.h+ul+P.reduce((O,W)=>O+z(W),0)+ji*Math.max(0,a-1)+r;for(;j()>i;){let O=-1;for(let W=0;WP[O]||P[W]===P[O]&&t.phases[W].steps.length>=t.phases[O].steps.length)&&(O=W);if(O<0)break;P[O]--}let V=[],$=F.y+F.h+ul,Xe=(b-2*ll-(y-1)*dl)/y;t.phases.forEach((O,W)=>{let J=P[W],te=J*y,ce=O.steps.length,Ee=te>=ce?ce:Math.max(0,te-1),Oo=ce-Ee,Rs=[];for(let Ye=0;Yebe.other-Ee.other||(c.get(be.link)||0)-(c.get(Ee.link)||0));let te=J.y+10,ce=Math.max(1,J.h-20);W.forEach((be,Ee)=>Ro.set(Wi(be.link,O),pe(te+(Ee+1)*ce/(W.length+1))))}let bc=l.map(O=>{let W=c.get(O)||0,J=pe(Co+Kn+W*g),te=Ro.get(Wi(O,O.from))||0,ce=Ro.get(Wi(O,O.to))||0;return{from:O.from,to:O.to,count:O.count,back:O.toi,narrow:s,header:F,blocks:V,arrows:lt,brackets:bc,trimmed:V.some(O=>O.more>0)}}function hl(t){return Math.abs(t.to-t.from)}function Wi(t,e){return t.from+">"+t.to+"@"+e}function fl(t,e,n){let o=t.get(e);o?o.push(n):t.set(e,[n])}function pe(t){return Math.round(t*10)/10}var um=0,qt=(t,e,n)=>t+" "+(t===1?e:n),Xn=class{constructor(e){this.input=null;this.layoutData=null;this.blockEls=[];this.active=0;this.covered=0;this.disposers=[];this.cb=e,this.uid="mlv-ov"+ ++um,this.root=f("section","mlv-overview"),this.root.setAttribute("aria-label","Phase overview"),this.root.hidden=!0,this.content=m(this.root,f("div","mlv-overview__content")),this.disposers.push(_(this.root,"keydown",n=>this.onKey(n))),this.disposers.push(_(this.root,"wheel",n=>n.stopPropagation()))}get open(){return!this.root.hidden}get layout(){return this.layoutData}get activeIndex(){return this.active}show(e,n,o,i){this.root.hidden=!1,this.active=Math.max(0,Math.min(e.phases.length-1,i)),this.draw(e,n,o),this.focusBlock(this.active)}hide(){this.root.hidden=!0}redraw(e,n,o){if(!this.open)return;let i=this.root.ownerDocument,s=!!i&&!!i.activeElement&&this.root.contains(i.activeElement);this.active=Math.max(0,Math.min(e.phases.length-1,this.active)),this.draw(e,n,o),s&&this.focusBlock(this.active)}focusBlock(e){let n=this.blockEls[e];if(!n){try{this.root.focus()}catch{}return}this.active=e;for(let[o,i]of this.blockEls.entries())i.tabIndex=o===e?0:-1;try{n.focus({preventScroll:!0})}catch{}mm(this.root,n,this.covered,e===0)}draw(e,n,o){this.input=e;let i=pl(e,n,o);this.layoutData=i,D(this.content),this.blockEls=[],this.root.setAttribute("data-layout",i.narrow?"narrow":"wide"),this.root.setAttribute("data-scrolls",i.scrolls?"true":"false"),this.content.style.width=i.width+"px",this.content.style.height=i.height+"px";let s=m(this.content,f("div","mlv-overview__header")),r=i.header.y+i.header.h,a=i.scrolls&&r*2<=i.viewH;this.covered=a?r:0,s.setAttribute("data-sticky",a?"true":"false"),s.style.width=i.width+"px",s.style.height=r+"px",s.style.paddingTop=i.header.y+"px",s.style.paddingLeft=i.header.x+"px",s.style.paddingRight=Math.max(0,i.width-i.header.x-i.header.w)+"px";let l=m(s,f("div","mlv-overview__top")),d=m(l,f("h3","mlv-overview__title","Phase overview"));d.id=this.uid+"-title",this.root.setAttribute("aria-labelledby",d.id);let c=m(l,f("button","mlv-btn mlv-overview__back"));c.type="button",c.tabIndex=-1,c.setAttribute("aria-label","Back to the diagram (Escape)"),c.title="Back to the diagram where you were (Escape or Shift+0)",c.appendChild(B("close",12)),m(c,f("span","mlv-overview__backlabel","Back")),m(c,f("kbd","mlv-overview__kbd","Esc")).setAttribute("aria-hidden","true"),_(c,"click",b=>{b.preventDefault(),b.stopPropagation(),this.cb.back()});let u=m(s,f("p","mlv-overview__summary",i.header.summary));if(u.id=this.uid+"-summary",this.root.setAttribute("aria-describedby",u.id),i.header.key){let b=m(s,f("p","mlv-overview__key"));i.header.key.split("◌").forEach((x,y)=>{y>0&&b.appendChild(ml()),b.appendChild(fm(s).createTextNode(x))})}let h=H("svg",{class:"mlv-overview__links",width:i.width,height:i.height,viewBox:"0 0 "+i.width+" "+i.height,"aria-hidden":"true",focusable:"false"}),p=H("defs"),g=H("marker",{id:this.uid+"-arrow",viewBox:"0 0 8 8",refX:7,refY:4,markerWidth:7,markerHeight:7,orient:"auto-start-reverse"});g.appendChild(H("path",{d:"M0 0.6 7.4 4 0 7.4Z",class:"mlv-overview__head"})),p.appendChild(g),h.appendChild(p);for(let b of i.arrows)h.appendChild(H("path",{class:"mlv-overview__arrow",d:"M"+b.x+" "+b.y1+"V"+b.y2,"marker-end":"url(#"+this.uid+"-arrow)"}));for(let b of i.brackets){let x=H("path",{class:"mlv-overview__bracket",d:"M"+b.x0+" "+b.y1+"H"+b.x+"V"+b.y2+"H"+(b.x0+2),"marker-end":"url(#"+this.uid+"-arrow)"});b.back&&x.setAttribute("data-back","true"),h.appendChild(x)}this.content.appendChild(h);for(let b of i.arrows){let x=m(this.content,f("span","mlv-overview__arrowlabel",qt(b.count,"connection","connections")));x.style.left=b.labelX+"px",x.style.top=b.labelY+"px",x.setAttribute("aria-hidden","true")}for(let b of i.brackets){let x=e.phases[b.from],y=e.phases[b.to],k=m(this.content,f("span","mlv-overview__count",String(b.count)));k.style.left=b.labelX+"px",k.style.top=b.labelY+"px",b.back&&k.setAttribute("data-back","true"),k.setAttribute("aria-hidden","true"),k.title=qt(b.count,"connection","connections")+" from phase "+(x.index+1)+(b.back?" back to":" ahead to")+" phase "+(y.index+1)}let v=m(this.content,f("ol","mlv-overview__blocks"));v.setAttribute("aria-label",qt(e.phases.length,"phase","phases")),i.blocks.forEach((b,x)=>{let y=e.phases[b.phase],k=m(v,f("li","mlv-overview__item")),E=m(k,f("div","mlv-ovblock"));E.setAttribute("role","button"),E.setAttribute("data-phase-id",y.id),E.setAttribute("data-block",String(x)),E.setAttribute("data-head",b.twoLineHead?"two-lines":"one-line"),le(E,y.index),E.tabIndex=x===this.active?0:-1,pm(E,b),E.setAttribute("aria-label",hm(e,b.phase));let w=m(E,f("div","mlv-ovblock__head"));w.style.height=b.headH+"px";let S=m(w,f("div","mlv-ovblock__name"));m(S,f("span","mlv-ovblock__num",String(y.index+1)));let C=m(S,f("span","mlv-ovblock__label",y.label));C.title="Phase "+(y.index+1)+": "+y.label;let L=m(w,f("div","mlv-ovblock__meta"));m(L,f("span","mlv-ovblock__steps",zi(y)));let I=Y(y.findings);if(I>0){let A=Ce(y.findings,12,Gt(y.findings));A&&L.appendChild(A),m(L,f("span","mlv-ovblock__unit",Pe(I)))}let F=m(E,f("ul","mlv-ovblock__list"));F.id=this.uid+"-list-"+x,E.setAttribute("aria-describedby",F.id);for(let A of b.items){let P=m(F,f("li","mlv-ovitem"));if(P.style.left=gl(A.x-b.x)+"px",P.style.top=gl(A.y-b.y)+"px",P.style.width=Math.max(0,A.w)+"px",A.step<0){P.classList.add("mlv-ovitem--more"),P.textContent="… "+qt(A.more,"more step","more steps"),m(P,f("span","mlv-sr","."));continue}let N=y.steps[A.step];P.setAttribute("data-node-ref",N.id),A.depth&&P.setAttribute("data-depth",String(A.depth)),N.basis&&N.basis!=="observed"&&P.setAttribute("data-basis",N.basis);let z=m(P,f("span","mlv-ovitem__mark",N.basis==="unresolved"?"?":""));N.basis==="inferred"&&z.appendChild(ml()),z.setAttribute("aria-hidden","true"),(N.basis==="inferred"||N.basis==="unresolved")&&m(P,f("span","mlv-sr",N.basis+": "));let j=m(P,f("span","mlv-ovitem__title",N.title));if(j.title=N.title+(N.basis&&N.basis!=="observed"?" ("+N.basis+")":""),N.tags.length){let V=m(P,f("span","mlv-ovitem__tags"));for(let $ of N.tags){let Xe=m(V,f("span","mlv-ovitem__tag"));Xe.setAttribute("data-sev",$.severity),Xe.title=$.short+" ("+$.id+"), "+ye[$.severity]+" severity";let lt=Z($.severity,10,"");lt.setAttribute("aria-hidden","true"),lt.removeAttribute("role"),Xe.appendChild(lt),m(Xe,f("span","mlv-ovitem__tagid",$.short))}V.setAttribute("aria-hidden","true"),m(P,f("span","mlv-sr",", "+(N.tags.length===1?"finding ":"findings ")+N.tags.map($=>$.short).join(", ")))}N.tags.length||!/[.!?…]$/.test(N.title.trim())?m(P,f("span","mlv-sr",". ")):m(P,f("span","mlv-sr"," "))}_(E,"click",A=>{A.preventDefault(),A.stopPropagation(),this.active=x,this.cb.go(y.id,A.detail===0)}),_(E,"focus",()=>{if(this.active!==x){this.active=x;for(let[A,P]of this.blockEls.entries())P.tabIndex=A===x?0:-1}}),this.blockEls.push(E)})}onKey(e){if(e.ctrlKey||e.metaKey||e.altKey)return;let n=e.key,o=this.blockEls.length,i=!0;if(n===")"||n==="0"&&e.shiftKey)this.cb.back();else if(n==="ArrowDown"||n==="ArrowRight")this.focusBlock(Math.min(o-1,this.active+1));else if(n==="ArrowUp"||n==="ArrowLeft")this.focusBlock(Math.max(0,this.active-1));else if(n==="Home")this.focusBlock(0);else if(n==="End")this.focusBlock(o-1);else if(n==="Enter"||n===" "){let s=this.input&&this.input.phases[this.active];s&&this.cb.go(s.id,!0)}else i=!1;i&&(e.preventDefault(),e.stopPropagation())}destroy(){for(let e of this.disposers)try{e()}catch{}this.disposers=[]}};function hm(t,e){let n=t.phases[e],o=t.phases.length?t.phases[t.phases.length-1].index+1:0,i=["Phase "+(n.index+1)+" of "+Math.max(o,t.phases.length)+": "+n.label+".",zi(n)+"."],s=Y(n.findings);s>0&&i.push(Pe(s)+".");let r=[];for(let a of t.links){if(a.from!==e)continue;let l=t.phases[a.to];r.push(qt(a.count,"connection","connections")+(Vt(a)?" to":a.to0))return;let a=t.scrollTop;i-12a+r&&(a=Math.min(i-12-n,s+12-r)),o&&s+12<=r&&(a=0),a=Math.max(0,a),a!==t.scrollTop&&(t.scrollTop=a)}function gl(t){return Math.round(t*10)/10}function gm(t,e){let n=new Set([e]),o=new Set,i=s=>{let r=[e],a=new Set([e]);for(;r.length;){let l=r.pop();for(let d of t){let c=s?d.source:d.target,u=s?d.target:d.source;c===l&&(o.add(d.id),!a.has(u)&&(a.add(u),n.add(u),r.push(u)))}}};return i(!0),i(!1),{nodes:n,edges:o}}function Vi(t,e){let n=new Set([e]),o=new Set;for(let i of t)i.source!==e&&i.target!==e||(o.add(i.id),n.add(i.source),n.add(i.target));return{nodes:n,edges:o}}function vm(t,e,n){return n==="direct"?Vi(t,e):gm(t,e)}function vl(t,e,n,o,i){for(let r of t.nodes.values())r.classList.remove("is-lit");for(let r of t.edges.values())r.classList.remove("is-lit");if(!n){t.canvas.classList.remove(o);return}let s=vm(e,n,i);for(let r of s.nodes){let a=t.nodes.get(r);a&&a.classList.add("is-lit")}for(let r of s.edges){let a=t.edges.get(r);a&&a.classList.add("is-lit")}t.canvas.classList.add(o)}var K={MAX_EDGES:120,PULSE_SPEED:320,PULSE_MIN_MS:380,PULSE_MAX_MS:2200,STREAM_SPEED:220,HEAD_PX:12,GAP_DATA:34,GAP_CALL:96,GAP_BACK:24,PERIOD_DATA:210,PERIOD_CALL:490,PERIOD_BACK:165,HOP_MS:90,HOP_MAX:6,PORT_R:3.5,CHARGE_HALO_R:9,CHARGE_GLOW_R:5.5,CHARGE_CORE_R:2.6,CHARGE_TWIN_PX:360,SETTLE_PASSES:2,SETTLE_MIN_MS:1600,SETTLE_MAX_MS:6e3},bm=["--mlv-flow-dur","--mlv-flow-len","--mlv-flow-head","--mlv-flow-gap","--mlv-flow-end","--mlv-flow-delay","--mlv-flow-iter"],wm=["is-flowing","is-flowing--pulse","is-flow-settled"];function bl(t){let e=0;for(let n=1;n0?Math.max(1,Math.round((t-e)/n)):1}function wl(t){return Math.min(t,K.HOP_MAX)*K.HOP_MS}function Sm(t){return t.kind!=="config"}function Mm(t,e){let n=new Map([[e,0]]),o=new Map,i=s=>{let r=[e],a=new Set([e]),l=0;for(;r.length;){l++;let d=[];for(let c of r)for(let u of t){let h=s?u.source:u.target,p=s?u.target:u.source;if(h!==c)continue;let g=o.get(u.id);if((g===void 0||lthis.pulse(e);let n=this.modeFor(1);if(this.host.canvas.setAttribute("data-flow",n),n==="off")return;let o=this.host.edges().get(e.id);if(!o)return;this.decorate(o,e,n,null,0),o.classList.add("is-flowing--pulse"),this.decorated.push(e.id),this.decoratedRoutes.set(e.id,e);let i=this.host.nodes(),s=i.get(e.source),r=i.get(e.target);s&&(s.classList.add("is-flow-source"),this.sourceId=e.source),r&&(r.classList.add("is-flow-target"),this.targetId=e.target)}stream(e,n){this.clear(),this.lastRun=()=>{this.stream(e,n)};let o=this.host.routes(),i=Lm(o,e,n),s=i.edges.size,r=this.modeFor(s);this.host.canvas.setAttribute("data-flow",r);let a=s>K.MAX_EDGES,l=a&&r!=="off";if(r==="off"||a)return{mode:r,capped:l,edges:s};let d=this.host.edges(),c=[],u=0,h=0;for(let g of o){let v=i.edges.get(g.id);if(v===void 0||!Sm(g))continue;let b=d.get(g.id);b&&(c.push({route:g,g:b,hop:v}),u=Math.max(u,bl(g.points||[])),h=Math.max(h,wl(v)))}let p=km(u,h);for(let{route:g,g:v,hop:b}of c)this.decorate(v,g,r,b,p),v.classList.add("is-flowing"),this.decorated.push(g.id),this.decoratedRoutes.set(g.id,g);return{mode:r,capped:l,edges:s}}settle(e){let n=this.decoratedRoutes.get(e),o=this.host.edges().get(e);if(!n||!o||o.classList.contains("is-flow-settled"))return!1;let i=o.querySelectorAll(".mlv-edge__flow, .mlv-edge__charge");for(let s=0;s=2&&!o.querySelector(".mlv-edge__dir")&&o.appendChild(kl(n)),o.classList.add("is-flow-settled"),this.settled&&this.host.canvas.setAttribute("data-flow-settled","true"),!0}get settled(){if(!this.decorated.length)return!1;let e=this.host.edges();return this.decorated.every(n=>{let o=e.get(n);return!o||o.classList.contains("is-flow-settled")})}get flowing(){return this.decorated.length>0}replay(){return!this.lastRun||!this.decorated.length||this.modeFor(1)!=="motion"?!1:(this.lastRun(),this.decorated.length>0)}modeFor(e){return this.enabledFlag?this.motionFlag==="reduced"||e>K.MAX_EDGES?"static":"motion":"off"}decorate(e,n,o,i,s){let r=n.points||[];if(r.length<2)return;let a=bl(r),l=e.style;if(e.appendChild(El("out",r[0])),e.appendChild(El("in",r[r.length-1])),o==="static"){e.appendChild(kl(n));return}let d=H("path",{class:"mlv-edge__flow",d:n.d,pathLength:100});e.appendChild(d);let c=xl(K.HEAD_PX,a);if(l.setProperty("--mlv-flow-len",Ge(a)+"px"),l.setProperty("--mlv-flow-head",String(c)),i===null){let u=ym(a);l.setProperty("--mlv-flow-dur",u+"ms"),l.setProperty("--mlv-flow-iter",String(K.SETTLE_PASSES));let h=a>0?Im(e):"";if(h){let p=Am(e),g=yl(h,u,0,p);e.appendChild(g),this.settleOnEnd(g,n.id),a>K.CHARGE_TWIN_PX&&e.appendChild(yl(h,u,-Math.round(u/2),p))}}else{let u=xm(n.kind,n.subkind,n.back),h=xl(u,a);l.setProperty("--mlv-flow-gap",String(h)),l.setProperty("--mlv-flow-end",String(Ge(-(c+h))));let p=wl(i);l.setProperty("--mlv-flow-delay",p+"ms"),l.setProperty("--mlv-flow-iter",String(_m(s,p,Em(n.kind,n.back)))),this.settleOnEnd(d,n.id)}}settleOnEnd(e,n){e.addEventListener("animationend",o=>{o.target===e&&this.settle(n)})}};function Im(t){let e=t.querySelector(".mlv-edge__path");return e&&e.getAttribute("id")||""}function Am(t){try{let e=t,n=e.ownerSVGElement||(typeof e.closest=="function"?e.closest("svg"):null);if(!n||typeof n.getCurrentTime!="function")return null;let o=n.getCurrentTime();return typeof o=="number"&&isFinite(o)?o:null}catch{return null}}function yl(t,e,n,o){let i=H("g",{class:n===0?"mlv-edge__charge":"mlv-edge__charge mlv-edge__charge--twin"});i.appendChild(H("circle",{class:"mlv-edge__charge-halo",cx:0,cy:0,r:K.CHARGE_HALO_R})),i.appendChild(H("circle",{class:"mlv-edge__charge-glow",cx:0,cy:0,r:K.CHARGE_GLOW_R})),i.appendChild(H("circle",{class:"mlv-edge__charge-core",cx:0,cy:0,r:K.CHARGE_CORE_R}));let s=H("animateMotion",{dur:e+"ms",repeatCount:String(K.SETTLE_PASSES),calcMode:"linear",rotate:"auto",fill:"freeze"});o!==null?s.setAttribute("begin",Cm(o+n/1e3)+"s"):n!==0&&s.setAttribute("begin",n+"ms");let r=H("mpath");return r.setAttribute("href","#"+t),r.setAttributeNS(Ns,"xlink:href","#"+t),s.appendChild(r),i.appendChild(s),i}function xl(t,e){return e>0?Ge(Math.min(100,t/e*100)):100}function Ge(t){return Math.round(t*100)/100}function Cm(t){return Math.round(t*1e3)/1e3}function El(t,e){return H("circle",{class:"mlv-edge__port mlv-edge__port--"+t,cx:Ge(e.x),cy:Ge(e.y),r:K.PORT_R})}function kl(t){return H("path",{class:"mlv-edge__dir",d:"M -3.4 -3.6 L 0 0 L -3.4 3.6",fill:"none",transform:"translate("+Ge(t.mid.x)+","+Ge(t.mid.y)+") rotate("+Ge(t.midAngle)+")"})}function _l(t){for(let o of wm)t.classList.remove(o);let e=t.style;if(e&&typeof e.removeProperty=="function")for(let o of bm)e.removeProperty(o);let n=t.querySelectorAll(".mlv-edge__flow, .mlv-edge__charge, .mlv-edge__port, .mlv-edge__dir");for(let o=0;oe.edges(),nodes:()=>e.nodes(),routes:()=>e.routes()}),this.motionWatch=new Wn(n=>{this.controller.setMotion(n),this.controller.syncCanvas(),this.cappedKey=null,this.stop()}),this.controller.setMotion(this.motionWatch.mode),this.controller.syncCanvas()}get enabled(){return this.controller.enabled}get motion(){return this.controller.motion}setEnabled(e){this.controller.setEnabled(e),e&&this.stop()}syncCanvas(){this.controller.syncCanvas()}clear(){this.cappedKey=null,this.controller.clear()}pulse(e){this.controller.pulse(e)}replay(){return this.controller.replay()}stream(e,n){let o=this.controller.stream(e,n);if(!o.capped)return;let i=n+":"+e;this.cappedKey!==i&&(this.cappedKey=i,this.host.toast(Tm(o.edges,this.controller.motion,n)))}stop(){if(this.latchedEdgeId){let i=this.routeById(this.latchedEdgeId);if(i){this.controller.pulse(i);return}}let e=this.host.lockedNodeId();if(e){this.stream(e,"lineage");return}let n=this.host.hoverNodeId();if(n){this.stream(n,"direct");return}let o=this.host.hoverEdgeId();if(o){let i=this.routeById(o);if(i){this.controller.pulse(i);return}}this.controller.clear()}setLatchedEdge(e){this.latchedEdgeId=e}routeById(e){for(let n of this.host.routes())if(n.id===e)return n;return null}incidentRoutes(e){return this.host.routes().filter(n=>n.source===e||n.target===e)}focusEdge(e){let n=this.host.edges().get(e);if(!n)return!1;let o=n.querySelector(".mlv-edge__hit");if(!o||typeof o.focus!="function")return!1;try{o.focus()}catch{return!1}return!0}blurFocusedEdge(){let e=this.host.canvas.ownerDocument,n=e?e.activeElement:null;return!n||!n.classList||!n.classList.contains("mlv-edge__hit")||typeof n.blur!="function"?!1:(n.blur(),!0)}edgeAnnouncement(e){let n=this.host.index(),o=n?n.nodeById.get(e.source):null,i=n?n.nodeById.get(e.target):null,s=o?o.label||o.qualname:e.source,r=i?i.label||i.qualname:e.target;return"Connection: "+s+" to "+r+(e.label?", "+e.label:"")+"."}destroy(){this.motionWatch.destroy()}};function Rm(t,e,n){let o=n.x-e.x,i=n.y-e.y,s=t.x-e.x,r=t.y-e.y,a=o*o+i*i,l=a>0?(s*o+r*i)/a:0;l<0?l=0:l>1&&(l=1);let d=s-l*o,c=r-l*i;return d*d+c*c}function Om(t,e){let n=t.points||[];if(n.length===0)return 1/0;if(n.length===1){let i=e.x-n[0].x,s=e.y-n[0].y;return Math.sqrt(i*i+s*s)}let o=1/0;for(let i=1;ithis.resolve(e))}enter(e){this.host.held&&this.host.held()||this.set(e)}drop(){let e=this.timer!==null;if(e&&(clearTimeout(this.timer),this.timer=null),!this.currentId){e&&this.host.close();return}let n=this.host.edges().get(this.currentId);n&&n.classList.remove("is-hover"),this.currentId=null,this.host.changed&&this.host.changed(null),this.host.close()}leave(e){this.currentId===e.id&&this.set(null)}reset(){this.currentId=null,this.timer!==null&&(clearTimeout(this.timer),this.timer=null),this.host.hide()}destroy(){this.reset()}resolve(e){if(this.host.held&&this.host.held())return;let n=this.host.routes();if(!n.length||this.host.canvas.classList.contains("is-panning"))return;let o=e.target;if(o&&typeof o.closest=="function"&&o.closest(Pm)){this.currentId&&this.set(null);return}let i=this.host.canvas.getBoundingClientRect(),s=this.host.viewport.vp,r=s.zoom>0?s.zoom:1,a={x:(e.clientX-i.left-s.x)/r,y:(e.clientY-i.top-s.y)/r},l=this.host.markers?this.host.markers():null,d=l&&l.size?Ml(n,l,a,ct):null;this.set(d||Sl(n,a,Hm/r))}set(e){let n=e?e.id:null;if(this.currentId===n)return;let o=this.host.edges();if(this.currentId){let i=o.get(this.currentId);i&&i.classList.remove("is-hover")}if(this.currentId=n,e){let i=o.get(e.id);if(i){i.classList.add("is-hover");let s=i.parentNode;s&&s.lastChild!==i&&s.appendChild(i)}}this.host.changed&&this.host.changed(e),this.intent(e)}intent(e){this.timer!==null&&(clearTimeout(this.timer),this.timer=null);let n=()=>{e?this.host.open(e):this.host.close()},o=e?this.host.openDelayMs:this.host.closeDelayMs;if(o<=0){n();return}this.timer=setTimeout(()=>{this.timer=null,n()},o)}};var $n=12,nt=8;function Nm(t,e,n,o,i){let s=o.w>0&&o.h>0,r=!1;if(s){let d=e-$n-o.h>=nt,c=i.h<=0||n+$n+o.h<=i.h-nt;r=!d&&c}let a=r?n+$n:e-$n;s&&(r?a=Math.min(a,Math.max(nt,i.h-nt-o.h)):a=Math.max(a,nt+o.h));let l=t;if(s&&i.w>0){let d=o.w/2,c=d+nt,u=i.w-d-nt;l=u>=c?Math.min(Math.max(t,c),u):i.w/2}return{left:Math.round(l),top:Math.round(a),below:r}}var Jn=class{constructor(e,n){this.root=f("div","mlv-tooltip"),this.root.setAttribute("role","tooltip"),this.root.hidden=!0,this.viewport=e,this.bounds=n||(()=>({w:0,h:0}))}showNode(e,n,o,i){let s=e.nodeById.get(n);if(!s)return;D(this.root),m(this.root,f("div","mlv-tooltip__title",s.label||s.qualname));let r=fe(s.basis);r&&m(this.root,f("div","mlv-tooltip__row mlv-tooltip__basis",r.replace("? ","")+", not observed")),s.sublabel&&m(this.root,f("div","mlv-tooltip__row",s.sublabel)),s.loc.file&&m(this.root,Qe("mlv-tooltip__loc",s.loc,"div"));let a=o.isGroup||o.collapsed;this.issueRows(a?e.subtreeIssues(n,i):e.issuesOf(n,i)),this.placeAt(o.x+o.w/2,o.y,o.y+o.h)}showEdge(e,n,o,i){let s=e.edgeById.get(n.id);D(this.root);let r=n.count<=1&&s&&s.label?s.label:"";m(this.root,f("div","mlv-tooltip__title",r||n.label||Ht(n.kind)));let a=s&&s.authoredKind?" · authored as "+s.authoredKind:"";m(this.root,f("div","mlv-tooltip__row",Ht(n.kind)+(n.subkind?" · "+n.subkind:"")+a));let l=s?fe(s.basis):"";l&&m(this.root,f("div","mlv-tooltip__row mlv-tooltip__basis",l.replace("? ","")+", not observed")),s&&s.loc.file&&m(this.root,Qe("mlv-tooltip__loc",s.loc,"div")),n.count>1&&m(this.root,f("div","mlv-tooltip__row",n.count+" merged connections")),this.issueRows(e.issuesOfEdges(n.ids,o)),i?this.placeAt(i.x,i.y-ct,i.y+ct):this.placeAt(n.mid.x,n.mid.y,n.mid.y)}issueRows(e){for(let n of e){let o=m(this.root,f("div","mlv-tooltip__row"));o.appendChild(Z(n.severity,12,"")),m(o,f("span",""," "+(n.short?n.short+" · ":"")+n.code+" "+n.title))}}placeAt(e,n,o){let i=this.viewport();this.root.hidden=!1,this.root.style.transform="translate(-50%, -100%)";let s=this.root.getBoundingClientRect(),r=Nm(e*i.zoom+i.x,n*i.zoom+i.y,o*i.zoom+i.y,{w:s.width,h:s.height},this.bounds());this.root.style.left=r.left+"px",this.root.style.top=r.top+"px",this.root.style.transform=r.below?"translate(-50%, 0)":"translate(-50%, -100%)"}hide(){this.root.hidden=!0}};function Ll(t){let e=f("div","mlv-state mlv-state--empty");e.setAttribute("role","status");let n=m(e,f("div","mlv-state__inner"));m(n,f("h2","mlv-state__title","No workflow steps")),m(n,f("p","mlv-state__body","This authored workflow does not contain any steps."));let o=t?t.diagnostics||[]:[];if(o.length){let i=m(n,f("ul","mlv-state__list"));for(let s of o.slice(0,8))m(i,f("li","",s.kind+" — "+s.message))}else t&&m(n,f("p","mlv-state__body","The author inspected "+t.workspace.filesAnalyzed+" files."));return e}function Il(t){let e=f("div","mlv-state mlv-state--empty");e.setAttribute("role","status");let n=m(e,f("div","mlv-state__inner"));m(n,f("h2","mlv-state__title","No steps match your filters"));let o=m(n,f("div","mlv-state__actions")),i=U("mlv-btn","Clear all filters");return _(i,"click",t),o.appendChild(i),e}var eo=class{constructor(){this.timers=[];this.root=f("div","mlv-toasts"),this.root.setAttribute("role","status")}show(e){let n=f("div","mlv-toast",e);for(this.root.appendChild(n);this.root.childElementCount>3&&this.root.firstElementChild;)this.root.removeChild(this.root.firstElementChild);let o=setTimeout(()=>{n.parentNode===this.root&&this.root.removeChild(n)},4e3);this.timers.push(o)}destroy(){for(let e of this.timers)clearTimeout(e);this.timers=[],D(this.root)}};var Fm=0,Bm=1,Dm=2,jm=100/3,qi=800,Gm=.999,Rl=40,Wm=6;function zm(t,e=qi){let n=typeof t.deltaMode=="number"?t.deltaMode:Fm,o=1;return n===Bm?o=jm:n===Dm&&(o=e>0?e:qi),{dx:Al(t.deltaX)*o,dy:Al(t.deltaY)*o,mode:n}}function Al(t){return typeof t=="number"&&isFinite(t)?t:0}function Vm(t,e){return t.ctrlKey||t.metaKey?"zoom":t.shiftKey?"pan":e.dx===0&&Math.abs(e.dy)>=Rl?"zoom":"pan"}function qm(t,e){let n=(t.ctrlKey||t.metaKey)&&Math.abs(e.dy){n.preventDefault();let o=t.getBoundingClientRect(),i=zm(n,o.height||t.clientHeight||qi);if(Vm(n,i)==="zoom"){e.zoomAt(qm(n,i),n.clientX-o.left,n.clientY-o.top);return}let s=Km(n,i);e.panBy(s.dx,s.dy)})}function Cl(t,e){let n=t.x-e.x,o=t.y-e.y;return Math.sqrt(n*n+o*o)}function Tl(t,e){return{x:(t.x+e.x)/2,y:(t.y+e.y)/2}}var to=class{constructor(e,n){this.points=new Map;this.lastDistance=0;this.lastMid=null;this.canvas=e,this.viewport=n}active(){return this.points.size>=2}down(e){this.points.set(e.pointerId,{x:e.clientX,y:e.clientY}),this.reseed()}up(e){this.points.delete(e.pointerId),this.reseed()}clear(){this.points.clear(),this.lastDistance=0,this.lastMid=null}move(e){if(!this.points.has(e.pointerId)||(this.points.set(e.pointerId,{x:e.clientX,y:e.clientY}),this.points.size<2))return!1;let n=this.pair();if(!n)return!1;let o=Cl(n[0],n[1]),i=Tl(n[0],n[1]),s=this.canvas.getBoundingClientRect();return this.lastDistance>0&&o>0&&(this.lastMid&&this.viewport.panBy(i.x-this.lastMid.x,i.y-this.lastMid.y),this.viewport.zoomAt(o/this.lastDistance,i.x-s.left,i.y-s.top)),this.lastDistance=o,this.lastMid=i,!0}pair(){let e=Array.from(this.points.keys()).sort((n,o)=>n-o);return e.length<2?null:[this.points.get(e[0]),this.points.get(e[1])]}reseed(){let e=this.pair();if(!e){this.lastDistance=0,this.lastMid=null;return}this.lastDistance=Cl(e[0],e[1]),this.lastMid=Tl(e[0],e[1])}};function Hl(t){let e=t.ownerDocument;return!e||!e.body||t.parentElement!==e.body?()=>{}:(e.documentElement.classList.add("mlv-fills-page"),e.body.classList.add("mlv-fills-page"),()=>{e.documentElement.classList.remove("mlv-fills-page"),e.body.classList.remove("mlv-fills-page")})}var Um=0;function Pl(t,e){t.classList.add("mlv-root"),t.setAttribute("data-theme",e),D(t);let n="mlv-shell"+ ++Um,o=f("div","mlv-body"),i=f("main","mlv-main");i.setAttribute("aria-labelledby",n+"-main-heading");let s=m(i,f("h2","mlv-sr","Pipeline diagram"));s.id=n+"-main-heading";let r=f("div","mlv-canvas");r.id=n+"-canvas",r.setAttribute("role","application"),r.setAttribute("aria-label","ML pipeline diagram"),r.setAttribute("data-lod","full"),r.tabIndex=0,i.appendChild(r);let a=f("a","mlv-skiplink","Skip to diagram");a.href="#"+r.id,_(a,"click",k=>{k.preventDefault();try{r.focus()}catch{}}),t.appendChild(a);let l=m(r,f("div","mlv-world")),d=m(l,f("div","mlv-layer mlv-layer--lanes")),c=H("svg",{class:"mlv-layer mlv-layer--edges",width:1,height:1});c.appendChild(pr());let u=H("g",{class:"mlv-bundles"}),h=H("g",{class:"mlv-edges"}),p=H("g",{class:"mlv-connectors"});c.appendChild(u),c.appendChild(h),c.appendChild(p),l.appendChild(c);let g=m(l,f("div","mlv-layer mlv-layer--nodes")),v=f("div","mlv-zoom"),b=m(v,f("span","mlv-zoom__level","100%"));b.setAttribute("aria-label","Zoom level");let x=m(r,f("div","mlv-statehost")),y=f("div","mlv-sr");return y.setAttribute("aria-live","polite"),y.setAttribute("role","status"),{body:o,main:i,canvas:r,world:l,lanesLayer:d,edgesSvg:c,bundleGroup:u,edgeGroup:h,connectorLayer:p,nodesLayer:g,zoomBar:v,zoomLevel:b,stateHost:x,live:y}}function Xm(t){let e=t;return!!e.classList&&e.classList.contains("mlv-root")}function Nl(t,e,n){let o=[],i=new to(t,e),s=!1,r=0,a=0;o.push(_(t,"pointerdown",c=>{let u=c.target;if(u&&u.closest&&u.closest(".mlv-overview"))return;if(i.down(c),i.active()){s=!1,t.classList.remove("is-panning");return}let h=c.target;if(!(h.closest&&h.closest(".mlv-node, .mlv-group__header, .mlv-phaseindex, .mlv-zoom, .mlv-edge__hit, .mlv-legend, .mlv-statehost, .mlv-tooltip, .mlv-toasts"))){s=!0,r=c.clientX,a=c.clientY,t.classList.add("is-panning");try{t.setPointerCapture(c.pointerId)}catch{}}})),o.push(_(t,"pointermove",c=>{i.move(c)||s&&(e.panBy(c.clientX-r,c.clientY-a),r=c.clientX,a=c.clientY)}));let l=c=>{c&&i.up(c),s=!1,t.classList.remove("is-panning")};o.push(_(t,"pointerup",l)),o.push(_(t,"pointercancel",l)),o.push(_(t,"pointerleave",l)),o.push(Ol(t,e)),o.push(_(t,"keydown",c=>n.onKeyDown(c)));let d=t.ownerDocument;return d&&o.push(_(d,"keydown",c=>{let u=c.target;u&&(u!==d.body&&u!==d.documentElement&&!Xm(u)||n.onKeyDown(c))})),o.push(_(t,"click",c=>{let u=c.target;(!u.closest||!u.closest(".mlv-node, .mlv-edge__hit, .mlv-group, .mlv-phaseindex, .mlv-overview, .mlv-legend"))&&n.onBackgroundClick()})),typeof window<"u"&&o.push(_(window,"resize",()=>n.onResize?n.onResize():e.apply())),o.push(()=>i.clear()),o}function Fl(t,e,n,o,i){D(t);let s=i.nodeIds[0];if(!s)return 0;let r=n.boxes.get(e.visibleRepresentative(s,o));if(!r)return 0;let a=ee(i.severity),l=0,d=new Set([r.id]);for(let c of i.nodeIds.slice(1)){let u=e.nodeById.get(c);if(!u)continue;let h=n.boxes.get(e.visibleRepresentative(c,o));!h||d.has(h.id)||(d.add(h.id),l++,t.appendChild(vr({x:r.x+r.w/2,y:r.y},{x:h.x+h.w/2,y:h.y},l,a,"Also affects — "+(u.label||u.id))))}return l}var no=class{constructor(e){this.hoverId=null;this.hoverTimer=null;this.pendingHover=null;this.focusLocked=!1;this.focusNodeId=null;this.focusShown=null;this.ctx=e}get hoverNodeId(){return this.hoverId}get lockedNodeId(){return this.focusLocked?this.focusShown??this.focusNodeId:null}get isFocusLocked(){return this.focusLocked}resetHover(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null),this.pendingHover=null,this.hoverId=null,this.trace(null,"is-tracing","direct"),this.ctx.tooltip.hide(),this.retraceFocus()}retraceFocus(){if(!this.focusLocked||!this.focusNodeId)return;let e=this.ctx,n=e.index(),o=n&&n.nodeById.has(this.focusNodeId)?n.visibleRepresentative(this.focusNodeId,e.collapsed()):null;if(o&&e.nodeEls().has(o)){this.focusShown=o,e.canvas.classList.remove("is-tracing"),this.trace(o,"is-focusing","lineage");return}this.focusLocked=!1,this.focusNodeId=null,this.focusShown=null,e.canvas.classList.remove("is-focusing"),this.trace(null,"is-focusing","lineage"),e.announce("Focus mode off.")}applySelection(e){let n=this.ctx;for(let o of n.nodeEls().values())o.classList.remove("is-selected");for(let o of n.edgeEls().values())o.classList.remove("is-selected");if(D(n.connectors),n.flow.setLatchedEdge(e&&e.kind==="edge"?e.id:null),!e){n.canvas.removeAttribute("aria-activedescendant"),this.retraceFocus(),n.flow.stop(),n.syncBundles();return}if(e.kind==="node"){let o=n.nodeEls().get(e.id);o&&(o.classList.add("is-selected"),n.canvas.setAttribute("aria-activedescendant",o.id))}else if(e.kind==="edge"){let o=n.edgeEls().get(e.id);o&&o.classList.add("is-selected")}else e.kind==="issue"&&this.highlightIssue(e.id);this.focusLocked&&e.kind==="node"?(this.focusNodeId=e.id,this.retraceFocus()):this.focusLocked&&this.trace(null,"is-focusing","lineage"),n.flow.stop(),n.syncBundles()}highlightIssue(e){let n=this.ctx,o=n.index(),i=n.frame();if(!o||!i)return;let s=o.issueById.get(e);if(!s)return;let r=s.nodeIds[0];if(r){let a=n.nodeEls().get(o.visibleRepresentative(r,n.collapsed()));a&&(a.classList.add("is-selected"),n.canvas.setAttribute("aria-activedescendant",a.id))}for(let a of s.edgeIds)for(let[l,d]of n.edgeEls())(d.getAttribute("data-edge-ids")||l).split(" ").indexOf(a)>=0&&d.classList.add("is-selected");Fl(n.connectors,o,i,n.collapsed(),s)}hoverIntent(e){if(e&&this.ctx.hoverHeld&&this.ctx.hoverHeld())return;if(this.hoverTimer!==null){if(this.pendingHover===e)return;clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null}if(this.hoverId===e)return;let n=e?400:120;if(n<=0){this.setHover(e);return}this.pendingHover=e,this.hoverTimer=setTimeout(()=>{this.hoverTimer=null,this.pendingHover=null,this.setHover(e)},n)}dropHover(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null),this.hoverId&&this.setHover(null)}setHover(e){if(this.hoverId===e||(this.hoverId=e,this.focusLocked))return;let n=this.ctx;if(!e){this.trace(null,"is-tracing","direct"),n.tooltip.hide(),n.flow.stop(),n.syncBundles();return}this.trace(e,"is-tracing","direct"),n.flow.stream(e,"direct"),n.syncBundles();let o=n.index(),i=n.frame();if(!o||!i)return;let s=i.boxes.get(e);s&&n.tooltip.showNode(o,e,s,r=>n.keep(r))}trace(e,n,o){let i=this.ctx;vl({nodes:i.nodeEls(),edges:i.edgeEls(),canvas:i.canvas},i.routes(),e,n,o),i.syncBundles()}toggleFocusMode(e){let n=this.ctx;if(this.focusLocked){this.focusLocked=!1,this.focusNodeId=null,this.focusShown=null,n.canvas.classList.remove("is-focusing"),this.trace(null,"is-focusing","lineage"),n.flow.clear(),n.syncBundles(),n.announce("Focus mode off.");let o=this.hoverId;o&&(this.hoverId=null,this.setHover(o));return}if(!e||e.kind!=="node"){n.toast("Select a node first, then press F to focus.");return}this.focusLocked=!0,this.focusNodeId=e.id,this.retraceFocus(),this.focusLocked&&(n.flow.stream(this.focusShown??e.id,"lineage"),n.announce("Focus mode on."))}destroy(){this.hoverTimer!==null&&(clearTimeout(this.hoverTimer),this.hoverTimer=null,this.pendingHover=null)}};function Bl(t,e,n,o){let i=n?t.querySelector(".mlv-group__header"):t;if(!i)return;let s=t.querySelector(".mlv-group__chevron-btn");s&&(_(s,"click",d=>{d.preventDefault(),d.stopPropagation(),o.isGroup(e)&&o.toggleCollapse(e)}),_(s,"dblclick",d=>{d.preventDefault(),d.stopPropagation()}));let r=n||o.isGroup(e),a=null,l=()=>{a!==null&&(clearTimeout(a),a=null)};o.addDisposer(l),_(i,"click",d=>{if(d.stopPropagation(),!r){o.activateNode(e,d);return}l(),a=setTimeout(()=>{a=null,o.activateNode(e)},220)}),_(i,"dblclick",d=>{d.preventDefault(),d.stopPropagation(),l(),o.isGroup(e)?o.toggleCollapse(e):r||o.openNode(e,!1)}),_(i,"pointerenter",()=>o.hoverIntent(e)),_(i,"pointerleave",()=>o.hoverIntent(null)),_(i,"keydown",d=>{d.key==="Enter"&&!d.ctrlKey&&!d.metaKey&&!d.shiftKey?(d.preventDefault(),d.stopPropagation(),o.openNode(e,d.altKey)):d.key===" "&&o.isGroup(e)&&(d.preventDefault(),d.stopPropagation(),o.toggleCollapse(e))})}function Dl(t,e,n){let o=t.querySelector(".mlv-edge__hit");if(!o)return;let i=o;_(i,"click",s=>{s.stopPropagation(),n.activateEdge(e.id,s)}),_(i,"dblclick",s=>{s.preventDefault(),s.stopPropagation(),n.openEdge(e.id,!1)}),_(i,"pointerenter",()=>n.enterEdge(e)),_(i,"pointerleave",()=>n.leaveEdge(e)),_(i,"keydown",s=>{s.key!=="Enter"||s.ctrlKey||s.metaKey||s.shiftKey||(s.preventDefault(),s.stopPropagation(),n.openEdge(e.id,s.altKey))}),_(i,"focus",()=>{t.classList.add("is-hover"),n.syncBundles(),n.pulse(e)}),_(i,"blur",()=>{t.classList.remove("is-hover"),n.syncBundles(),n.stopFlow()})}var oo=class{constructor(e,n){this.overviewReturn=null;this.panelWidth=0;this.indexFolded=!1;this.indexHidden=!1;this.arrowLane=null;this.hoverHeld=!1;this.pointerAt=null;this.heldAt=null;this.index=null;this.frameData=null;this.routes=[];this.labelPlan=null;this.collapsedSet=new Set;this.staleFiles=new Map;this.nodeEls=new Map;this.edgeEls=new Map;this.markerPoints=new Map;this.bundles=new Pn;this.mountSerial=hr();this.disposers=[];this.lastArea=null;this.host=n,this.canvasEl=e.canvas,this.worldEl=e.world,this.lanesLayer=e.lanesLayer,this.edgesSvg=e.edgesSvg,this.bundleGroup=e.bundleGroup,this.edgeGroup=e.edgeGroup,this.connectorLayer=e.connectorLayer,this.nodesLayer=e.nodesLayer,this.zoomLevelEl=e.zoomLevel,this.stateHost=e.stateHost,this.tooltip=new Jn(()=>this.viewport.vp,()=>this.viewport.size()),this.canvasEl.appendChild(this.tooltip.root),this.phaseIndex=new qn({go:i=>this.goToPhase(i,{focusCanvas:!0}),fold:i=>{this.indexFolded=i,this.syncPhaseIndex(),this.host.onPhaseIndexChanged()}}),this.canvasEl.appendChild(this.phaseIndex.root),this.overview=new Xn({go:(i,s)=>{s&&this.holdHover(),this.goToPhase(i,{focusCanvas:!0})},back:()=>this.closeOverview(!0)}),this.canvasEl.appendChild(this.overview.root),this.toasts=new eo,this.canvasEl.appendChild(this.toasts.root),this.viewport=new zn(this.canvasEl,this.worldEl,i=>{this.zoomLevelEl.textContent=Math.round(i.zoom*100)+"%";let s=this.viewport.size();this.phaseIndex.setInView(i,s.w,s.h),this.lastArea=this.viewport.visibleArea(),this.host.onViewportChange(i)}),this.viewport.setCovered(()=>this.indexCovered());for(let i of Nl(this.canvasEl,this.viewport,{onKeyDown:s=>this.host.onKeyDown(s),onBackgroundClick:()=>this.host.onBackgroundClick(),onResize:()=>this.handleResize()}))this.disposers.push(i);let o=typeof window<"u"?window.ResizeObserver:void 0;if(o){let i=new o(()=>this.handleResize());i.observe(this.canvasEl),this.disposers.push(()=>i.disconnect())}this.flow=new Zn({canvas:this.canvasEl,edges:()=>this.edgeEls,nodes:()=>this.nodeEls,routes:()=>this.routes,index:()=>this.index,hoverNodeId:()=>this.emphasis.hoverNodeId,lockedNodeId:()=>this.emphasis.lockedNodeId,hoverEdgeId:()=>this.edgeHover.hoveredId,toast:i=>this.toasts.show(i)}),this.emphasis=new no({canvas:this.canvasEl,connectors:this.connectorLayer,flow:this.flow,tooltip:this.tooltip,nodeEls:()=>this.nodeEls,edgeEls:()=>this.edgeEls,routes:()=>this.routes,index:()=>this.index,frame:()=>this.frameData,collapsed:()=>this.collapsedSet,syncBundles:()=>this.syncBundles(),keep:i=>this.host.keep(i),announce:i=>this.host.announce(i),toast:i=>this.toasts.show(i),hoverHeld:()=>this.hoverHeld}),this.edgeHover=new Qn({canvas:this.canvasEl,viewport:this.viewport,routes:()=>this.routes,edges:()=>this.edgeEls,markers:()=>this.markerPoints,open:i=>{this.index&&(this.tooltip.showEdge(this.index,i,s=>this.host.keep(s),this.markerPoints.get(i.id)),this.flow.pulse(i))},close:()=>{this.emphasis.hoverNodeId||this.tooltip.hide(),this.flow.stop()},hide:()=>this.tooltip.hide(),changed:()=>this.syncBundles(),openDelayMs:400,closeDelayMs:120,held:()=>this.hoverHeld}),this.disposers.push(_(this.canvasEl,"pointermove",i=>this.trackPointer(i))),this.disposers.push(_(this.canvasEl,"pointerdown",()=>this.releaseHover())),this.disposers.push(this.edgeHover.wire())}holdHover(){this.hoverHeld=!0,this.heldAt=this.pointerAt,this.emphasis.dropHover(),this.edgeHover.drop(),this.tooltip.hide()}get hoverIsHeld(){return this.hoverHeld}releaseHover(){this.hoverHeld=!1,this.heldAt=null}trackPointer(e){let n={x:e.clientX,y:e.clientY};if(this.pointerAt=n,!this.hoverHeld)return;if(!this.heldAt){this.heldAt=n;return}if(n.x===this.heldAt.x&&n.y===this.heldAt.y)return;this.releaseHover();let o=e.target,i=o&&typeof o.closest=="function"?o.closest(".mlv-node[data-node-id], .mlv-group__header"):null,s=i&&i.classList.contains("mlv-group__header")?i.closest("[data-node-id]"):i,r=s?s.getAttribute("data-node-id"):null;r&&this.canvasEl.contains(s)&&this.emphasis.hoverIntent(r)}get frame(){return this.frameData}get collapsed(){return this.collapsedSet}setIndex(e){this.index=e}setCollapsed(e){this.collapsedSet=new Set(e)}get phaseIndexFolded(){return this.indexFolded}get phaseIndexHidden(){return this.indexHidden}setPhaseIndex(e){typeof e.folded=="boolean"&&(this.indexFolded=e.folded),typeof e.hidden=="boolean"&&(this.indexHidden=e.hidden),this.syncPhaseIndex()}get phaseIndexElement(){return this.phaseIndex.root}phasesInView(){return this.phaseIndex.inView()}setPanelWidth(e){e>0&&(this.panelWidth=e,this.syncPhaseIndex())}phaseIndexUnavailable(){return this.phaseIndex.available?null:"Shown when the diagram has two or more phases"}closePhasePopover(){return this.phaseIndex.closePopover()}syncPhaseIndex(){let e=this.canvasEl.getBoundingClientRect().height,n=!(this.panelWidth>0)||this.panelWidth>=1e3,o=!(e>0)||e>=350;this.phaseIndex.setMode({roomy:n&&o,folded:this.indexFolded,hidden:this.indexHidden,suppressed:this.overview.open})}indexCovered(){let e=this.canvasEl.getBoundingClientRect(),n=this.viewport.size();return this.phaseIndex.coveredRect(n.w,n.h,e.width>0?e.left:0,e.width>0?e.top:0)}renderPhaseIndex(){if(!this.index||!this.frameData)return;let e=this.frameData.lanes.map(o=>({id:o.id,label:o.label,index:this.index.phaseIndexOf(o.id),steps:o.nodeCount,counts:this.index.laneCounts(o.id,i=>this.host.keep(i)),rect:{x:o.x,y:o.y,w:o.w,h:o.h}}));this.phaseIndex.setRows(e),this.syncPhaseIndex();let n=this.viewport.size();this.phaseIndex.setInView(this.viewport.vp,n.w,n.h)}get overviewOpen(){return this.overview.open}get overviewElement(){return this.overview.root}overviewLayout(){return this.overview.layout}openOverview(){if(!this.index||!this.frameData||this.overview.open||!this.index.lanes.length)return!1;let e=this.canvasEl.ownerDocument,n=e?e.activeElement:null;this.overviewReturn=n&&n!==e.body?n:null,this.tooltip.hide();let o=Un(this.index,l=>this.host.keep(l)),i=this.viewport.size(),s=this.phaseIndex.currentPhase(),r=Math.max(0,o.phases.findIndex(l=>l.id===s));this.overview.show(o,i.w,i.h,r),this.syncPhaseIndex();let a=o.phases.length;return this.host.announce("Phase overview: "+a+(a===1?" phase":" phases")+", "+o.totalSteps+(o.totalSteps===1?" step":" steps")+". Arrow keys move between phases, Enter goes to one, Escape goes back."),!0}closeOverview(e){if(!this.overview.open)return!1;let n=this.canvasEl.ownerDocument,o=!!n&&!!n.activeElement&&this.overview.root.contains(n.activeElement);this.overview.hide(),this.syncPhaseIndex();let i=this.overviewReturn;return this.overviewReturn=null,e?(this.host.announce("Back to the diagram."),o&&this.focusSafely(i&&i.isConnected&&!this.overview.root.contains(i)?i:this.canvasEl)):o&&this.focusSafely(this.canvasEl),!0}toggleOverview(){this.overview.open?this.closeOverview(!0):this.openOverview()}goToPhase(e,n={}){if(!this.index||!this.frameData)return;let o=this.frameData.lanes.findIndex(l=>l.id===e);if(o<0)return;let i=this.closeOverview(!1),s=this.viewport.size(),r=nl(this.frameData,o,s.w,s.h);r&&this.viewport.animateTo({x:r.x,y:r.y,zoom:r.zoom}),this.arrowLane=e;let a=this.frameData.lanes[o];this.host.announce("Phase "+(this.index.phaseIndexOf(e)+1)+": "+a.label+", "+a.nodeCount+(a.nodeCount===1?" step":" steps")+"."),(n.focusCanvas||i)&&this.focusSafely(this.canvasEl)}takeArrowLane(){let e=this.arrowLane;return this.arrowLane=null,e}clearArrowLane(){this.arrowLane=null}focusSafely(e){try{e.focus()}catch{}}setStale(e){this.staleFiles=new Map(e.map(n=>[n.path,n.reason]))}nodeElement(e){return this.nodeEls.get(e)}focusTarget(e){let n=null;if(e&&e.kind==="node"&&this.index){let o=this.nodeEls.get(this.index.visibleRepresentative(e.id,this.collapsedSet));n=o&&o.classList.contains("mlv-group")?o.querySelector(".mlv-group__header"):o}else e&&e.kind==="edge"&&(n=this.edgeEls.get(e.id)?.querySelector(".mlv-edge__hit"));this.focusSafely(n||this.canvasEl)}relayout(){this.index&&(this.frameData=_a(this.index,this.collapsedSet),this.routes=Ta(this.index,this.frameData,this.collapsedSet),this.labelPlan=cr(this.frameData,this.routes),this.viewport.setContent(this.frameData.width,this.frameData.height,this.frameData),this.render())}planInputs(){return!this.index||!this.frameData?null:{index:this.index,frame:this.frameData,routes:this.routes,labels:this.labelPlan?this.labelPlan.placements:null,mountSerial:this.mountSerial,keep:e=>this.host.keep(e),staleFiles:this.staleFiles}}scenePlan(){let e=this.planInputs();return e?jn(e):null}render(){let e=this.planInputs();if(!e)return;let n=this.nodeWiring(),o=this.edgeWiring(),i=Ua({world:this.worldEl,lanes:this.lanesLayer,edgesSvg:this.edgesSvg,bundleGroup:this.bundleGroup,edgeGroup:this.edgeGroup,connectors:this.connectorLayer,nodes:this.nodesLayer},{...e,wireNode:(s,r,a)=>Bl(s,r,a,n),wireEdge:(s,r)=>Dl(s,r,o)});this.nodeEls=i.nodeEls,this.edgeEls=i.edgeEls,this.markerPoints=new Map;for(let s of i.plan.edges)s.severity&&this.markerPoints.set(s.route.id,ei(s));if(this.bundles.adopt(i.bundleEls,i.plan.bundles.map(s=>s.bundle)),this.syncBundles(),this.emphasis.resetHover(),this.edgeHover.reset(),this.flow.clear(),this.renderPhaseIndex(),this.renderEmptyState(),this.overview.open&&this.index){let s=this.viewport.size();this.overview.redraw(Un(this.index,r=>this.host.keep(r)),s.w,s.h)}}syncShortCanvas(){if(this.syncPhaseIndex(),this.overview.open&&this.index){let e=this.viewport.size();this.overview.redraw(Un(this.index,n=>this.host.keep(n)),e.w,e.h)}}handleResize(){let e=this.host.keptTarget(),n=e?this.targetRect(e):null,o=this.lastArea,i=!!(n&&o&&this.viewport.isVisible(n,o));n&&i?(this.viewport.acceptResize(),this.viewport.apply(),this.viewport.isVisible(n)||this.viewport.revealRect(n)):this.viewport.onResize(),this.syncShortCanvas()}afterSheetToggle(){this.viewport.acceptResize(),this.handleResize(),this.lastArea=this.viewport.visibleArea()}afterChromeChange(){this.viewport.refitIfFitted(),this.syncShortCanvas()}renderEmptyState(){let e=this.stateHost.querySelector(".mlv-state--empty");if(e&&this.stateHost.removeChild(e),!this.index)return;let n=this.index.graph;n.nodes.length===0?this.stateHost.appendChild(Ll(n)):this.frameData&&this.frameData.boxes.size===0&&this.stateHost.appendChild(Il(()=>this.host.clearFilters()))}nodeWiring(){return{isGroup:e=>!!this.index&&this.index.isGroup(e),toggleCollapse:e=>this.toggleCollapse(e),hoverIntent:e=>this.emphasis.hoverIntent(e),activateNode:(e,n)=>this.host.activateNode(e,n),openNode:(e,n)=>this.host.openNode(e,n),addDisposer:e=>this.disposers.push(e)}}edgeWiring(){return{activateEdge:(e,n)=>this.host.activateEdge(e,n),openEdge:(e,n)=>this.host.openEdge(e,n),enterEdge:e=>this.edgeHover.enter(e),leaveEdge:e=>this.edgeHover.leave(e),syncBundles:()=>this.syncBundles(),pulse:e=>this.flow.pulse(e),stopFlow:()=>this.flow.stop()}}incidentRoutes(e){return this.flow.incidentRoutes(e)}focusEdge(e){return this.flow.focusEdge(e)}blurFocusedEdge(){return this.flow.blurFocusedEdge()}edgeAnnouncement(e){return this.flow.edgeAnnouncement(e)}applySelection(e){this.emphasis.applySelection(e)}hoverIntent(e){this.emphasis.hoverIntent(e)}setHover(e){this.emphasis.setHover(e)}get isFocusLocked(){return this.emphasis.isFocusLocked}toggleFocusMode(e){this.emphasis.toggleFocusMode(e)}syncBundles(){this.bundles.sync(this.edgeEls)}toggleCollapse(e){if(!this.frameData)return;let n=this.frameData.boxes.get(e),o=this.viewport.vp.zoom,i=n?{x:n.x*o+this.viewport.vp.x,y:n.y*o+this.viewport.vp.y}:null;this.collapsedSet.has(e)?this.collapsedSet.delete(e):this.collapsedSet.add(e),this.relayout();let s=this.frameData.boxes.get(e);s&&i&&this.viewport.set({x:i.x-s.x*this.viewport.vp.zoom,y:i.y-s.y*this.viewport.vp.zoom});let r=this.index?this.index.nodeById.get(e):null,a=this.index?this.index.descendantCount(e):0;this.host.afterCollapse(),this.host.announce("Group "+(r?r.label:e)+(this.collapsedSet.has(e)?" collapsed, "+a+(a===1?" step":" steps")+" hidden.":" expanded."))}expandAncestors(e){if(!this.index)return!1;let n=!1;for(let o of this.index.ancestors(e))this.collapsedSet.delete(o)&&(n=!0);return n&&this.relayout(),n}centerOnNode(e,n){if(!this.index||!this.frameData)return;let o=this.index.visibleRepresentative(e,this.collapsedSet),i=this.frameData.boxes.get(o);if(!i||(this.viewport.centerOn(i),!n))return;let s=this.nodeEls.get(o);s&&(s.classList.add("is-pulse"),setTimeout(()=>s.classList.remove("is-pulse"),700))}revealNode(e,n){if(!this.index||!this.frameData)return;let o=this.frameData.boxes.get(this.index.visibleRepresentative(e,this.collapsedSet));if(o){if(this.viewport.vp.zoom>=Be){this.centerOnNode(e,n);return}this.viewport.centerOn(o,Math.max(this.viewport.vp.zoom,this.readableZoomFor(o))),n&&this.pulseNode(this.index.visibleRepresentative(e,this.collapsedSet))}}keepInView(e){let n=this.targetRect(e);n&&!this.viewport.isVisible(n)&&this.viewport.centerOn(n)}refresh(e){let n=this.canvasEl.ownerDocument,o=n?n.activeElement:null,i=!!o&&o!==this.canvasEl&&this.canvasEl.contains(o)&&typeof o.closest=="function",s=i&&o.closest("[data-node-id]")?.getAttribute("data-node-id")||null,r=i&&!s&&o.classList.contains("mlv-edge__hit")&&o.closest("[data-edge-id]")?.getAttribute("data-edge-id")||null;this.render(),this.applySelection(e);let a=null;if(s){let l=this.nodeEls.get(s);a=l&&l.classList.contains("mlv-group")?l.querySelector(".mlv-group__header"):l}else r&&(a=this.edgeEls.get(r)?.querySelector(".mlv-edge__hit"));if(a)try{a.focus()}catch{}}frameIssue(e){if(!this.index||!this.frameData)return;let n=this.index.issueById.get(e);if(!n)return;let o=[],i=[];for(let d of n.nodeIds){let c=this.targetRect({kind:"node",id:d});c&&o.push(c)}for(let d of n.edgeIds){let c=this.targetRect({kind:"edge",id:d});c&&i.push(c)}let s=o.concat(i);if(!s.length)return;let r=d=>{let c=Math.min(...d.map(h=>h.x)),u=Math.min(...d.map(h=>h.y));return{x:c,y:u,w:Math.max(...d.map(h=>h.x+h.w))-c,h:Math.max(...d.map(h=>h.y+h.h))-u}},a=r(s),l=!o.length||!i.length||this.viewport.fitsAt(a,zt)?a:r(o);this.viewport.frameRect(l,s[0],De),n.nodeIds[0]&&this.pulseNode(this.index.visibleRepresentative(n.nodeIds[0],this.collapsedSet))}frameEdge(e){if(!this.index||!this.frameData)return;let n=this.index.edgeById.get(e),o=this.targetRect({kind:"edge",id:e});if(!n||!o)return;let i=g=>this.frameData.boxes.get(this.index.visibleRepresentative(g,this.collapsedSet)),s=i(n.source),r=i(n.target);this.viewport.frameRect(o,s||o,De);let a=this.viewport.covered();if(!r||!a||this.viewport.isVisible(r))return;let l=48,{w:d,h:c}=this.viewport.visibleArea(),u=Math.min((d-l)/Math.max(1,o.w),(c-a.h-l)/Math.max(1,o.h)),h=Math.min((d-a.w-l)/Math.max(1,o.w),(c-l)/Math.max(1,o.h)),p=Math.min(this.viewport.vp.zoom,Math.max(u,h));p>=zt&&this.viewport.centerOn(o,p)}revealEdge(e){let n=this.targetRect({kind:"edge",id:e});n&&(this.viewport.vp.zoomn.classList.remove("is-pulse"),700))}zoomStep(e){let n=this.viewport.size();this.viewport.zoomAt(e>0?1.2:1/1.2,n.w/2,n.h/2)}fit(){this.viewport.fit()}fitWhole(){this.viewport.fitWhole()}zoomToNode(e){if(!this.index||!this.frameData)return;let n=this.frameData.boxes.get(this.index.visibleRepresentative(e,this.collapsedSet));n&&this.viewport.zoomToBox(n)}nextSelection(e,n){let o=this.index,i=this.frameData;if(!o||!i)return null;let s=d=>o.nodeById.has(d)?o.visibleRepresentative(d,this.collapsedSet):null,r=d=>{let c=s(d);return c&&i.boxes.has(c)?Ra(o,i,c,n):void 0},a=d=>{let c=o.edgeById.get(d);return c&&Oa(i,s(c.source),s(c.target),n)||void 0},l;if(e&&e.kind==="node")l=r(e.id);else if(e&&e.kind==="edge")l=a(e.id);else if(e&&e.kind==="issue"){let d=o.issueById.get(e.id);d&&d.nodeIds.length&&(l=r(d.nodeIds[0])),l===void 0&&d&&d.edgeIds.length&&(l=a(d.edgeIds[0]))}return l===void 0&&(l=Ci(i)),l?{id:l.id,visible:this.viewport.isVisible(l)}:null}labelOf(e){let n=this.nodeEls.get(e);return n?n.getAttribute("aria-label"):null}toast(e){this.toasts.show(e)}destroy(){this.phaseIndex.destroy(),this.overview.destroy(),this.emphasis.destroy(),this.edgeHover.destroy();for(let e of this.disposers)try{e()}catch{}this.disposers=[],this.flow.destroy(),this.toasts.destroy()}};var jl='button, [role="button"], a[href], select';function Gl(t){return typeof t.getClientRects=="function"&&t.getClientRects().length>0}var xt=class{constructor(e,n="input, textarea"){this.activeIndex=0;this.activeEl=null;this.disposers=[];this.container=e,this.ownStopSelector=n,this.disposers.push(_(e,"keydown",o=>this.onKey(o))),this.disposers.push(_(e,"focusin",o=>{let i=o.target;if(!i||typeof i.closest!="function")return;let r=this.items().indexOf(i);r>=0&&this.setActive(r,!1)})),this.sync()}items(){let e=this.container.querySelectorAll(jl),n=Gl(this.container),o=[];for(let i=0;i=0?s:r>=0?r:this.activeEl?Math.min(this.activeIndex,e.length-1):0;this.activeIndex=a,(s>=0||this.activeEl)&&(this.activeEl=e[a]);for(let l=0;l"u")return!1;let t=navigator,e=t.userAgentData&&t.userAgentData.platform||t.platform||t.userAgent||"";return/mac/i.test(e)}catch{return!1}}function ot(t,e=Xi()){return e?t.replace(/^Mod\+/,"⌘").replace(/^Ctrl\+/,"⌃").replace(/^Alt\+/,"⌥").replace(/^Shift\+/,"⇧"):t.replace(/^Mod\+/,"Ctrl+")}var Wl=[{id:"search",label:"Search steps and findings",icon:"search",keys:"Mod+F",folded:!0},{id:"about",label:"About this revision",icon:"info",folded:!0},{id:"exceptions",label:"Not observed",icon:"notobserved",check:!0,folded:!0},{id:"review",label:"Review the claims",icon:"review",keys:"R",group:!0},{id:"overview",label:"Phase overview",icon:"phases",keys:"Shift+0"},{id:"legend",label:"Legend",icon:"legend",keys:"L",check:!0,group:!0},{id:"flow",label:"Connection flow animation",icon:"flow",keys:"A",check:!0},{id:"phaseindex",label:"Phase index",icon:"phaseindex",check:!0},{id:"rail",label:"Side panel",icon:"rail",keys:"B",check:!0},{id:"fit",label:"Fit the whole diagram",icon:"fit",group:!0},{id:"zoomsel",label:"Zoom to the selection",icon:"target",keys:"Z"},{id:"svg",label:"Export SVG…",icon:"image",group:!0},{id:"png",label:"Export PNG…",icon:"image"},{id:"copy-svg",label:"Copy SVG",icon:"copy"},{id:"shortcuts",label:"Keyboard shortcuts",icon:"keyboard",keys:"?",group:!0}],zl=["svg","png","copy-svg"],$m=0,io=class{constructor(e,n=()=>{}){this.items=new Map;this.openState=!1;this.disposers=[];this.onPick=e,this.beforeOpen=n;let o="mlv-more"+ ++$m;this.button=he("mlv-btn mlv-btn--icon mlv-btn--more","More actions"),this.button.title="More: review, phase overview, legend, fit, export, shortcuts",this.button.appendChild(B("more",16)),this.button.setAttribute("aria-haspopup","menu"),this.button.setAttribute("aria-expanded","false"),this.button.setAttribute("aria-controls",o),_(this.button,"click",s=>{s.preventDefault(),s.stopPropagation(),this.setOpen(!this.openState)}),_(this.button,"keydown",s=>{if(s.key==="Escape"){if(!this.openState)return;s.preventDefault(),s.stopPropagation(),this.setOpen(!1);return}s.key!=="ArrowDown"&&s.key!=="ArrowUp"||(s.preventDefault(),s.stopPropagation(),this.setOpen(!0),this.focusItem(s.key==="ArrowDown"?0:this.shown().length-1))}),this.panel=f("div","mlv-moremenu"),this.panel.id=o,this.panel.setAttribute("role","menu"),this.panel.setAttribute("aria-label","More actions"),this.panel.hidden=!0;for(let s of Wl){if(s.group){let a=m(this.panel,f("div","mlv-moremenu__sep"));a.setAttribute("role","separator"),a.setAttribute("data-sep-before",s.id)}let r=f("button","mlv-moremenu__item");r.type="button",r.tabIndex=-1,r.setAttribute("role",s.check?"menuitemcheckbox":"menuitem"),r.setAttribute("data-more-item",s.id),zl.indexOf(s.id)>=0&&r.setAttribute("data-export-action",s.id),s.folded&&r.setAttribute("data-folded","1"),r.appendChild(B(s.icon,14)),m(r,f("span","mlv-moremenu__label",s.label)),s.id==="phaseindex"&&(m(r,f("span","mlv-moremenu__note")).hidden=!0),s.keys&&m(r,f("kbd","mlv-moremenu__keys",ot(s.keys))).setAttribute("aria-hidden","true"),_(r,"click",a=>{if(a.preventDefault(),r.disabled)return;let l=a.detail===0;this.setOpen(!1),this.onPick(s.id,l)}),this.panel.appendChild(r),this.items.set(s.id,r)}_(this.panel,"keydown",s=>this.onKey(s));let i=typeof document>"u"?null:document;i&&(this.disposers.push(_(i,"pointerdown",s=>{if(!this.openState)return;let r=s.target;r&&(this.panel.contains(r)||this.button.contains(r))||this.setOpen(!1)})),this.disposers.push(_(i,"keydown",s=>{if(!this.openState||s.key!=="Escape")return;s.preventDefault(),s.stopPropagation();let r=i.activeElement,a=!!r&&(this.panel.contains(r)||this.button.contains(r));this.setOpen(!1),a&&this.focusButton()},!0)))}get open(){return this.openState}update(e){for(let[o,i]of this.items){let s=Wl.find(a=>a.id===o),r=!s.folded||(o==="search"?e.folded.search:o==="about"?e.folded.about:e.folded.exceptions);if(i.hidden=!r,o==="about"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.aboutLabel),i.title="Show About: the request, coverage, limitations and provenance"}if(o==="exceptions"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.exceptionsLabel),i.title=e.exceptionsOn?"The observed claims are faded; choose to show them again":"Fade the observed claims so these stand out"}if(o==="review"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.walking?"End the review":"Review the claims"),i.title=e.walking?"End the review walk (Escape); it remembers its place for this revision":"Walk the claims one by one, each opened and highlighted in the editor beside; focus stays here",i.disabled=!e.canExport}if(o==="rail"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.railMode==="sheet"?"Bottom panel":"Side panel"),i.title=e.railMode==="sheet"?"Open or collapse the bottom panel":"Show or hide the side panel"}if(o==="overview"){let a=i.querySelector(".mlv-moremenu__label");a&&(a.textContent=e.overviewOpen?"Close the phase overview":"Phase overview"),i.title=e.overviewOpen?"Back to the diagram where you were (Escape)":"Every phase as a block of its step titles, with the connections between phases; choose one to go there",i.disabled=!e.canExport}if(s.check){let a=o==="legend"?e.legendOpen:o==="flow"?e.flowOn:o==="phaseindex"?e.phaseIndexShown&&!e.phaseIndexUnavailable:o==="exceptions"?e.exceptionsOn:e.railOpen;i.setAttribute("aria-checked",a?"true":"false")}if(o==="phaseindex"){let a=i.querySelector(".mlv-moremenu__note");a&&(a.textContent=e.phaseIndexUnavailable||"",a.hidden=!e.phaseIndexUnavailable),i.disabled=!!e.phaseIndexUnavailable,i.title=e.phaseIndexUnavailable?"Phase index: "+e.phaseIndexUnavailable.charAt(0).toLowerCase()+e.phaseIndexUnavailable.slice(1):"Show or hide the phase index: each phase with its findings and steps, the phases in view marked"}o==="zoomsel"&&(i.disabled=!e.hasSelection),zl.indexOf(o)>=0&&(i.disabled=!e.canExport),i.disabled?i.setAttribute("aria-disabled","true"):i.removeAttribute("aria-disabled")}let n=this.shown()[0];for(let o of Array.from(this.panel.querySelectorAll(".mlv-moremenu__sep")))o.hidden=!!n&&o.getAttribute("data-sep-before")===n.getAttribute("data-more-item")}setOpen(e){if(this.openState!==e)if(this.openState=e,this.panel.hidden=!e,this.button.setAttribute("aria-expanded",e?"true":"false"),e){try{this.beforeOpen()}catch{}this.position(),this.focusItem(0)}else{let n=this.panel.ownerDocument?this.panel.ownerDocument.activeElement:null;n&&this.panel.contains(n)&&this.focusButton()}}shown(){return Array.from(this.items.values()).filter(e=>!e.hidden&&!e.disabled)}position(){let e=null;try{e=this.button.getBoundingClientRect()}catch{e=null}e&&(this.panel.style.position="fixed",this.panel.style.top=Math.round(e.bottom+4)+"px",this.panel.style.left="auto",this.panel.style.right=Math.max(8,Math.round(Jm()-e.right))+"px")}focusItem(e){let n=this.shown();if(!n.length)return;let o=(e%n.length+n.length)%n.length;try{n[o].focus()}catch{}}focusButton(){try{this.button.focus()}catch{}}onKey(e){if(e.key==="Escape"){e.preventDefault(),e.stopPropagation(),this.setOpen(!1);return}if(e.key==="Tab"){this.setOpen(!1);return}let n=this.shown(),o=n.indexOf(e.target);if(!(o<0)){if(e.key==="ArrowDown")this.focusItem(o+1);else if(e.key==="ArrowUp")this.focusItem(o-1);else if(e.key==="Home")this.focusItem(0);else if(e.key==="End")this.focusItem(n.length-1);else return;e.preventDefault(),e.stopPropagation()}}destroy(){for(let e of this.disposers)try{e()}catch{}this.disposers=[]}};function Jm(){try{if(typeof window<"u"&&window.innerWidth)return window.innerWidth}catch{}return 1280}var Yi={mid:620,wide:1e3,full:1200};function eg(t){return t>0?tt+" "+(t===1?e:n);function Vl(t){let e=t.steps+t.connections+t.findings,n=[];return t.steps&&n.push(We(t.steps,"step","steps")),t.connections&&n.push(We(t.connections,"connection","connections")),t.findings&&n.push(We(t.findings,"finding","findings")),e+" not observed ("+n.join(", ")+")"}function tg(t){let e=t.coverage.limitations.length;return"Coverage: "+t.coverage.status+(e?" · "+We(e,"limitation","limitations"):"")}var ql=4;function ng(t){let e=t.steps+t.connections+t.findings;return e+(e===1?" claim":" claims")+" not observed"}var og=0,so=class{constructor(e,n){this.sevButtons=new Map;this.roving=null;this.layout="full";this.width=0;this.fitLevel=0;this.hasDocument=!1;this.hasExceptions=!1;this.sevShown=new Set;this.searchOpen=!1;this.lastAboutLabel="About this revision";this.state=null;this.cb=e;let o="mlv"+ ++og;this.header=f("header","mlv-header"),this.header.setAttribute("data-layout",this.layout),this.title=m(this.header,f("h1","mlv-header__title","")),this.toolbar=m(this.header,f("div","mlv-header__bar")),this.toolbar.setAttribute("role","toolbar"),this.toolbar.setAttribute("aria-label","Diagram controls"),this.toolbar.setAttribute("aria-orientation","horizontal"),this.provenance=f("button","mlv-chip mlv-header__prov"),this.provenance.type="button",m(this.provenance,f("span","mlv-header__dot")).setAttribute("aria-hidden","true"),this.provHost=m(this.provenance,f("span","mlv-header__host","")),this.provRev=m(this.provenance,f("span","mlv-header__rev","")),_(this.provenance,"click",l=>e.onAbout(l.detail===0)),this.toolbar.appendChild(this.provenance),m(this.toolbar,f("span","mlv-header__spacer")),this.search=m(this.toolbar,f("div","mlv-search")),m(this.search,B("search",14)).setAttribute("class","mlv-uicon mlv-search__glyph");let s=m(this.search,f("label","mlv-sr","Search steps, findings, IDs, or cited text"));s.htmlFor=o+"-search-input",this.searchInput=m(this.search,f("input","mlv-input")),this.searchInput.id=o+"-search-input",this.searchInput.type="search",this.searchInput.placeholder="Search steps and findings",this.searchInput.setAttribute("role","combobox"),this.searchInput.setAttribute("aria-expanded","false"),this.searchInput.setAttribute("aria-controls",o+"-search-results"),this.searchInput.autocomplete="off",this.results=m(this.search,f("ul","mlv-search__results")),this.results.id=o+"-search-results",this.results.setAttribute("role","listbox"),this.results.setAttribute("aria-label","Search results"),this.results.hidden=!0,_(this.searchInput,"input",()=>e.onQuery(this.searchInput.value)),_(this.searchInput,"keydown",l=>e.onSearchKey(l)),_(this.searchInput,"blur",()=>{this.searchInput.value||this.setSearchOpen(!1)}),this.searchBtn=he("mlv-btn mlv-btn--icon mlv-header__searchbtn","Search ("+ot("Mod+F")+")"),this.searchBtn.appendChild(B("search",16)),this.searchBtn.setAttribute("aria-controls",this.searchInput.id),_(this.searchBtn,"click",()=>e.onSearchOpen()),this.toolbar.appendChild(this.searchBtn);for(let l of ne){let d=f("button","mlv-chip mlv-chip--btn mlv-chip--sev");d.type="button",d.setAttribute("aria-pressed","true"),d.setAttribute("data-severity",l),d.appendChild(Z(l,14,"")),m(d,f("span","mlv-chip__count","0")),_(d,"click",()=>e.onSeverity(l)),this.sevButtons.set(l,d),this.toolbar.appendChild(d)}this.sevUnit=m(this.toolbar,f("span","mlv-header__unit","findings")),this.sevUnit.setAttribute("aria-hidden","true"),this.sevUnit.hidden=!0,this.exceptionsBtn=f("button","mlv-chip mlv-chip--btn mlv-chip--exceptions"),this.exceptionsBtn.type="button",this.exceptionsBtn.setAttribute("aria-pressed","false"),this.exceptionsBtn.hidden=!0,_(this.exceptionsBtn,"click",()=>e.onToggleExceptions(this.exceptionsBtn.getAttribute("aria-pressed")!=="true")),this.toolbar.appendChild(this.exceptionsBtn),this.reviewBtn=f("button","mlv-btn mlv-header__review"),this.reviewBtn.type="button",this.reviewBtn.setAttribute("aria-pressed","false"),this.reviewBtn.appendChild(B("review",14)),m(this.reviewBtn,f("span","mlv-header__reviewlabel","Review")),this.reviewBtn.hidden=!0,_(this.reviewBtn,"click",()=>e.onReview()),this.toolbar.appendChild(this.reviewBtn),this.more=new io((l,d)=>this.pick(l,d),()=>e.onMenuOpen()),this.toolbar.appendChild(this.more.button),this.refineSlot=m(this.toolbar,f("span","mlv-header__refine")),this.status=f("footer","mlv-status"),this.status.setAttribute("data-layout",this.layout),this.counts=m(this.status,f("span","mlv-status__counts","")),this.coverage=f("button","mlv-status__coverage"),this.coverage.type="button",this.coverage.hidden=!0,_(this.coverage,"click",l=>e.onAbout(l.detail===0)),this.status.appendChild(this.coverage),m(this.status,f("span","mlv-status__spacer")),this.fresh=m(this.status,f("span","mlv-status__freshness"));let r=he("mlv-btn mlv-btn--icon mlv-zoom__btn","Zoom out");r.appendChild(B("minus",12)),_(r,"click",()=>e.onZoom(-1));let a=he("mlv-btn mlv-btn--icon mlv-zoom__btn","Zoom in");a.appendChild(B("plus",12)),_(a,"click",()=>e.onZoom(1)),n.insertBefore(r,n.firstChild),n.appendChild(a),this.status.appendChild(n),this.roving=new xt(this.toolbar)}destroy(){this.roving&&this.roving.destroy(),this.roving=null,this.more.destroy()}syncRoving(){this.fitRow()}setWidth(e){let n=eg(e),o=n!==this.layout;return o&&(this.layout=n,this.header.setAttribute("data-layout",n),this.status.setAttribute("data-layout",n),this.syncSearch()),(o||e!==this.width)&&(this.width=e,this.fitRow()),n}get headerFit(){return this.fitLevel}get headerLayout(){return this.layout}setSearchOpen(e){this.searchOpen=e,this.syncSearch(),this.fitRow()}syncSearch(){let n=!(this.layout==="mid"||this.layout==="narrow")||this.searchOpen||!!this.searchInput.value;this.header.setAttribute("data-search",n?"open":"closed"),this.searchBtn.setAttribute("aria-expanded",n?"true":"false"),this.syncControls()}syncControls(){this.provenance.hidden=!this.hasDocument||this.layout==="narrow"||this.fitLevel>=1;let e=(this.layout==="mid"||this.layout==="narrow")&&this.header.getAttribute("data-search")!=="open";this.searchBtn.hidden=!(this.layout==="mid"&&e),this.exceptionsBtn.hidden=!this.hasExceptions||this.fitLevel>=2,this.reviewBtn.hidden=!this.hasDocument||this.layout==="narrow"||this.fitLevel>=1;for(let[n,o]of this.sevButtons)o.hidden=!this.sevShown.has(n)||this.fitLevel>=4;this.sevUnit.hidden=this.sevShown.size===0||this.fitLevel>=4,this.header.setAttribute("data-fit",String(this.fitLevel))}fitRow(){this.fitLevel=0,this.syncControls();let e=this.header.clientWidth,n=this.header.getAttribute("data-search")==="open"&&(this.layout==="mid"||this.layout==="narrow")?ql:ql-1;if(e>0)for(;this.fitLevelthis.header.clientWidth;)this.fitLevel++,this.syncControls();this.updateMenu(),this.roving&&this.roving.sync()}updateMenu(){let e=this.state;if(!e)return;let n=e.notObserved,o=this.hasExceptions&&this.fitLevel>=2;this.more.update({folded:{search:this.layout==="narrow",about:this.layout==="narrow"||this.fitLevel>=1,exceptions:o},aboutLabel:this.lastAboutLabel,exceptionsLabel:this.hasExceptions?Vl(n):"",exceptionsOn:e.exceptionsOn,legendOpen:e.legendOpen,flowOn:e.flowOn,phaseIndexShown:e.phaseIndexShown,phaseIndexUnavailable:e.phaseIndexUnavailable,overviewOpen:e.overviewOpen,railOpen:e.railOpen,walking:e.walking,railMode:e.railMode,hasSelection:e.hasSelection,canExport:!!e.graph})}pick(e,n){let o=this.cb;e==="search"?o.onSearchOpen():e==="about"?o.onAbout(n):e==="exceptions"?o.onToggleExceptions(!(this.state&&this.state.exceptionsOn)):e==="legend"?o.onToggleLegend(!(this.state&&this.state.legendOpen)):e==="flow"?o.onToggleFlow(!(this.state&&this.state.flowOn)):e==="phaseindex"?o.onTogglePhaseIndex(!(this.state&&this.state.phaseIndexShown)):e==="overview"?o.onOverview():e==="rail"?o.onToggleRail():e==="fit"?o.onFitWhole():e==="zoomsel"?o.onZoomToSelection():e==="shortcuts"?o.onShortcuts():e==="review"?o.onReview():o.onExport(e)}update(e){this.state=e;let n=e.document,o=e.graph;this.renderHeader(e,n);let i=e.notObserved,s=i.steps+i.connections+i.findings;if(this.hasExceptions=!!o&&s>0,s>0){let a=Vl(i),l=a.indexOf(" (");D(this.exceptionsBtn),this.exceptionsBtn.appendChild(B("notobserved",14)),this.layout==="full"&&l>0?(m(this.exceptionsBtn,f("span","mlv-chip__lead",a.slice(0,l))),m(this.exceptionsBtn,f("span","mlv-chip__detail",a.slice(l)))):m(this.exceptionsBtn,f("span","mlv-chip__lead",ng(i))),this.exceptionsBtn.setAttribute("aria-label",a),this.exceptionsBtn.setAttribute("aria-pressed",e.exceptionsOn?"true":"false"),this.exceptionsBtn.title=a+(e.exceptionsOn?". The observed claims are faded; press to show them again.":". Press to fade the observed claims so these stand out.")}for(let a of ne){let l=this.sevButtons.get(a),d=e.filters.severities.indexOf(a)>=0,c=e.visibleCounts[a];l.setAttribute("aria-pressed",d?"true":"false");let u=l.querySelector(".mlv-chip__count");u&&(u.textContent=String(c)),o&&c>0?this.sevShown.add(a):this.sevShown.delete(a),l.title=We(c,a+" finding",a+" findings")+(d?". Press to hide them.":", hidden. Press to show them."),l.setAttribute("aria-label",l.title)}let r=ne.filter(a=>this.sevShown.has(a));this.sevUnit.textContent=r.length===1&&e.visibleCounts[r[0]]===1?"finding":"findings",this.reviewBtn.setAttribute("aria-pressed",e.walking?"true":"false"),this.reviewBtn.title=e.walking?"End the review walk (Escape). It remembers its place for this revision.":"Review the claims one by one (R): each is selected here and its cited lines are opened and highlighted in the editor beside. Focus stays here.",this.reviewBtn.setAttribute("aria-label","Review the claims"),this.syncSearch(),this.renderStatus(e),this.fitRow()}renderHeader(e,n){if(!n){this.title.textContent="",this.title.removeAttribute("title"),this.hasDocument=!1;return}this.title.textContent=n.title,this.title.title=n.title,this.hasDocument=!0;let o=n.producer.host,i=n.producer.model?" ("+n.producer.model+")":"";this.provHost.textContent=o+" · ",this.provRev.textContent=n.revision.id;let s=!!e.freshness&&e.freshness.state==="stale";this.provenance.setAttribute("data-stale",s?"true":"false");let r=n.verification?"Published "+n.verification.publishedAt+" with hashes of "+We(Object.keys(n.verification.files||{}).length,"file","files")+".":"Published without source hashes.",a=s&&e.freshness?" "+e.freshness.text+".":"";this.provenance.title="Revision "+n.revision.id+" by "+o+i+". "+r+a+" Show About: the request, coverage and provenance.",this.provenance.setAttribute("aria-label","Revision "+n.revision.id+", "+o+"."+a+" Show About this revision"),this.lastAboutLabel="About revision "+n.revision.id+" · "+o}renderStatus(e){let n=e.graph,o=e.document;if(!n){this.counts.textContent="Waiting for a workflow…",this.coverage.hidden=!0,D(this.fresh);return}if(this.counts.textContent=We(n.nodes.length,"step","steps")+" · "+We(n.edges.length,"connection","connections"),o){this.coverage.hidden=!1;let s=tg(o);this.coverage.textContent=this.layout==="narrow"?s.replace(/^Coverage: /,""):s,this.coverage.setAttribute("aria-label",s);let a=o.coverage.status==="scoped"?'"scoped": the assistant lists no remaining work within the stated scope.':'"partial": the assistant lists work that remains.';this.coverage.title="Coverage as the assistant recorded it. "+a+" Show About, where the limitations are listed."}else this.coverage.hidden=!0;D(this.fresh);let i=e.freshness;if(this.fresh.setAttribute("data-stale",i&&i.state==="stale"?"true":"false"),i&&i.state==="stale"){let s=m(this.fresh,f("span","mlv-status__fresh is-warn"));s.appendChild(B("warning",12)),m(s,f("span","",i.text)),s.title=i.title,s.setAttribute("data-freshness","stale")}if(e.checking)m(this.fresh,f("span","mlv-status__fresh is-checking","Checking source freshness…")).setAttribute("data-freshness","checking");else if(i&&i.state!=="stale"){let s=m(this.fresh,f("span","mlv-status__fresh is-muted",i.text));s.title=i.title,s.setAttribute("data-freshness",i.state)}}};function Kt(t){let e=yt(t.basis,"finding");return e?(e.removeAttribute("aria-hidden"),e.classList.add("mlv-basis-tag--rail"),e):null}function ro(t,e){if(!e)return[];let n=[];for(let o of[t.loc].concat(t.relatedLocs||[])){let i=o&&o.file?e(o.file):void 0;i&&n.indexOf(i)<0&&n.push(i)}return n}function Et(t){return ae(t)?"cites a file in another folder":t.length===1&&t[0]==="changed"?"cites a changed file":t.indexOf("changed")<0?t.length===1?"cites a missing file":"cites missing files":"cites changed or missing files"}function ao(t,e="div"){if(!(t.fixHint||"").trim())return null;let o=f("div","mlv-insp__fix");return o.setAttribute("data-suggestion",t.id),m(o,f(e,"mlv-insp__fix-label","What to change")),m(o,f("p","mlv-insp__fix-text",t.fixHint)),o}function Ut(t,e,n,o){if(o){t.disabled=!0,t.classList.add("is-stale"),t.setAttribute("data-stale",o);let i=we(e)+": "+me[o]+". Not opened.";t.title=i,t.setAttribute("aria-label",i);return}_(t,"click",i=>{i.stopPropagation(),n(e,i.altKey)}),_(t,"keydown",i=>{i.key!=="Enter"||!i.altKey||(i.preventDefault(),i.stopPropagation(),n(e,!0))})}function Kl(t,e,n){if(D(t),m(t,f("h3","mlv-sr","Findings")),!e.index){m(t,f("div","mlv-empty-note","No workflow loaded yet."));return}let o=e.issues.filter(e.keep);if(!o.length){e.issues.length?t.appendChild(ug(n)):(e.index.graph.nodes||[]).length===0?t.appendChild(cg(e)):t.appendChild(dg(e));return}for(let i of ne){let s=o.filter(l=>ee(l.severity)===i);if(!s.length)continue;let r=m(t,f("section","mlv-rail__section"));r.setAttribute("data-severity-section",i);let a=m(r,f("h4","mlv-rail__heading"));a.appendChild(Z(i,12,"")),m(a,f("span","",i+" · "+s.length+(s.length===1?" finding":" findings"))),r.appendChild(ig(s,i,e,n))}}function ig(t,e,n,o,i){let s=f("ul","mlv-issues");s.setAttribute("role","listbox"),s.setAttribute("aria-label",i||e+" severity findings");for(let r of t)s.appendChild(sg(r,n,o));return lg(s,o),s}function sg(t,e,n){let o=e.selectedIssueId===t.id,i=f("li","mlv-issues__item");i.setAttribute("role","presentation");let s=f("div","mlv-issue");s.setAttribute("role","option"),s.tabIndex=-1,s.setAttribute("data-issue-id",t.id),s.setAttribute("aria-selected",o?"true":"false"),s.setAttribute("aria-label",(t.short?t.short+", ":"")+t.code+" "+t.severity+" severity, "+t.title+(t.loc.file?", "+en(t.loc):"")+$e(t.basis)),o&&s.classList.add("is-selected");let r=ro(t,e.staleReason);r.length&&(s.classList.add("is-stale"),s.setAttribute("data-stale",r.join(" ")),s.setAttribute("aria-label",s.getAttribute("aria-label")+", "+Et(r)+(ae(r)?"":" since publishing"))),s.appendChild(Z(t.severity,14,"")),t.short&&m(s,f("span","mlv-issue__short",t.short));let a=m(s,f("div","mlv-issue__text"));m(a,f("div","mlv-issue__title",t.title));let l=m(a,f("div","mlv-issue__meta"));m(l,f("span","",t.code)),t.loc.file&&l.appendChild(Qe("",t.loc));let d=Kt(t);if(d&&l.appendChild(d),r.length&&l.appendChild(rg(r)),_(s,"click",c=>n.onSelectIssue(t.id,c)),i.appendChild(s),t.loc.file){let c=he("mlv-btn mlv-btn--icon mlv-issue__open","Open "+we(t.loc));c.appendChild(B("open",12)),Ut(c,t.loc,n.onOpen,e.staleReason?e.staleReason(t.loc.file):void 0),i.appendChild(c)}return o&&i.appendChild(ag(t,e,n)),i}function rg(t){let e=f("span","mlv-chip mlv-chip--stale");return e.appendChild(B("warning",11)),m(e,f("span","",Et(t))),e.setAttribute("data-stale",t.join(" ")),e.title=ae(t)?"A file this finding cites is not under the workspace root; it is unchanged in another folder, which the notice above names. Its jump is blocked.":"A file this finding cites no longer matches the published revision. Its jump is blocked.",e}function ag(t,e,n){let o=f("div","mlv-issue__detail");o.setAttribute("data-issue-detail",t.id),t.message&&m(o,f("p","mlv-insp__line",t.message));let i=ao(t);i&&o.appendChild(i);let s=m(o,f("div","mlv-issue__goto"));if(t.loc.file){let r=U("mlv-btn","Go to "+we(t.loc));Ut(r,t.loc,n.onOpen,e.staleReason?e.staleReason(t.loc.file):void 0),s.appendChild(r)}for(let r of t.relatedLocs||[]){let a="Go to "+(r.message||r.role.replace(/_/g," "))+" — "+we(r),l=U("mlv-btn",a);Ut(l,r,n.onOpen,e.staleReason?e.staleReason(r.file):void 0),s.appendChild(l)}return o}function lg(t,e){let n=()=>Array.prototype.slice.call(t.querySelectorAll('[role="option"]')),o=n(),i=o.filter(s=>s.getAttribute("aria-selected")==="true")[0]||o[0]||null;for(let s of o)s.tabIndex=s===i?0:-1;_(t,"keydown",s=>{let r=s.target;if(!r||typeof r.closest!="function")return;let a=r.closest('[role="option"]');if(!a||!t.contains(a))return;let l=n(),d=l.indexOf(a),c=null;if(s.key==="ArrowDown")c=l[Math.min(l.length-1,d+1)];else if(s.key==="ArrowUp")c=l[Math.max(0,d-1)];else if(s.key==="Home")c=l[0];else if(s.key==="End")c=l[l.length-1];else if(s.key==="Enter"){if(s.ctrlKey||s.metaKey||s.shiftKey)return;s.preventDefault();let u=a.getAttribute("data-issue-id");u&&e.onOpenIssue(u,s.altKey);return}else if(s.key===" "){s.preventDefault();let u=a.getAttribute("data-issue-id");u&&e.onSelectIssue(u);return}else return;if(s.preventDefault(),!!c){for(let u of l)u.tabIndex=u===c?0:-1;c.focus()}})}function dg(t){let e=f("div","mlv-empty-note mlv-nofindings");e.setAttribute("role","status"),m(e,f("div","mlv-clean__title","No findings recorded in this revision"));let n=t.index?t.index.graph.authoredCoverage:void 0,o=n?n.status:"unknown",i=n?n.limitations:0;return m(e,f("div","mlv-clean__detail","The assistant recorded no findings. Coverage: "+o+(i?"; "+i+(i===1?" limitation":" limitations")+" listed in About":"")+". This is not a check result.")),e}function cg(t){let e=f("div","mlv-empty-note");e.setAttribute("role","status"),m(e,f("div","mlv-clean__title","Nothing analyzed"));let n=t.index?t.index.graph:null,o=n?n.diagnostics||[]:[],i=n?n.workspace.filesAnalyzed:0;if(m(e,f("div","mlv-clean__detail","No ML pipeline was found, so there is nothing to flag. "+i+(i===1?" file":" files")+" analyzed.")),o.length){let s=m(e,f("ul","mlv-state__list"));for(let r of o.slice(0,5))m(s,f("li","",r.kind+" — "+r.message))}return e}function ug(t){let e=f("div","mlv-empty-note");m(e,f("div","","No findings match these filters."));let n=U("mlv-btn","Clear filters");return _(n,"click",()=>t.onClearFilters()),e.appendChild(n),e}var Zi='[role="treeitem"]';function Xl(t,e,n){let o=m(t,f("ul","mlv-outline"));o.setAttribute("role","tree"),o.setAttribute("aria-label","Pipeline outline");for(let i of e.index.lanes){let s=m(o,f("li","mlv-outline__item"));s.setAttribute("role","treeitem"),s.setAttribute("data-outline-lane",i.id),s.tabIndex=-1;let r=m(s,f("div","mlv-outline__row mlv-outline__row--lane"));m(r,f("span","mlv-outline__label",e.index.phaseIndexOf(i.id)+1+" · "+(i.label||i.id)));let a=e.index.laneCounts(i.id,e.keep);if(Y(a)>0){r.appendChild(Z(oe(a),12,""));let d=Y(a),c=m(r,f("span","mlv-outline__stage mlv-outline__phasecount",Pe(d)));c.title=Gt(a)}_(r,"click",()=>n.onSelectLane(i.id));let l=e.index.roots(i.id);s.setAttribute("aria-expanded",l.length?"true":"false"),l.length&&s.appendChild(Yl(l,e,n))}return hg(t,e,n),bg(o,n),Zl(o,pg(o,e.selectedNodeId)),o}function hg(t,e,n){let o=m(t,f("section","mlv-relations"));m(o,f("h4","mlv-rail__heading","Text relationships"));let i=e.selectedNodeId&&e.index.nodeById.has(e.selectedNodeId)?e.selectedNodeId:null;i&&m(o,f("p","mlv-relations__context","For "+(e.index.nodeById.get(i)?.label||i)));let s=m(o,f("div","mlv-relations__views"));s.setAttribute("role","group"),s.setAttribute("aria-label","Relationship direction");let r=m(o,f("div")),a=(c,u=!1)=>{for(let g of Array.from(s.querySelectorAll("button")))g.setAttribute("aria-pressed",g.getAttribute("data-relation-view")===c?"true":"false");for(;r.firstChild;)r.removeChild(r.firstChild);let h=c==="unresolved"?e.index.graph.edges.filter(g=>g.basis==="unresolved"||e.index.nodeById.get(g.source)?.basis==="unresolved"||e.index.nodeById.get(g.target)?.basis==="unresolved"):!i||c==="all"?e.index.graph.edges:c==="incoming"?e.index.inEdges.get(i)||[]:e.index.outEdges.get(i)||[],p=m(r,f("ul","mlv-relations__list"));if(p.setAttribute("aria-label",c[0].toUpperCase()+c.slice(1)+" relationships"),!h.length){m(p,f("li","mlv-empty-note","No "+c+" relationships in this view."));return}for(let g of h){let v=e.index.nodeById.get(g.source),b=e.index.nodeById.get(g.target),x=m(p,f("li","mlv-relations__item")),y=m(x,f("button","mlv-relations__row"));y.type="button",y.setAttribute("data-relation-id",g.id);let k=i?g.target===i?"Incoming":g.source===i?"Outgoing":"Related":"Directed",E=$e(g.basis);y.setAttribute("aria-label",`${k}: ${v?.label||g.source} to ${b?.label||g.target}; ${g.label||g.kind}${E?";"+E.slice(1):""}`),m(y,f("span","mlv-relations__path",(v?.label||g.source)+" → "+(b?.label||g.target))),m(y,f("span","mlv-relations__detail",(g.label||g.kind||"connection")+(fe(g.basis)?" · "+fe(g.basis):""))),_(y,"click",()=>n.onSelectEdge(g.id))}u&&p.querySelector("button")?.focus()},l=[{id:"all",label:"All"},{id:"incoming",label:"Incoming"},{id:"outgoing",label:"Outgoing"},{id:"unresolved",label:"Unresolved"}];for(let c of l){let u=m(s,f("button","mlv-btn",c.label));u.type="button",u.setAttribute("data-relation-view",c.id),u.disabled=!i&&c.id!=="all"&&c.id!=="unresolved",_(u,"click",()=>{n.onRelationMode(c.id),a(c.id,!0)})}let d=!i&&(e.relationMode==="incoming"||e.relationMode==="outgoing")?"all":e.relationMode;a(d)}function Yl(t,e,n){let o=f("ul");o.setAttribute("role","group");for(let i of t){let s=e.index.nodeById.get(i);if(!s)continue;let r=m(o,f("li","mlv-outline__item"));r.setAttribute("role","treeitem"),r.setAttribute("data-outline-id",i),r.tabIndex=-1;let a=e.index.laneChildren(i),l=a.length>0&&e.collapsed.has(i);a.length&&r.setAttribute("aria-expanded",l?"false":"true");let d=m(r,f("div","mlv-outline__row")),c=e.selectedNodeId===i;if(r.setAttribute("aria-selected",c?"true":"false"),c&&d.classList.add("is-selected"),a.length){let p=B("chevron",12);p.setAttribute("class","mlv-uicon mlv-outline__chevron"),d.appendChild(p)}else m(d,f("span","mlv-outline__chevron mlv-outline__chevron--none"));m(d,f("span","mlv-outline__label",s.label||s.qualname));let u=fe(s.basis);u&&m(d,f("span","mlv-outline__stage",u)).setAttribute("data-basis",s.basis||"");let h=fg(e,i);if(h&&d.appendChild(h),_(d,"click",p=>n.onSelectNode(i,p)),a.length){let p=Yl(a,e,n);p.hidden=l,r.appendChild(p)}}return o}function fg(t,e){let n=oe(t.index.subtreeCounts(e,t.keep));return n?Z(n,12,""):null}function Qi(t){return Array.prototype.slice.call(t.querySelectorAll(Zi)).filter(n=>{let o=n.parentElement;for(;o&&o!==t;){if(o.hidden)return!1;o=o.parentElement}return!0})}function Zl(t,e){let n=Qi(t),o=e&&n.indexOf(e)>=0?e:n[0]||null;for(let i of n)i.tabIndex=i===o?0:-1}function pg(t,e){if(!e)return null;let n=Qi(t);for(let o of n)if(o.getAttribute("data-outline-id")===e)return o;return null}function it(t,e){e&&(Zl(t,e),e.focus())}function mg(t,e){let n=e.parentElement;for(;n&&n!==t;){if(n.getAttribute("role")==="treeitem")return n;n=n.parentElement}return null}function gg(t){let e=t.querySelector('[role="group"]');return!e||e.hidden?null:e.querySelector(Zi)}function Ul(t,e){let n=t.getAttribute("data-outline-id");if(n){e.onSelectNode(n);return}let o=t.getAttribute("data-outline-lane");o&&e.onSelectLane(o)}function vg(t){let e=t.querySelector(".mlv-outline__label");return(e&&e.textContent||"").toLowerCase()}function bg(t,e){_(t,"keydown",n=>{let o=n.target;if(!o||typeof o.closest!="function")return;let i=o.closest(Zi);if(!i||!t.contains(i))return;let s=Qi(t),r=s.indexOf(i);if(r<0)return;let a=i.getAttribute("aria-expanded"),l=i.getAttribute("data-outline-id");switch(n.key){case"ArrowDown":n.preventDefault(),it(t,s[Math.min(s.length-1,r+1)]);return;case"ArrowUp":n.preventDefault(),it(t,s[Math.max(0,r-1)]);return;case"Home":n.preventDefault(),it(t,s[0]);return;case"End":n.preventDefault(),it(t,s[s.length-1]);return;case"ArrowRight":n.preventDefault(),a==="false"&&l?e.onToggleCollapse(l):a==="true"&&it(t,gg(i));return;case"ArrowLeft":n.preventDefault(),a==="true"&&l?e.onToggleCollapse(l):it(t,mg(t,i));return;case"Enter":if(n.ctrlKey||n.metaKey||n.shiftKey)return;n.preventDefault(),l?e.onOpenNode(l,n.altKey):Ul(i,e);return;case" ":n.preventDefault(),Ul(i,e);return;default:break}if(n.key.length!==1||n.ctrlKey||n.metaKey||n.altKey)return;let d=n.key.toLowerCase();for(let c=1;c<=s.length;c++){let u=s[(r+c)%s.length];if(vg(u).indexOf(d)===0){n.preventDefault(),it(t,u);return}}})}var kt=["notObserved","findings","changed","all"],lo={notObserved:"Not observed",findings:"Findings",changed:"Changed files",all:"All claims"};function co(t,e){let n=e===1?" claim":" claims";return t==="all"?"All "+e+n:lo[t]+", "+e+n}var wg={notObserved:"not observed",findings:"findings",changed:"changed files",all:""};function ze(t){return!!t&&t!=="observed"}function ue(t,e){return!!t&&!!e&&t.kind===e.kind&&t.id===e.id}function Ql(t){let e=[],n=new Map,o=[],i=c=>{if(!(n.has(c)||!t.nodeById.has(c))){n.set(c,o.length),o.push(c);for(let u of t.laneChildren(c))i(u)}};for(let c of t.lanes)for(let u of t.roots(c.id))i(u);for(let c of t.graph.nodes||[])i(c.id);let s=new Map,r=[];for(let c of t.graph.issues||[]){let u=null;for(let h of c.nodeIds||[]){let p=n.get(h);p!==void 0&&(u===null||p{l.has(c)||(l.add(c),e.push({kind:"issue",id:c}))};for(let c of o){e.push({kind:"node",id:c});for(let u of t.outEdges.get(c)||[])a.has(u.id)||(a.add(u.id),e.push({kind:"edge",id:u.id}));for(let u of s.get(c)||[])d(u)}for(let c of t.graph.edges||[])a.has(c.id)||(a.add(c.id),e.push({kind:"edge",id:c.id}));for(let c of r)d(c);return e}function uo(t,e){return e.kind==="node"?t.nodeById.get(e.id)?.basis:e.kind==="edge"?t.edgeById.get(e.id)?.basis:t.issueById.get(e.id)?.basis}function $i(t,e){return e.kind==="node"?t.nodeById.get(e.id)?.evidenceLocs||[]:e.kind==="edge"?t.edgeById.get(e.id)?.evidenceLocs||[]:t.issueById.get(e.id)?.relatedLocs||[]}function $l(t){return t.length>0&&!ae(t)}function Jl(t,e,n,o){return t==="all"?!0:t==="findings"?n.kind==="issue":t==="notObserved"?ze(uo(e,n)):$i(e,n).some(i=>!!i.file&&o(i.file))}function ed(t,e,n,o){return t.filter(i=>Jl(e,n,i,o))}function td(t,e,n){let o={notObserved:0,findings:0,changed:0,all:0};for(let i of t)for(let s of kt)Jl(s,e,i,n)&&o[s]++;return o}function nd(t,e){return kt.filter(n=>n==="all"||t[n]>0&&(n!=="changed"||e))}function _t(t,e,n){if(!t.length)return-1;if(!e)return 0;let o=t.findIndex(a=>ue(a,e));if(o>=0)return o;let i=new Map;n.forEach((a,l)=>i.set(a.kind+":"+a.id,l));let s=i.get(e.kind+":"+e.id);if(s===void 0)return 0;let r=t.findIndex(a=>(i.get(a.kind+":"+a.id)??-1)>s);return r>=0?r:t.length-1}function yg(t,e){return e.kind==="node"?t.isGroup(e.id)?"Group":"Step":e.kind==="edge"?"Connection":"Finding"}function od(t,e){let n=t.edgeById.get(e.id);if(!n)return null;let o=t.nodeById.get(n.source),i=t.nodeById.get(n.target);return{from:o?o.label||o.id:n.source,to:i?i.label||i.id:n.target}}function xg(t,e){if(e.kind==="node"){let o=t.nodeById.get(e.id);return o?o.label||o.id:e.id}if(e.kind==="edge"){let o=t.edgeById.get(e.id),i=od(t,e);return!o||!i?e.id:(o.label?o.label+" · ":"")+i.from+" → "+i.to}let n=t.issueById.get(e.id);return n?(n.short?n.short+" ":"")+n.title:e.id}function Eg(t,e){if(e.kind!=="edge")return xg(t,e);let n=t.edgeById.get(e.id),o=od(t,e);return!n||!o?e.id:(n.label?n.label+", ":"")+"from "+o.from+" to "+o.to}function id(t,e,n,o,i){let s=uo(o,i),r=wg[n];return"Claim "+(t+1)+" of "+e+(r?", "+r:"")+": "+yg(o,i)+" "+Eg(o,i)+(ze(s)?", "+s:"")+"."}function Ji(t){let e=t.endLine&&t.endLine>t.line?t.endLine:t.line,n=e>t.line?"lines "+t.line+"–"+e:"line "+t.line,o=ke(t);return t.file+(o?" · cell "+o.cell:"")+" · "+n}function kg(t){return t.quotes>1?" (quote "+(t.quote+1)+" of "+t.quotes+")":""}function sd(t){let e=t.loc?Ji(t.loc)+kg(t):"";switch(t.state){case"none":return"This claim cites no lines, so nothing is opened for it.";case"unavailable":return"This view cannot open source files.";case"idle":return"Enter shows "+e+" in the editor beside.";case"opening":return"Opening in the editor beside: "+e+"…";case"done":return"In the editor beside: "+e+", highlighted. "+(t.focusEditor?"Focus moved to the editor.":"Focus stays here.");default:return(t.quotes>1?"Quote "+(t.quote+1)+" of "+t.quotes+": ":"")+(t.message||"Not opened.")}}function rd(t){switch(t.state){case"idle":return"Enter shows these lines in the editor beside.";case"opening":return"Opening in the editor beside…";case"done":return t.focusEditor?"In the editor beside, highlighted; the focus moved there.":"In the editor beside, highlighted.";case"blocked":case"failed":return t.message||"Not opened.";default:return""}}function ad(t,e){return/\bnot opened\b/i.test(e)?e:(t==="blocked"?"Not opened: ":"Failed: ")+e}var _g="A matching quote shows these lines exist unchanged since publishing. Whether they support the claim is for you to judge.",Ve=(t,e,n)=>t+" "+(t===1?e:n);function Sg(t,e){return t==="inferred"?"Reasoned from the cited code and stated assumptions; the quotes do not show all of it directly.":t==="unresolved"?"The evidence does not settle this claim. It does not mean the "+e+" is missing.":""}function dd(t,e,n){let o=m(t,f("div","mlv-sel"));o.setAttribute("data-columns",String(e.columns)),e.issue?(o.setAttribute("data-kind","finding"),Tg(o,e.issue,e,n)):e.edge?(o.setAttribute("data-kind","connection"),Cg(o,e.edge,e,n)):e.node?(o.setAttribute("data-kind",e.index.isGroup(e.node.id)?"group":"step"),Ag(o,e.node,e,n)):m(o,f("p","mlv-empty-note","Select a step, a connection or a finding to read its claim beside its evidence.")),os(t,e.walk||null)}function os(t,e,n=!1){if(!t)return;for(let r of Array.from(t.querySelectorAll(".mlv-quote.is-walk"))){r.classList.remove("is-walk"),r.removeAttribute("data-walk-status");let a=r.querySelector(".mlv-quote__walk");a&&a.remove()}if(!e)return;let i=t.querySelectorAll(".mlv-sel .mlv-insp__source-evidence > .mlv-quote")[e.quote];if(!i)return;i.classList.add("is-walk"),i.setAttribute("data-walk-status",e.status.state);let s=rd(e.status);if(s){let r=f("p","mlv-quote__walk");(e.status.state==="blocked"||e.status.state==="failed")&&r.appendChild(B("warning",12)),m(r,f("span","",s));let a=i.querySelector(".mlv-quote__head");a&&a.nextSibling?i.insertBefore(r,a.nextSibling):i.appendChild(r)}n&&Ig(i,n==="claim")}var St=8;function Mg(t,e,n,o,i,s=!1){if(!(e>0))return t;let a=Math.min(o,n+e-2*St)+St-e,l=n-St;if(i!==null&&i-St>=a)l=Math.min(l,i-St);else if(i!==null&&s)return Math.max(0,Math.round(Math.min(t,i-St)));return l{n.clientHeight!==i&&(a.disconnect(),t.isConnected&&t.classList.contains("is-walk")&&ld(n,t,o,e))});a.observe(n),s.setTimeout(()=>a.disconnect(),Lg)}function ld(t,e,n,o){let i=t.clientHeight;if(!(i>0))return i;let s=t.getBoundingClientRect(),r=h=>h.top-s.top+t.scrollTop,a=e.getBoundingClientRect(),l=e.querySelector(".mlv-quote__walk")||e.querySelector(".mlv-quote__head"),d=l?r(l.getBoundingClientRect())+l.getBoundingClientRect().height:r(a)+a.height,c=t.querySelector(".mlv-sel .mlv-insp__title"),u=Mg(n,i,r(a),d,c?r(c.getBoundingClientRect()):null,o);return u!==t.scrollTop&&(t.scrollTop=u),i}function is(t,e){if(e.columns===1)return{claim:t,evidence:t};let n=m(t,f("div","mlv-sel__col mlv-sel__col--claim")),o=m(t,f("div","mlv-sel__col mlv-sel__col--evidence"));return{claim:n,evidence:o}}function Ag(t,e,n,o){let i=n.index,s=i.isGroup(e.id),r=s?"group":"step",{claim:a,evidence:l}=is(t,n),d=m(a,f("p","mlv-insp__eyebrow")),c=i.phaseIndexOf(e.stage),u=e.phaseLabel||ud(i,e.stage),h=m(d,f("span","mlv-insp__phase"));h.setAttribute("data-stage",e.stage),le(h,c),m(h,f("span","mlv-insp__phasedot")).setAttribute("aria-hidden","true"),m(h,f("span","",c+1+" · "+u)),h.title="Phase "+(c+1)+": "+u,e.kind&&e.kind!=="unknown"&&es(d,"mlv-insp__kind",e.kind);let p=i.parentOf.get(e.id),g=p?i.nodeById.get(p):void 0;g&&es(d,"mlv-insp__parent","in "+(g.label||g.id)),m(a,f("h4","mlv-insp__title",e.label||e.qualname)),ss(a,e.basis,r),e.detail&&m(a,f("p","mlv-insp__detail",e.detail)),s?(ts(a,i.subtreeIssues(e.id,n.keep),"Findings in this group",n,o),Ng(a,e,n,o)):ts(a,i.issuesOf(e.id,n.keep),"Findings on this step",n,o);let v=()=>s?Fg(a,e,n,o):Pg(a,e,n,o);n.columns===1?(ns(l,e.evidenceLocs||[],n,o,r),v()):(v(),ns(l,e.evidenceLocs||[],n,o,r)),rs(l,o),as(l,n,o)}function Cg(t,e,n,o){let i=n.index,{claim:s,evidence:r}=is(t,n),a=m(s,f("p","mlv-insp__eyebrow"));m(a,f("span","","Connection"));let l=es(a,"mlv-insp__edgekind",Ht(e.kind)+(e.authoredKind?" · authored as "+e.authoredKind:""));l.setAttribute("data-edge-kind",e.kind),l.title="Kind of connection, as the author named it",m(s,f("h4","mlv-insp__title",e.label||"Connection"));let d=i.nodeById.get(e.source),c=i.nodeById.get(e.target),u=m(s,f("p","mlv-insp__ends"));u.appendChild(st(e.source,d,o)),u.appendChild(document.createTextNode(" → ")),u.appendChild(st(e.target,c,o)),ss(s,e.basis,"connection"),ts(s,i.issuesOfEdge(e.id,n.keep),"Findings on this connection",n,o),ns(r,e.evidenceLocs||[],n,o,"connection"),rs(r,o),as(r,n,o)}function Tg(t,e,n,o){let i=n.index,{claim:s,evidence:r}=is(t,n),a=m(s,f("div","mlv-insp__eyebrow mlv-insp__issue-head"));if(a.setAttribute("data-issue-id",e.id),a.appendChild(Z(e.severity,14,"")),m(a,f("span","mlv-insp__severity",Bg(e.severity)+" finding")),e.short){let g=m(a,f("span","mlv-insp__short",e.short));g.title=e.short+" is this finding's number in this revision; its id is "+e.code+"."}m(a,f("span","mlv-mono",e.code));let l=Kt(e);l&&a.appendChild(l),m(s,f("h4","mlv-insp__title",e.title)),ss(s,e.basis,"finding"),e.message&&m(s,f("p","mlv-insp__detail mlv-insp__message",e.message));let d=ao(e,"h5");d&&s.appendChild(d);let c=m(s,f("section","mlv-insp__section mlv-insp__cited"));c.setAttribute("data-section","cited");let u=e.nodeIds.filter(g=>i.nodeById.has(g)),h=e.edgeIds.filter(g=>i.edgeById.has(g));if(!u.length&&!h.length&&m(c,f("p","mlv-insp__note","This finding cites no step or connection: it is about the workflow as a whole.")),u.length){c.appendChild(qe("Cited steps",Ve(u.length,"step","steps")));let g=m(c,f("ul","mlv-insp__links"));for(let v of u){let b=i.nodeById.get(v),x=m(g,f("li"));x.appendChild(st(v,b,o));let y=b.phaseLabel||ud(i,b.stage);m(x,f("span","mlv-insp__aside"," · "+(i.phaseIndexOf(b.stage)+1)+" · "+y))}}if(h.length){c.appendChild(qe("Cited connections",Ve(h.length,"connection","connections")));let g=m(c,f("ul","mlv-insp__links"));for(let v of h){let b=i.edgeById.get(v),x=m(g,f("li")),y=U("mlv-link mlv-insp__flow-edge",b.label||"connection","Select the connection "+ds(i,b));y.setAttribute("data-edge-id",b.id),_(y,"click",()=>o.onShowEdge(b.id)),x.appendChild(y),m(x,f("span","mlv-insp__aside"," · "+ls(i,b)))}}let p=e.relatedLocs||[];cd(r,p,n,o,{noun:"finding",role:g=>g.role||"Supporting evidence",staleNote:()=>{let g=ro(e,n.staleReason);return g.length?ae(g)?"This finding "+Et(g)+"; those jumps are blocked. The notice above says which folder to add.":"This finding "+Et(g)+" since publishing; those jumps are blocked.":""}}),rs(r,o),as(r,n,o)}function es(t,e,n){return m(t,f("span","mlv-insp__sep"," · ")).setAttribute("aria-hidden","true"),m(t,f("span",e,n))}function ss(t,e,n){let o=Sg(e,n);if(!o)return;let i=m(t,f("p","mlv-insp__basis"));i.setAttribute("data-basis",e||"");let s=n==="finding"?null:yt(e,n);s&&(s.removeAttribute("aria-hidden"),s.removeAttribute("title"),i.appendChild(s),i.appendChild(document.createTextNode(" "))),m(i,f("span","",o))}function qe(t,e){let n=f("h5","mlv-rail__heading");return m(n,f("span","mlv-rail__headtext",t)),e&&m(n,f("span","mlv-rail__count",e)),n}function ts(t,e,n,o,i){if(!e.length)return;let s=m(t,f("section","mlv-insp__section"));s.setAttribute("data-section","findings"),s.appendChild(qe(n,Ve(e.length,"finding","findings")));for(let r of e){let a=m(s,f("div","mlv-insp__issue"));a.setAttribute("data-issue-id",r.id);let l=m(a,f("div","mlv-insp__issue-head"));if(l.appendChild(Z(r.severity,14,"")),r.short){let p=m(l,f("span","mlv-insp__short",r.short));p.title=r.short+" is this finding's number in this revision; its id is "+r.code+"."}let d=U("mlv-link mlv-insp__issue-title",r.title,"Select finding "+(r.short?r.short+", ":"")+r.title);_(d,"click",()=>i.onShowIssue(r.id)),l.appendChild(d);let c=Kt(r);c&&l.appendChild(c),m(a,f("p","mlv-insp__issue-id mlv-mono",r.code)),r.message&&m(a,f("p","mlv-insp__line",r.message));let u=ao(r,"h6");u&&a.appendChild(u);let h=ro(r,o.staleReason);if(h.length){a.classList.add("is-stale");let p=m(a,f("p","mlv-insp__stale-note"));p.appendChild(B("warning",12)),m(p,f("span","","This finding "+Et(h)+(ae(h)?".":" since publishing.")))}}}function ns(t,e,n,o,i){cd(t,e,n,o,{noun:i,nav:!0,staleNote:()=>{let s=d=>n.staleReason&&d.file?n.staleReason(d.file):void 0,r=Vs(e,d=>!!(n.staleReason&&n.staleReason(d)));if(!r.stale)return"";let a=e.map(s).filter(d=>!!d),l=r.stale+" of "+r.total+(r.total===1?" quote cites ":" quotes cite ");return ae(a)?l+"a file that is not under the workspace root but is unchanged in another folder; those jumps are blocked. The notice above says which folder to add.":l+"a file that changed or went missing since publishing; those jumps are blocked. To compare the claim with the code as it is now, ask the assistant for a fresh revision."}})}function cd(t,e,n,o,i){let s=m(t,f("section","mlv-insp__section mlv-insp__quotes"));if(s.setAttribute("data-section","quotes"),!e.length){s.appendChild(qe("Source evidence","no quotes")),m(s,f("p","mlv-empty-note mlv-insp__no-evidence","No source evidence was authored for this "+i.noun+". Its basis and the coverage limitations describe what remains uncertain."));return}s.appendChild(qe("Source evidence",Ve(e.length,"quote","quotes"))),m(s,f("p","mlv-insp__caption",_g));let r=i.staleNote();if(r){let c=m(s,f("p","mlv-insp__stale-note"));c.appendChild(B("warning",12)),m(c,f("span","",r))}let a=0,l=()=>{};if(i.nav&&e.length>1){let c=m(s,f("div","mlv-insp__evidence-nav")),u=U("mlv-btn","Previous evidence"),h=U("mlv-btn","Next evidence");l=()=>{u.disabled=a===0,h.disabled=a===e.length-1,u.title=u.disabled?"This is the first evidence item":"Open the previous evidence item",h.title=h.disabled?"This is the last evidence item":"Open the next evidence item"},_(u,"click",()=>{a>0&&o.onOpen(e[--a]),l()}),_(h,"click",()=>{a{let h=m(d,f("li","mlv-quote")),p=n.staleReason&&c.file?n.staleReason(c.file):void 0;p&&(h.classList.add("is-stale"),h.setAttribute("data-stale",p));let g=m(h,f("div","mlv-quote__head"));m(g,f("span","mlv-quote__num",String(u+1))).setAttribute("aria-hidden","true");let v=m(g,f("span","mlv-quote__loc"));m(v,f("span","mlv-quote__file",c.file)),m(v,f("span","mlv-quote__at",Rg(c))),Ze(c)&&(v.title=Ze(c)),i.role&&m(g,f("span","mlv-quote__role",i.role(c))),g.appendChild(Og(c,p,n));let b=U("mlv-link mlv-quote__open","Open","Open "+we(c));b.setAttribute("data-evidence-id",c.evidenceId||""),p||(b.title="Open "+we(c)+" beside the diagram and highlight the cited lines; focus stays here (Alt+click moves it)."),Ze(c)&&!p&&(b.title=Ze(c)),Ut(b,c,(x,y)=>{a=u,l(),o.onOpen(x,y)},p),g.appendChild(b),c.snippet&&h.appendChild(Hg(c))})}function Rg(t){let e=ke(t),n=e?" › cell "+e.cell:"",o=t.endLine&&t.endLine>t.line?t.endLine:t.line;return n+(o>t.line?", lines "+t.line+"–"+o:", line "+t.line)}function Og(t,e,n){if(e){let s=f("span","mlv-quote__fresh mlv-insp__stale");return s.setAttribute("data-fresh","stale"),s.setAttribute("data-stale",e),s.appendChild(B("warning",12)),m(s,f("span","",me[e])),s}let o=!!(n.document&&n.document.verification&&t.file&&Object.prototype.hasOwnProperty.call(n.document.verification.files||{},t.file)),i=f("span","mlv-quote__fresh is-muted",o?"unchanged":"not checked");return i.setAttribute("data-fresh",o?"unchanged":"unchecked"),i.title=o?"Unchanged means these exact lines still exist as quoted. It does not mean they support the claim.":"This revision has no published hash for this file, so MLView cannot tell whether it changed.",i}function Hg(t){let e=f("pre","mlv-quote__text"),n=String(t.snippet||"").split(` +`);return n.forEach((o,i)=>{let s=m(e,f("span","mlv-quote__line"));m(s,f("span","mlv-quote__ln",String(t.line+i))).setAttribute("aria-hidden","true"),m(s,f("span","mlv-quote__code",o)),i{let h=m(l,f("li"));h.setAttribute("data-direction",u);let p=u==="in"?c.source:c.target;p===e.id?m(h,f("span","","Loops back to this step")):(m(h,f("span","",u==="in"?"Comes from ":"Feeds ")),h.appendChild(st(p,i.nodeById.get(p),o))),m(h,f("span","",": "));let g=U("mlv-link mlv-insp__flow-edge",c.label||"an unlabelled connection","Select the connection "+ds(i,c));g.setAttribute("data-edge-id",c.id),_(g,"click",()=>o.onShowEdge(c.id)),h.appendChild(g);let v=c.basis==="inferred"?" (inferred, not observed)":c.basis==="unresolved"?" (unresolved)":"";m(h,f("span",v?"mlv-insp__flow-basis":"",v+"."))};for(let c of s)d(c,"in");for(let c of r)c.source!==c.target&&d(c,"out")}function Ng(t,e,n,o){let i=n.index,s=i.laneChildren(e.id).filter(l=>i.nodeById.has(l));if(!s.length)return;let r=m(t,f("section","mlv-insp__section"));r.setAttribute("data-section","members"),r.appendChild(qe("Steps in this group",Ve(s.length,"step","steps")));let a=m(r,f("ul","mlv-insp__links"));for(let l of s){let d=m(a,f("li"));d.appendChild(st(l,i.nodeById.get(l),o));let c=i.descendantCount(l);c&&m(d,f("span","mlv-insp__aside"," · group of "+Ve(c,"step","steps")))}}function Fg(t,e,n,o){let i=n.index,s=new Set,r=h=>{s.add(h);for(let p of i.laneChildren(h))r(p)};r(e.id);let a=[],l=[];for(let h of i.graph.edges){let p=s.has(h.source),g=s.has(h.target);if(p&&g){h.source===e.id&&h.target===e.id&&a.push(h);continue}g?a.push(h):p&&l.push(h)}if(!a.length&&!l.length)return;let d=m(t,f("section","mlv-insp__section mlv-insp__flow"));d.setAttribute("data-section","connections"),d.appendChild(qe("Connections across its edge",Ve(a.length+l.length,"connection","connections")));let c=m(d,f("ul","mlv-insp__sentences")),u=(h,p)=>{let g=m(c,f("li"));g.setAttribute("data-direction",p);let v=p==="in"?h.source:h.target,b=p==="in"?h.target:h.source;v===e.id&&b===e.id?m(g,f("span","","Loops back to this group")):(m(g,f("span","",p==="in"?"Comes from ":"Feeds ")),g.appendChild(st(v,i.nodeById.get(v),o)),b!==e.id&&(m(g,f("span","",p==="in"?" into ":" from ")),g.appendChild(st(b,i.nodeById.get(b),o)))),m(g,f("span","",": "));let x=U("mlv-link mlv-insp__flow-edge",h.label||"an unlabelled connection","Select the connection "+ds(i,h));x.setAttribute("data-edge-id",h.id),_(x,"click",()=>o.onShowEdge(h.id)),g.appendChild(x);let y=h.basis==="inferred"?" (inferred, not observed)":h.basis==="unresolved"?" (unresolved)":"";m(g,f("span",y?"mlv-insp__flow-basis":"",y+"."))};for(let h of a)u(h,"in");for(let h of l)u(h,"out")}function rs(t,e){let n=m(t,f("div","mlv-insp__actions")),o=U("mlv-btn mlv-insp__challenge","Challenge this claim","Copy a prompt that asks your assistant to check this claim against the code. MLView calls no model.");_(o,"click",()=>e.onChallenge());let i=U("mlv-btn mlv-insp__refine","Refine…","Copy a refinement prompt about this selection. MLView calls no model.");_(i,"click",()=>e.onRefine()),n.append(o,i)}function as(t,e,n){let o=(e.index.graph.diagnostics||[]).filter(r=>r.kind==="workflow_limitation").length;if(!o)return;let i=m(t,f("p","mlv-insp__limits"));i.setAttribute("data-limitations",String(o)),m(i,f("span","",(o===1?"1 document-wide limitation applies":o+" document-wide limitations apply")+" to every claim. "));let s=U("mlv-link mlv-link--inline mlv-insp__limits-show",o===1?"Read it in About":"Read them in About");s.setAttribute("aria-label",o===1?"Read the document-wide limitation in About":"Read the "+o+" document-wide limitations in About"),_(s,"click",()=>n.onShowLimitations()),i.appendChild(s)}function st(t,e,n){let o=e?e.label||e.id:t,i=U("mlv-link mlv-insp__flow-step",o,"Select the step "+o);return i.setAttribute("data-node-id",t),_(i,"click",()=>n.onShowNode(t)),i}function ls(t,e){let n=t.nodeById.get(e.source),o=t.nodeById.get(e.target);return(n?n.label:e.source)+" → "+(o?o.label:e.target)}function ds(t,e){return(e.label?e.label+", ":"")+"from "+ls(t,e).replace(" → "," to ")}function ud(t,e){let n=(t.graph.stages||[]).find(o=>o.id===e);return n&&n.label||e}function Bg(t){return t&&t[0].toUpperCase()+t.slice(1)}function hd(t,e){let n=s=>s==="inferred"?" Inferred, not observed.":s==="unresolved"?" Unresolved.":"";if(e.kind==="node"){let s=t.nodeById.get(e.id);if(!s)return"";let r=s.detail?" "+Dn(s.detail):"";return"Step: "+(s.label||s.id)+"."+n(s.basis)+r}if(e.kind==="edge"){let s=t.edgeById.get(e.id);return s?"Connection: "+(s.label?s.label+", ":"")+"from "+ls(t,s).replace(" → "," to ")+"."+n(s.basis):""}let o=t.issueById.get(e.id);if(!o)return"";let i=o.message?" "+Dn(o.message):"";return"Finding "+(o.short?o.short+", ":"")+o.severity+" severity: "+o.title+"."+n(o.basis)+i}var Dg=280,jg=4,Gg=3,cs=/(^|[.!?]["')\]]?\s+|\n\s*)([A-Z][A-Za-z0-9&/(),' -]{0,47}?):\s+/g;function Wg(t){let e=String(t||""),n=[];cs.lastIndex=0;for(let s=cs.exec(e);s;s=cs.exec(e)){let r=s[2].trim();if(r.split(/\s+/).filter(Boolean).length>6)continue;let l=s.index+s[1].length;n.push({at:l,textAt:s.index+s[0].length,head:r})}if(n.length{let a=r+1n&&e.push({text:t.slice(n,o.index),kv:!1}),e.push({text:i,kv:!0}),n=o.index+i.length,ho.lastIndex=n}return nDg||n.request.question.split(` +`).length>jg){r.setAttribute("data-clamped","true");let w=U("mlv-link mlv-about__more","Show all","Show the whole question");w.setAttribute("aria-expanded","false"),w.setAttribute("aria-controls",r.id),_(w,"click",()=>{let S=r.getAttribute("data-clamped")==="true";r.setAttribute("data-clamped",S?"false":"true"),w.textContent=S?"Show less":"Show all",w.setAttribute("aria-expanded",S?"true":"false"),w.setAttribute("aria-label",S?"Show less of the question":"Show the whole question")}),s.appendChild(w)}let l=rt(i,"traced","What the model traced"),d=Wg(n.coverage.summary);l.setAttribute("data-paragraphs",String(d.length));for(let w of d){let S=m(l,f("p","mlv-about__para"));w.head&&(m(S,f("strong","mlv-about__lead",w.head+":")),S.appendChild(document.createTextNode(" "))),S.appendChild(document.createTextNode(w.text))}let c=rt(i,"coverage","Coverage");m(c,f("p","mlv-about__status mlv-workflow__coverage",Vg(n.coverage.status))).setAttribute("data-status",n.coverage.status);let h=n.coverage.limitations;if(h.length){let w=m(c,f("details","mlv-about__limitations mlv-workflow__limitations"));w.open=!0;let S=h.length;m(w,f("summary","",S+(S===1?" coverage limitation":" coverage limitations")+" (apply to every claim)"));let C=m(w,f("ul"));for(let L of h)m(C,f("li","",L))}else m(c,f("p","mlv-about__note","The assistant listed no coverage limitations."));let p=rt(i,"scope","Scope");m(p,f("p","mlv-about__text",n.request.scope));let g=n.request.entrypoints&&n.request.entrypoints.length?n.request.entrypoints.join(", "):"not specified";m(p,f("p","mlv-about__meta","Entrypoints: "+g));let v=rt(i,"config","Run configuration"),b=n.request.configuration;if(b){let w=m(v,f("p","mlv-about__text mlv-about__config"));for(let S of zg(b))S.kv?m(w,f("code","mlv-about__kv",S.text)):w.appendChild(document.createTextNode(S.text))}else m(v,f("p","mlv-about__meta","Configuration: not specified."));let x=rt(i,"files","Cited files"),y=[];for(let w of n.evidence||[])y.indexOf(w.file)<0&&y.push(w.file);if(!y.length)m(x,f("p","mlv-about__note","No file is cited."));else{let w=x.querySelector(".mlv-rail__heading");w&&m(w,f("span","mlv-rail__count",y.length+(y.length===1?" file":" files")));let S=m(x,f("ul","mlv-about__files")),C=n.verification?n.verification.files||{}:null;for(let L of y){let I=m(S,f("li","mlv-about__file"));I.setAttribute("data-file",L),m(I,f("span","mlv-about__path",L)).title=L;let F=e.staleReason?e.staleReason(L):void 0;if(F){let A=m(I,f("span","mlv-quote__fresh mlv-insp__stale"));A.setAttribute("data-fresh","stale"),A.setAttribute("data-stale",F),A.appendChild(B("warning",12)),m(A,f("span","",me[F]))}else{let A=!!C&&Object.prototype.hasOwnProperty.call(C,L);m(I,f("span","mlv-quote__fresh is-muted",A?"unchanged":"not checked")).setAttribute("data-fresh",A?"unchanged":"unchecked")}}m(x,f("p","mlv-about__note","Unchanged means the quoted lines still exist as published. It does not mean they support the claims."))}let k=rt(i,"provenance","Provenance"),E=[n.producer.host,n.producer.model||"model not named","revision "+n.revision.id+(n.revision.parent?" (after "+n.revision.parent+")":"")];E.push(n.verification?"published "+n.verification.publishedAt:"published without source hashes"),m(k,f("p","mlv-about__meta",E.join(" · "))),m(k,f("p","mlv-about__trust mlv-workflow__provenance","Model-authored; MLView checks citations, not the interpretation."))}function rt(t,e,n){let o=m(t,f("section","mlv-about__section"));o.setAttribute("data-about",e);let i=m(o,f("h4","mlv-rail__heading"));return m(i,f("span","mlv-rail__headtext",n)),o}var Ke=[{id:"about",label:"About",heading:"About this revision"},{id:"issues",label:"Findings",heading:"Findings"},{id:"inspector",label:"Selection",heading:"Selection"},{id:"outline",label:"Outline",heading:"Outline"}],pd=.05,Kg=.15,Ug=0,fo=class{constructor(e){this.tabs=new Map;this.counts=new Map;this.panels=new Map;this.relationMode="outgoing";this.relationNodeId=null;this.current="about";this.mode="docked";this.open=!0;this.selectionKey="";this.cb=e;let n="mlv"+ ++Ug;this.root=f("aside","mlv-rail"),this.root.id=n+"-rail",this.root.setAttribute("data-mode","docked"),this.root.setAttribute("aria-labelledby",n+"-rail-heading"),this.heading=m(this.root,f("h2","mlv-sr","Side panel")),this.heading.id=n+"-rail-heading",this.grip=m(this.root,f("div","mlv-rail__grip")),this.grip.setAttribute("role","separator"),this.grip.setAttribute("aria-orientation","vertical"),this.grip.setAttribute("aria-label","Resize side panel"),this.grip.tabIndex=0,this.wireGrip(this.grip);let o=m(this.root,f("div","mlv-rail__bar")),i=m(o,f("div","mlv-rail__tabs"));i.setAttribute("role","tablist"),i.setAttribute("aria-label","Panel");for(let s=0;se.onTab(r.id)),_(a,"keydown",d=>{let c;if(d.key==="ArrowRight")c=(s+1)%Ke.length;else if(d.key==="ArrowLeft")c=(s+Ke.length-1)%Ke.length;else if(d.key==="Home")c=0;else if(d.key==="End")c=Ke.length-1;else return;d.preventDefault();let u=Ke[c].id;e.onTab(u),this.tabs.get(u)?.focus()}),i.appendChild(a),this.tabs.set(r.id,a);let l=f("div","mlv-rail__panel");l.id=n+"-panel-"+r.id,l.setAttribute("role","tabpanel"),l.setAttribute("aria-labelledby",a.id),l.setAttribute("data-tab",r.id),l.tabIndex=0,l.hidden=!0,this.root.appendChild(l),this.panels.set(r.id,l)}this.chevron=m(o,f("button","mlv-btn mlv-btn--icon mlv-rail__chevron")),this.chevron.type="button",this.chevron.setAttribute("aria-controls",this.root.id),this.chevron.appendChild(B("chevron",14)),this.chevron.hidden=!0,_(this.chevron,"click",()=>e.onSheetToggle()),_(this.root,"keydown",s=>{s.key!=="Escape"||s.defaultPrevented||this.mode!=="sheet"||!this.open||(s.preventDefault(),s.stopPropagation(),e.onSheetCollapse())}),this.applyShape()}setMode(e,n){this.mode=e,this.root.setAttribute("data-mode",e);let o=this.root.parentElement;o&&o.setAttribute("data-rail",e),e==="sheet"&&(this.root.style.width=""),this.setSheetFraction(n),this.applyShape()}setOpen(e){this.open=e,this.applyShape()}setSheetFraction(e){if(this.root.style.setProperty("--mlv-sheet-fraction",String(e)),this.mode==="sheet"){let n=this.cb.sheetFractionMax(),o=Math.round(Math.min(e,n)*100);this.grip.setAttribute("aria-valuemax",String(Math.round(n*100))),this.grip.setAttribute("aria-valuenow",String(o)),this.grip.setAttribute("aria-valuetext",this.open?o+" percent of the height":"collapsed")}}focusTab(e){let n=this.tabs.get(e);if(n)try{n.focus()}catch{}}activePanel(){return this.panels.get(this.current)}revealLimitations(){let e=this.panels.get("about"),n=e.querySelector(".mlv-about__limitations");if(!n)return e;n.open=!0;let o=n.querySelector("summary")||n;if(typeof n.scrollIntoView=="function")try{n.scrollIntoView({block:"nearest"})}catch{}return o}applyShape(){let e=this.mode==="sheet";if(this.root.hidden=!e&&!this.open,e?this.root.setAttribute("data-expanded",this.open?"true":"false"):this.root.removeAttribute("data-expanded"),this.heading.textContent=e?"Bottom panel":"Side panel",this.grip.setAttribute("aria-orientation",e?"horizontal":"vertical"),this.grip.setAttribute("aria-label",e?"Resize the bottom panel":"Resize side panel"),e)this.grip.setAttribute("aria-valuemin","25"),this.setSheetFraction(this.cb.sheetFraction());else for(let o of["aria-valuemin","aria-valuemax","aria-valuenow","aria-valuetext"])this.grip.removeAttribute(o);this.chevron.hidden=!e,this.chevron.setAttribute("aria-expanded",this.open?"true":"false");let n=this.open?"Collapse the bottom panel (Escape)":"Expand the bottom panel";this.chevron.setAttribute("aria-label",n),this.chevron.title=n;for(let[o,i]of this.panels)i.hidden=o!==this.current||e&&!this.open}wireGrip(e){let n=0,o=0,i=0,s=0,r=0,a=!1,l=!1,d=!0,c=0,u=0,h=g=>{if(!l){this.cb.onResize(i+(n-g.clientX));return}let v=o-g.clientY;!a&&Math.abs(v)<4||(a||(a=!0,!d&&v>0&&this.cb.onSheetToggle()),c=s+v/r,this.open&&this.cb.onSheetResize(c))},p=()=>{document.removeEventListener("pointermove",h),document.removeEventListener("pointerup",p),l&&(a?this.open&&c{l=this.mode==="sheet",n=g.clientX,o=g.clientY,i=this.root.getBoundingClientRect().width||360,r=Math.max(1,this.cb.bodyHeight()),d=this.open,u=this.cb.sheetFraction(),s=d?Math.min(u,this.cb.sheetFractionMax()):32/r,c=s,a=!1,document.addEventListener("pointermove",h),document.addEventListener("pointerup",p),g.preventDefault()}),_(e,"keydown",g=>{if(this.mode==="sheet"){if(g.key==="Enter"||g.key===" ")this.cb.onSheetToggle();else if(g.key==="ArrowUp"||g.key==="ArrowDown"){let b=g.key==="ArrowUp";this.open?this.cb.onSheetResize(this.cb.sheetFraction()+(b?pd:-pd)):b&&this.cb.onSheetToggle()}else return;g.preventDefault();return}let v=this.root.getBoundingClientRect().width||360;if(g.key==="ArrowLeft")this.cb.onResize(v+16);else if(g.key==="ArrowRight")this.cb.onResize(v-16);else return;g.preventDefault()})}update(e){let n=this.captureFocus();this.current=e.tab;for(let[i,s]of this.tabs){let r=i===e.tab;s.setAttribute("aria-selected",r?"true":"false"),s.tabIndex=r?0:-1}let o=this.counts.get("issues");if(o){let i=e.issues.length;o.textContent=e.index?" ("+i+")":"";let s=this.tabs.get("issues");s.title=e.index?i+(i===1?" finding":" findings")+" in this revision":"",e.index?s.setAttribute("aria-label","Findings, "+i+(i===1?" finding":" findings")):s.removeAttribute("aria-label")}this.applyShape();for(let[i,s]of this.panels)i!==e.tab&&D(s);e.tab==="about"?this.renderAbout(e):e.tab==="issues"?this.renderIssues(e):e.tab==="outline"?this.renderOutline(e):this.renderSelection(e),n()}captureFocus(){let e=typeof document<"u"?document.activeElement:null;if(!e||typeof e.closest!="function"||!this.root.contains(e))return()=>{};let n=e.closest('[role="tabpanel"]'),o=()=>{if(!n||e.isConnected)return;let a=this.root.ownerDocument;if(a.activeElement&&a.activeElement!==a.body)return;let l=this.panels.get(this.current);if(!(!l||l.hidden))try{l.focus()}catch{}},i=e.closest('[data-issue-id][role="option"], [data-outline-id], [data-outline-lane], [data-relation-id]');if(!i)return o;let s=i.hasAttribute("data-issue-id")?"data-issue-id":i.hasAttribute("data-outline-id")?"data-outline-id":i.hasAttribute("data-outline-lane")?"data-outline-lane":"data-relation-id",r=i.getAttribute(s)||"";return()=>{let a=this.root.querySelector("["+s+'="'+r+'"]');if(!a){o();return}if(a.closest('[role="tabpanel"]')?.hidden)return;let d=a.closest('[role="tree"], [role="listbox"]');if(d){let c=d.querySelectorAll('[role="treeitem"], [role="option"]');for(let u=0;uthis.cb.onSelectIssue(n,o),onOpenIssue:(n,o)=>this.cb.onOpenIssue(n,o),onOpen:(n,o)=>this.cb.onOpen(n,o),onClearFilters:()=>this.cb.onClearFilters()})}renderSelection(e){let n=this.panels.get("inspector");if(D(n),m(n,f("h3","mlv-sr","Selection")),!e.index){m(n,f("div","mlv-empty-note","No workflow loaded yet."));return}dd(n,{index:e.index,keep:e.keep,node:e.selectedNode,edge:e.selectedEdge,issue:e.selectedIssue,columns:e.columns,document:e.document,staleReason:e.staleReason,walk:e.walk||null},{onOpen:(s,r)=>this.cb.onOpen(s,r),onShowNode:s=>this.cb.onShowNode(s),onShowEdge:s=>this.cb.onShowEdge(s),onShowIssue:s=>this.cb.onShowIssue(s),onChallenge:()=>this.cb.onChallenge(),onRefine:()=>this.cb.onRefine(),onShowLimitations:()=>this.cb.onShowLimitations()});let i=(e.selectedIssue?"issue:"+e.selectedIssue.id:e.selectedEdge?"edge:"+e.selectedEdge.id:e.selectedNode?"node:"+e.selectedNode.id:"")+"@"+(e.document&&e.document.revision?e.document.revision.id:"");i!==this.selectionKey&&(this.selectionKey=i,n.scrollTop=0)}renderOutline(e){let n=this.panels.get("outline");D(n),m(n,f("h3","mlv-sr","Outline"));let o=e.index;if(!o){m(n,f("div","mlv-empty-note","No workflow loaded yet."));return}e.selectedNode&&(this.relationNodeId=e.selectedNode.id);let i=this.relationNodeId&&o.nodeById.has(this.relationNodeId)?this.relationNodeId:null;Xl(n,{index:o,keep:e.keep,selectedNodeId:e.selectedNode?e.selectedNode.id:i,collapsed:e.collapsed,relationMode:this.relationMode},{onSelectNode:(s,r)=>this.cb.onSelectNode(s,r),onOpenNode:(s,r)=>this.cb.onOpenNode(s,r),onSelectEdge:s=>this.cb.onSelectEdge(s),onRelationMode:s=>{this.relationMode=s},onSelectLane:s=>this.cb.onSelectLane(s),onToggleCollapse:s=>this.cb.onToggleCollapse(s)})}};var Xg=[{group:"basis",key:"observed",label:"Observed",detail:"Directly supported by cited workspace source. Most claims are observed, so they carry no mark."},{group:"basis",key:"inferred",label:"Inferred",detail:'Reasoned from cited source and stated assumptions; the quotes do not show all of it. Dashed border or line, and an "inferred" tag.'},{group:"basis",key:"unresolved",label:"Unresolved",detail:'The available evidence does not settle this claim. It does not mean the step is absent. Dotted border or line, faint hatching, and a "? unresolved" tag.'}],Yg=[{group:"edge",key:"arrow",label:"connection",detail:"Points from the step that produces something to the step that uses it. The line style shows the basis above, not the kind."},{group:"edge",key:"back",label:"loop back",detail:"The return leg of a loop, marked with a chevron."},{group:"edge",key:"kinds",label:"kind",detail:"What a connection carries (data, call, state…) is written in its hover card and the Selection tab."}],Zg=[{group:"phase",key:"order",label:"phase colour",detail:"Each phase gets a colour by its place in the document, shown on the lane's left rule, the swatch before its number and name, the card's left edge, and its number in the phase index and the phase overview. It means nothing else. Problems are the only other colour on the diagram."},{group:"phase",key:"index",label:"phase index",detail:`The list in the lower right corner of the diagram: each phase's number, name, the findings that touch it and its step count, with the phases in view marked. A row goes to that phase. Beside the code it is one line, such as "4/6 Data loading", that opens the list. Shift+0 shows every phase as a block of its step titles (the phase overview).`}],Qg=[{group:"freshness",key:"verified",label:"Unchanged",detail:"The status bar counts the cited files that still match the hashes published with this revision. Unchanged means the quoted lines still exist; it does not prove the interpretation."},{group:"freshness",key:"draft",label:"Not checked",detail:"This revision has no published source hashes, so the status bar says freshness is not checked."},{group:"freshness",key:"stale",label:"Changed or missing",detail:"A cited file no longer matches the published revision. Cards, connections, findings and quotes that cite it carry this mark and a border in the warning colour, and their jumps are blocked. It does not say whether the claim is still right. When the notice above says the workspace root is the wrong folder, the mark means the file is unchanged in another folder."}];function $g(){return[{id:"basis",title:"Claim basis",rows:Xg},{id:"severity",title:"Findings",rows:ne.map(t=>({group:"severity",key:t,label:ye[t],detail:t==="high"?"High potential impact if the finding is correct.":t==="medium"?"Medium potential impact if the finding is correct.":"Low potential impact if the finding is correct. Severity does not express certainty."})).concat([{group:"severity",key:"short",label:"numbers",detail:"Findings numbered in document order. A new revision can renumber them; the hover card, the Selection tab and Refine prompts use the real id."}])},{id:"edges",title:"Connections",rows:Yg},{id:"phases",title:"Phases",rows:Zg},{id:"freshness",title:"Source freshness",rows:Qg}]}function md(t,e=!1){let n=H("svg",{class:"mlv-legend__swatch",viewBox:"0 0 44 14",width:44,height:14,"aria-hidden":"true"}),o=H("g",{class:"mlv-edge mlv-edge--data"+(e?" mlv-edge--back":"")});return o.setAttribute("data-basis",t),o.appendChild(H("path",{class:"mlv-legend__edge",d:"M2 7 H 33"})),o.appendChild(H("path",{class:"mlv-arrow",d:Pt.d,transform:"translate(31,2)"})),e&&o.appendChild(H("path",{class:"mlv-edge__loopmark",d:"M22 3 L18 7 L22 11",fill:"none",stroke:"var(--mlv-edge)","stroke-width":1.3,"stroke-linecap":"round"})),n.appendChild(o),n}function Jg(t){let e=f("span","mlv-legend__basis"),n=m(e,f("span","mlv-legend__card"));n.setAttribute("data-basis",t);let o=fe(t);return o&&m(n,f("span","mlv-legend__tag",o)).setAttribute("data-basis",t),e.appendChild(md(t)),e}function ev(){let t=f("span","mlv-legend__phases");t.setAttribute("aria-hidden","true");for(let e=0;e{this.setOpen(!1),e(!1)}),n.appendChild(this.toggleBtn),this.body=m(this.root,f("div","mlv-legend__body"));for(let o of $g()){let i=m(this.body,f("section","mlv-legend__section"));i.setAttribute("data-legend-section",o.id),m(i,f("h3","mlv-legend__heading",o.title));let s=m(i,f("dl","mlv-legend__list"));for(let r of o.rows){let a=m(s,f("dt","mlv-legend__term"));a.setAttribute("data-legend-row",r.group+":"+r.key);let l=tv(r);a.appendChild(l);let d=r.group==="basis";!d&&(l.textContent||"").trim()!==r.label&&m(a,f("span","mlv-legend__label",r.label));let c=m(s,f("dd","mlv-legend__desc"));d&&m(c,f("strong","mlv-legend__label",r.label+". ")),c.appendChild(c.ownerDocument.createTextNode(r.detail))}}this.setOpen(!1)}get open(){return this.openState}setKinds(e,n){let o=this.root.querySelector('[data-legend-row="edge:kinds"]'),i=o&&o.nextElementSibling;if(!i)return;let s=i.querySelector(".mlv-legend__authored");s&&s.remove();let r=[];if(e.length){let a=e.slice(0,8).map(l=>l.kind+" "+l.count).join(", ");r.push("Connections in this diagram, by kind: "+a+(e.length>8?", and "+(e.length-8)+" more kinds":"")+".")}n>0&&r.push(n+(n===1?" connection has":" connections have")+" no kind."),r.length&&m(i,f("span","mlv-legend__authored"," "+r.join(" ")))}setOpen(e){this.openState=e,this.root.hidden=!e}toggle(){return this.setOpen(!this.openState),this.openState}};var gd=[{keys:["Mod+F","/"],action:"focusSearch",description:"Search steps, findings, IDs, or cited text"},{keys:["n","p"],action:"cycleIssue",description:"Next / previous finding (document order); in the review walk, in the walk's order"},{keys:["Enter"],action:"open",description:"Open the cited source beside the diagram; focus stays here (in the review walk: open the current quote again)"},{keys:["Alt+Enter"],action:"openFocus",description:"Open the cited source and move focus to the editor"},{keys:["Space"],action:"collapse",description:"Collapse or expand the selected group"},{keys:["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"],action:"move",description:"Move the selection (from a connection, to its end that lies that way; in the review walk, ↓ and ↑ step through the claims)"},{keys:["r"],action:"review",description:"Review walk: go claim by claim, each opened and highlighted in the editor beside (starts on the claims not observed; r again or Escape ends it)"},{keys:["j","k"],action:"walkStep",description:"In the review walk: next / previous claim"},{keys:["[","]"],action:"walkQuote",description:"In the review walk: previous / next quote of the claim"},{keys:["u","Shift+U"],action:"walkNotObserved",description:"Review walk on the claims not observed: start it, then next / previous one"},{keys:["0"],action:"fit",description:"Readable view: the whole diagram if it fits at reading size, otherwise phase 1"},{keys:["Shift+0"],action:"overview",description:"Phase overview: every phase as a block of its step titles; arrows move between phases, Enter goes to one, Escape comes back"},{keys:["+","="],action:"zoomIn",description:"Zoom in"},{keys:["-","_"],action:"zoomOut",description:"Zoom out"},{keys:["z"],action:"zoomToSelection",description:"Zoom to the selection"},{keys:["f"],action:"focusMode",description:"Focus mode: light the full lineage of the selection"},{keys:["l"],action:"legend",description:"Show or hide the legend"},{keys:["a"],action:"flow",description:"Turn the connection flow animation on or off"},{keys:["Shift+A"],action:"replayFlow",description:"Play the connection flow again (it stops after two passes)"},{keys:["e","Shift+E"],action:"cycleConnections",description:"Next / previous connection of the selected step"},{keys:["1","2","3"],action:"toggleSeverity",description:"Toggle the high / medium / low filters"},{keys:["b"],action:"toggleRail",description:"Show or hide the side panel; open or collapse the bottom panel"},{keys:["t"],action:"railTab",description:"Go to the panel tabs, About / Findings / Selection / Outline (opens the panel); ← and → pick a tab"},{keys:["?"],action:"shortcuts",description:"Show this shortcut sheet"},{keys:["Escape"],action:"escape",description:"Close the menu, this sheet, the Refine popover, the phase list or the legend, leave the phase overview, end the review walk, collapse the bottom panel, exit focus mode, clear the selection, leave the canvas"},{keys:["Tab","Shift+Tab"],action:"browser",description:"Move focus out of the diagram (never intercepted)"}];function vd(t,e){let n=t.key,o=t.ctrlKey||t.metaKey,i=()=>(t.preventDefault(),!0);if(n==="Tab")return!1;if(o)return n==="f"||n==="F"?(e.focusSearch(),i()):!1;if(t.altKey&&n==="Enter"&&!t.shiftKey)return e.openSelection(!0)?i():!1;if(t.altKey)return!1;if(n==="/")return e.focusSearch(),i();if(n==="?")return e.toggleShortcuts(),i();if(n==="Escape")return e.escape()?i():!1;if(n==="r"||n==="R")return e.review(),i();if(n==="u"||n==="U")return e.walkNotObserved(t.shiftKey)?i():!1;if(e.walking()){let s=n==="j"||n==="J"||n==="ArrowDown"?1:n==="k"||n==="K"||n==="ArrowUp"?-1:0;if(s)return e.walkStep(s)?i():!1;if(n==="["||n==="]")return e.walkQuote(n==="]"?1:-1)?i():!1}return n==="n"||n==="N"||n==="p"||n==="P"?e.cycleIssue(n==="p"||n==="P")?i():!1:n==="+"||n==="="?(e.zoom(1),i()):n==="-"||n==="_"?(e.zoom(-1),i()):n===")"||n==="0"&&t.shiftKey?(e.overview(),i()):n==="0"?(e.fit(),i()):n==="1"||n==="2"||n==="3"?(e.toggleSeverity(Number(n)-1),i()):n==="f"||n==="F"?(e.toggleFocusMode(),i()):n==="l"||n==="L"?(e.toggleLegend(),i()):n==="a"||n==="A"?t.shiftKey?e.replayFlow()?i():!1:(e.toggleFlow(),i()):n==="e"||n==="E"?e.cycleConnections(t.shiftKey)?i():!1:n==="z"||n==="Z"?(e.zoomToSelection(),i()):n==="b"||n==="B"?(e.toggleRail(),i()):n==="t"||n==="T"?(e.focusRailTabs(),i()):n===" "?e.toggleCollapse()?i():!1:n==="Enter"?e.openSelection(!1)?i():!1:n.indexOf("Arrow")===0?(e.move(n),i()):!1}var mo=class{constructor(e){this.opener=null;this.root=f("div","mlv-sheet"),this.root.hidden=!0;let n=m(this.root,f("div","mlv-sheet__scrim"));_(n,"click",()=>e());let o=m(this.root,f("div","mlv-sheet__panel"));this.panel=o,o.setAttribute("role","dialog"),o.setAttribute("aria-modal","true"),o.setAttribute("aria-label","Keyboard shortcuts");let i=m(o,f("div","mlv-sheet__head"));m(i,f("h2","mlv-sheet__title","Keyboard shortcuts")),this.closeBtn=he("mlv-btn mlv-btn--icon","Close shortcuts"),this.closeBtn.appendChild(B("close")),_(this.closeBtn,"click",()=>e()),i.appendChild(this.closeBtn),_(this.root,"keydown",l=>{if(an(this.panel,l)){l.stopPropagation();return}l.key==="Escape"&&(l.preventDefault(),l.stopPropagation(),e())});let s=Xi();m(o,f("p","mlv-sheet__note","Click a card, connection, finding or Outline row to select it and read its claim. Double-click a step, connection, finding or Outline step, or press Enter on it, to open the cited source beside the diagram; focus stays here. Double-click a group to collapse it. "+(s?"Option":"Alt")+"+click an Open link, or press "+ot("Alt+Enter",s)+", to move focus to the editor. From the code: right-click a cited line in the editor and choose Reveal in Diagram to show the claim that cites it here."));let a=m(o,f("dl","mlv-sheet__list"));for(let l of gd){let d=m(a,f("dt","mlv-sheet__keys"));for(let c of l.keys)m(d,f("kbd","mlv-kbd",ot(c,s)));m(a,f("dd","mlv-sheet__desc",l.description))}}get open(){return!this.root.hidden}show(){if(!this.open){let e=this.root.ownerDocument,n=e?e.activeElement:null;this.opener=n&&!this.root.contains(n)?n:null}this.root.hidden=!1;try{this.closeBtn.focus()}catch{}}hide(e=null){let n=this.open;if(this.root.hidden=!0,!n)return;let o=this.opener;this.opener=null,At(o,e)}toggle(){return this.open?this.hide():this.show(),this.open}};function bd(t,e){return(t.id||"mlv-search-results")+"-opt-"+e}var nv=52;function ov(t,e=nv){return t.length<=e?t:"…"+t.slice(t.length-(e-1))}var us=(t,e,n)=>t+" "+(t===1?e:n);function iv(t){let e=[];return t.totalNodes&&e.push(us(t.totalNodes,"step","steps")),t.totalIssues&&e.push(us(t.totalIssues,"finding","findings")),us(t.total,"match","matches")+(e.length?": "+e.join(", "):"")}function wd(t,e,n){let{query:o,result:i,cursor:s,onPick:r}=n,a=i.hits;if(D(t),!o){t.hidden=!0,e.setAttribute("aria-expanded","false"),e.removeAttribute("aria-activedescendant");return}if(t.hidden=!1,e.setAttribute("aria-expanded","true"),!a.length){m(t,f("li","mlv-result__empty","No matches")),e.removeAttribute("aria-activedescendant");return}let l=m(t,f("li","mlv-result__count",iv(i)));if(l.setAttribute("role","presentation"),l.setAttribute("data-search-count",String(i.total)),a.forEach((d,c)=>{let u=m(t,f("li"));u.setAttribute("role","presentation");let h=f("button","mlv-result");if(h.type="button",h.id=bd(t,c),h.setAttribute("role","option"),h.tabIndex=-1,h.setAttribute("aria-selected",c===s?"true":"false"),d.severity&&h.appendChild(Z(d.severity,12,"")),m(h,f("span","mlv-result__label",d.label)),d.meta){let p=m(h,f("span","mlv-result__meta",ov(d.meta)));p.textContent!==d.meta&&(p.title=d.meta)}if(d.location){let p=d.location.line===null?"first in file":"line "+d.location.line;m(h,f("span","mlv-result__jump",d.location.exact?"jump to "+p:"nearest to "+p)).setAttribute("data-search-jump",d.location.exact?"exact":"nearest"),u.setAttribute("data-search-pinned","1")}_(h,"click",()=>r(d)),u.appendChild(h)}),i.truncated){let d=m(t,f("li","mlv-result__foot"));d.setAttribute("role","presentation"),d.setAttribute("data-search-truncated",String(i.total)),m(d,f("span","","showing "+a.length+" of "+i.total));let c=U("mlv-link mlv-link--inline","Show more","Show more search results");c.setAttribute("data-search-more","1"),_(c,"click",u=>{u.preventDefault(),n.onShowMore()}),d.appendChild(c)}s>=0&&s|]+\.[A-Za-z][A-Za-z0-9_]{0,7}$/;function Ed(t){let e=(t||"").trim();if(!e)return null;let n=/^(.+?):(\d+)(?::\d+)?$/.exec(e);if(n){let o=n[1].trim(),i=Number(n[2]);return!xd.test(o)||!isFinite(i)||i<1?null:{path:hs(o),line:i}}return xd.test(e)?{path:hs(e),line:null}:null}function hs(t){let e=t.split("\\").join("/");for(;e.indexOf("./")===0;)e=e.slice(2);return e}function sv(t,e){let n=hs(t);return n===e?!0:n.length>e.length&&n.slice(n.length-e.length-1)==="/"+e}function kd(t,e){let n=t.graph.nodes||[],o=null;for(let i of n){if(!i.loc||!sv(i.loc.file,e.path))continue;let s=i.loc.line,r=Math.max(s,i.loc.endLine||s),a=r-s,l,d;e.line===null?(l=!1,d=s):(l=e.line>=s&&e.line<=r,d=l?0:e.lineg.hit),p=av(t,e);if(p){let g=h.findIndex(v=>v.kind==="node"&&v.id===p.id);g>=0&&h.splice(g,1),h.unshift(p),h.length>n&&(h.length=n)}return{hits:h,total:i.length+s.length,totalNodes:i.length,totalIssues:s.length,limit:n,truncated:i.length+s.length>h.length}}function av(t,e){let n=Ed(e);if(!n||n.line===null)return null;let o=kd(t,n);if(!o)return null;let i=o.node;return{kind:"node",id:i.id,label:i.label||i.qualname,meta:dt(i.loc),stage:i.stage,location:{path:n.path,line:n.line,exact:o.contains}}}function fs(t){t.sort((e,n)=>e.score-n.score||e.at-n.at)}var Xt=40,ps={hits:[],total:0,totalNodes:0,totalIssues:0,limit:Xt,truncated:!1},go=class{constructor(e,n,o){this.result=ps;this.limit=Xt;this.cursor=-1;this.input=e,this.results=n,this.host=o}run(e){this.limit=Xt,this.execute(e),this.host.onQueryChanged(e)}execute(e){let n=this.host.index();this.result=n?Sd(n,e,this.limit):ps,this.cursor=this.result.hits.length?0:-1,this.render()}showMore(){this.limit+=Xt,this.execute(this.input.value)}get hits(){return this.result.hits}clear(){this.input.value="",this.result=ps,this.limit=Xt,this.cursor=-1,this.render()}focus(){this.input.focus(),this.input.select()}hideResults(){this.results.hidden=!0}handleKey(e){if(e.key==="Escape"){e.preventDefault(),this.clear(),this.host.onQueryChanged(""),this.host.blurToCanvas();return}let n=this.result.hits;if(n.length){if(e.key==="ArrowDown"||e.key==="ArrowUp")e.preventDefault(),this.cursor=yd(this.cursor,n.length,e.key==="ArrowDown"?1:-1),this.render();else if(e.key==="Enter"){e.preventDefault();let o=n[Math.max(0,this.cursor)];o&&(this.host.activate(o),this.hideResults())}}}render(){wd(this.results,this.input,{query:this.input.value,result:this.result,cursor:this.cursor,onPick:e=>{this.host.activate(e),this.hideResults()},onShowMore:()=>this.showMore()})}};var lv=new Set(["invalid","parse","missing","unreadable","obsolete","same-id-changed"]),dv=new Set(["stale","root-hint"]),vo=class{constructor(e){this.reviewCount=0;this.walking=!1;this.message="";this.codes=[];this.cb=e,this.root=f("div","mlv-hostnotice"),this.root.setAttribute("role","status"),this.root.hidden=!0,this.icon=m(this.root,f("span","mlv-hostnotice__icon")),this.icon.setAttribute("aria-hidden","true"),this.text=m(this.root,f("p","mlv-hostnotice__text")),this.actions=m(this.root,f("div","mlv-hostnotice__actions")),this.review=U("mlv-btn mlv-hostnotice__review","Review affected claims"),this.review.setAttribute("data-notice-action","review"),_(this.review,"click",()=>this.cb.onReviewAffected())}setReview(e,n){e===this.reviewCount&&n===this.walking||(this.reviewCount=e,this.walking=n,this.syncActions())}update(e,n){let o=n||[],i=o.some(s=>lv.has(s))?"error":o.some(s=>dv.has(s))?"warn":"info";this.root.hidden=!e,this.root.className="mlv-hostnotice mlv-hostnotice--"+i,this.root.setAttribute("data-codes",o.join(" ")),this.text.textContent=e,D(this.icon),i!=="info"&&this.icon.appendChild(B("warning",14)),this.message=e,this.codes=o,this.syncActions()}syncActions(){let e=this.message,n=this.codes;if(D(this.actions),this.actions.hidden=!0,e&&n.indexOf("stale")>=0&&this.reviewCount>0&&!this.walking&&(this.review.title="Walk the "+this.reviewCount+(this.reviewCount===1?" claim":" claims")+" whose quotes cite a file that changed or went missing; nothing stale is opened",this.actions.appendChild(this.review),this.actions.hidden=!1),e&&n.indexOf("root-hint")>=0){let o=U("mlv-btn mlv-btn--primary","Add folder to workspace");o.setAttribute("data-workspace-hint","add"),_(o,"click",()=>this.cb.onWorkspaceHint("add"));let i=U("mlv-btn","Open folder");i.setAttribute("data-workspace-hint","open"),i.title="Open the folder the citations resolve against in a new window",_(i,"click",()=>this.cb.onWorkspaceHint("open")),this.actions.append(o,i),this.actions.hidden=!1}}};var cv=new Set(["j","J","k","K","[","]","u","U","n","N","p","P"]),bo=class{constructor(e){this.filters=new Map;this.root=f("section","mlv-walkbar"),this.root.setAttribute("aria-label","Review walk"),this.root.hidden=!0;let n=m(this.root,f("div","mlv-walkbar__row"));this.pos=m(n,f("p","mlv-walkbar__pos")),this.posMark=m(this.pos,f("span","mlv-walkbar__mark")),this.posMark.setAttribute("aria-hidden","true"),this.posMark.appendChild(B("warning",12)),this.posMark.hidden=!0,this.posText=m(this.pos,f("span","mlv-walkbar__postext")),this.posSpoken=m(this.pos,f("span","mlv-sr mlv-walkbar__posspoken")),this.tools=m(n,f("div","mlv-walkbar__tools")),this.tools.setAttribute("role","toolbar"),this.tools.setAttribute("aria-label","Review walk controls");let o=m(this.tools,f("div","mlv-walkbar__steps"));o.setAttribute("role","group"),o.setAttribute("aria-label","Step through the claims");for(let[s,r,a]of[[-1,"Previous claim (k)","prev"],[1,"Next claim (j)","next"]]){let l=U("mlv-btn mlv-btn--icon mlv-walkbar__step mlv-walkbar__step--"+a,"",r);l.setAttribute("data-walk-step",a),l.title=r+(s<0?", or ↑":", or ↓"),l.appendChild(B("chevron",12)),_(l,"click",()=>e.onStep(s)),o.appendChild(l)}let i=m(this.tools,f("div","mlv-walkbar__filters"));i.setAttribute("role","group"),i.setAttribute("aria-label","Which claims to walk");for(let s of kt){let r=U("mlv-walkbar__filter","");r.setAttribute("data-walk-filter",s),r.setAttribute("aria-pressed","false"),m(r,f("span","mlv-walkbar__flabel",lo[s])),m(r,f("span","mlv-walkbar__fcount","0")),_(r,"click",()=>e.onFilter(s)),this.filters.set(s,r),i.appendChild(r)}this.keys=m(this.tools,f("span","mlv-walkbar__keys")),this.keys.setAttribute("aria-hidden","true");for(let[s,r]of[["j k","step"],["[ ]","quotes"],["Enter","opens again"],["Esc","exits"]]){let a=m(this.keys,f("span","mlv-walkbar__key"));for(let l of s.split(" "))m(a,f("kbd","mlv-kbd",l));m(a,f("span",""," "+r))}this.exit=U("mlv-btn mlv-walkbar__exit","","Exit the review (Escape)"),this.exit.appendChild(B("close",12)),m(this.exit,f("span","mlv-walkbar__exitlabel","Exit")),this.exit.title="Exit the review (Escape). The walk remembers its place for this revision.",_(this.exit,"click",()=>e.onExit()),this.tools.appendChild(this.exit),this.editor=m(this.root,f("p","mlv-walkbar__editor")),this.editorIcon=m(this.editor,f("span","mlv-walkbar__editoricon")),this.editorIcon.setAttribute("aria-hidden","true"),this.editorIcon.appendChild(B("warning",12)),this.editorText=m(this.editor,f("span","mlv-walkbar__editortext")),_(this.root,"keydown",s=>{if(!(s.defaultPrevented||s.ctrlKey||s.metaKey||s.altKey)){if(s.key==="Escape"){s.preventDefault(),s.stopPropagation(),e.onExit();return}cv.has(s.key)&&e.onKey(s)&&s.preventDefault()}}),this.roving=new xt(this.tools)}get shown(){return!this.root.hidden}update(e){if(!e){this.root.hidden=!0;return}this.root.hidden=!1,this.root.setAttribute("data-layout",e.narrow?"narrow":"wide");let n=e.status.state==="blocked"||e.status.state==="failed";this.root.setAttribute("data-walk-status",e.status.state);let o=lo[e.filter],i=e.total?"Claim "+(e.position+1)+" of "+e.total:"No claims";this.posText.textContent=e.narrow?e.total?e.position+1+"/"+e.total:"0/0":i+" · "+o,this.posMark.hidden=!(n&&e.narrow);let s=sd(e.status);this.pos.title=i+" · "+o+(e.narrow?". "+s:""),e.narrow?this.posText.setAttribute("aria-hidden","true"):this.posText.removeAttribute("aria-hidden"),this.posSpoken.textContent=e.narrow?i+", "+o+(n?". "+s:""):"",this.posSpoken.hidden=!e.narrow;for(let[r,a]of this.filters){let l=e.offered.indexOf(r)>=0;a.hidden=!l,a.setAttribute("aria-pressed",r===e.filter?"true":"false");let d=a.querySelector(".mlv-walkbar__fcount");d&&(d.textContent=String(e.counts[r]));let c=e.counts[r];a.setAttribute("aria-label",co(r,c)),a.title=uv(r,c)}this.keys.hidden=e.narrow,this.editor.hidden=e.narrow,this.editor.setAttribute("data-walk-status",e.status.state),this.editorIcon.hidden=!n,this.editorText.textContent=s,this.roving.sync()}focus(){let e=Array.from(this.tools.querySelectorAll("button")).find(n=>n.tabIndex===0&&!n.hidden);try{(e||this.exit).focus()}catch{}}destroy(){this.roving.destroy(),D(this.root)}};function uv(t,e){let n=e+(e===1?" claim":" claims");return t==="notObserved"?"Walk the "+n+` the author marked inferred or unresolved (the header's "not observed" count)`:t==="findings"?"Walk the "+n+" that are findings":t==="changed"?"Walk the "+n+" whose quotes cite a file that changed or went missing since publishing":"Walk all "+n+": every step, connection and finding"}var Md={bg:"--mlv-bg",surface:"--mlv-surface",surface2:"--mlv-surface-2",border:"--mlv-border",borderStrong:"--mlv-border-strong",text:"--mlv-text",text2:"--mlv-text-2",text3:"--mlv-text-3",link:"--mlv-link",accent:"--mlv-accent",edge:"--mlv-edge",sevHigh:"--mlv-sev-high",sevMedium:"--mlv-sev-medium",sevLow:"--mlv-sev-low",sevHighInk:"--mlv-sev-high-ink",sevMediumInk:"--mlv-sev-medium-ink",sevLowInk:"--mlv-sev-low-ink",stageUnknown:"--mlv-stage-unknown",nodeEdge:"--mlv-node-edge"},hv=["--mlv-phase-0","--mlv-phase-1","--mlv-phase-2","--mlv-phase-3","--mlv-phase-4","--mlv-phase-5","--mlv-phase-6","--mlv-phase-7"],fv={light:.64,dark:.54,hc:1},pv={light:.58,dark:.46,hc:1},Ld={laneTint:"--mlv-lane-tint",groupTint:"--mlv-group-tint"},ms={bg:"#FBFBFD",surface:"#FFFFFF",surface2:"#F3F4F8",border:"#E3E5EB",borderStrong:"#C9CDD6",text:"#16181D",text2:"#5A6070",text3:"#676E81",link:"#2B57C4",accent:"#3B6CF6",edge:"#8C93A3",sevHigh:"#D0342C",sevMedium:"#E8A317",sevLow:"#2F5FD0",sevHighInk:"#FFFFFF",sevMediumInk:"#3A2500",sevLowInk:"#FFFFFF",stageUnknown:"#7A8090",nodeEdge:"",phases:["#00796B","#6A4FB3","#0A693C","#A0388F","#558B2F","#0B7F99","#546E7A","#1F6FB2"],laneTint:.025,groupTint:0},mv={bg:"#131417",surface:"#1A1C21",surface2:"#22252C",border:"#2C3038",borderStrong:"#3B414C",text:"#E6E8EE",text2:"#9AA1B1",text3:"#969DAD",link:"#8FB0FF",accent:"#6E96FF",edge:"#79808F",sevHigh:"#FF6169",sevMedium:"#F2B03C",sevLow:"#6E96FF",sevHighInk:"#1A0405",sevMediumInk:"#2A1A00",sevLowInk:"#0B1020",stageUnknown:"#8B93A7",phases:["#4DB6AC","#A48BE0","#4CA871","#D684C5","#9CCC65","#4FC3D9","#90A4AE","#6FA8DC"],laneTint:.035,groupTint:0},gv={phases:["#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF","#FFFFFF"],bg:"#000000",surface:"#000000",surface2:"#000000",border:"#FFFFFF",borderStrong:"#FFFFFF",text:"#FFFFFF",text2:"#FFFFFF",text3:"#FFFFFF",link:"#6BB7FF",edge:"#FFFFFF",laneTint:0,groupTint:0},Id={light:wo({...ms},"light"),dark:wo({...ms,...mv},"dark"),hc:wo({...ms,...gv},"hc")};function vv(t){let e=Id[t]||Id.light;return{...e,phases:e.phases.slice()}}function vs(t,e){if(e===void 0||!t.phases.length)return t.stageUnknown;let n=t.phases.length;return t.phases[(e%n+n)%n]||t.stageUnknown}function wo(t,e){return e==="hc"?(t.edge=t.border,t.nodeEdge=t.border,t):(t.edge=gs(t.text,t.bg,fv[e])||t.edge,t.nodeEdge=gs(t.text,t.bg,pv[e])||t.border,t)}function Ad(t){let e=t.trim().toLowerCase(),n=/^#([0-9a-f]{3,8})$/.exec(e);if(n){let i=n[1];return i.length===3||i.length===4?[0,1,2].map(s=>parseInt(i[s]+i[s],16)):i.length===6||i.length===8?[0,2,4].map(s=>parseInt(i.slice(s,s+2),16)):null}let o=/^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)/.exec(e);return o?[Number(o[1]),Number(o[2]),Number(o[3])].map(i=>Math.max(0,Math.min(255,i))):null}function bv(t){return"#"+t.map(e=>Math.round(e).toString(16).padStart(2,"0")).join("").toUpperCase()}function gs(t,e,n){let o=Ad(t),i=Ad(e);return!o||!i?"":bv(o.map((s,r)=>s*n+i[r]*(1-n)))}function wv(t){let e=/^color-mix\(\s*in srgb\s*,\s*(.+?)\s+([\d.]+)%\s*,\s*(.+?)\s*\)$/i.exec(t.trim());return e?gs(e[1],e[3],Number(e[2])/100):""}function at(t,e){return e==="high"?t.sevHigh:e==="medium"?t.sevMedium:e==="low"?t.sevLow:t.edge}function Td(t,e){return e==="high"?t.sevHighInk:e==="medium"?t.sevMediumInk:t.sevLowInk}function Rd(t,e){let o={...vv(e)},i=yv(t);if(!t||!i||typeof i.getComputedStyle!="function")return o;let s;try{s=i.getComputedStyle(t)}catch{return o}if(!s||typeof s.getPropertyValue!="function")return o;let r=new Set;for(let l of Object.keys(Md)){let d=Cd(s.getPropertyValue(Md[l]));d&&(o[l]=d,r.add(l))}hv.forEach((l,d)=>{let c=Cd(s.getPropertyValue(l));c&&(o.phases[d]=c)});let a=wo({...o},e);r.has("edge")||(o.edge=a.edge),r.has("nodeEdge")||(o.nodeEdge=a.nodeEdge);for(let l of Object.keys(Ld)){let d=Od(s.getPropertyValue(Ld[l])),c=d?Number(d):NaN;isFinite(c)&&c>=0&&c<=1&&(o[l]=c)}return o}function yv(t){if(!t)return null;let e=t.ownerDocument;return e?e.defaultView:null}function Cd(t){let e=(t||"").trim();return e.toLowerCase().startsWith("color-mix(")?wv(e):Od(e)}function Od(t){let e=(t||"").trim();return!e||e.indexOf("var(")>=0||e.indexOf("color-mix(")>=0?"":e}var bs="ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",Zt="ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace",xv=.51,Ev=.55,kv=.6;function re(t,e,n,o){return t?'"+R(t)+"":""}function ws(t,e,n,o,i,s){return''}function ie(t,e,n,o){let i=n?kv:o?Ev:xv;return t.length*e*i}function Ue(t,e,n,o,i){if(ie(t,n,o,i)<=e)return t;let s=t.length;for(;s>1&&ie(t.slice(0,s)+"…",n,o,i)>e;)s--;return t.slice(0,Math.max(1,s))+"…"}function yo(t,e,n,o,i,s){let r=ee(t),a=o/18,l=at(i,r),d=s?'':'',c='';return''+d+c+""}function Hd(t,e,n,o,i,s=[]){let r=oe(t);if(!r)return"";let a=Y(t),l=s.length?Bo(s):a>1?String(a):"",d=26+(l?ie(l,10.5,!1,!0)+4:0),c=e-d,u=['',yo(r,c+4,n+1,18,o,i)];return l&&u.push(re(l,c+24,n+14,{size:10.5,fill:o.text2,weight:650})),u.join("")}function xo(t,e,n,o,i,s){let r=[];for(let c of ne)t[c]>0&&r.push({sev:c,n:t[c]});if(!r.length)return"";let a=0;for(let c of r)a+=o+2+ie(String(c.n),10.5,!1,!1)+6;let l=e-a,d=[];for(let c of r)d.push(yo(c.sev,l,n-o/2,o,i,s)),l+=o+2,d.push(re(String(c.n),l,n+3.5,{size:10.5,fill:i.text2})),l+=ie(String(c.n),10.5,!1,!1)+6;return d.join("")}function Pd(t){if(!t.length)return{x:0,y:0,w:0,h:0};let e=t[0].x,n=t[0].y,o=t[0].x,i=t[0].y;for(let s of t)s.xo&&(o=s.x),s.y>i&&(i=s.y);return{x:e,y:n,w:o-e,h:i-n}}function Nd(t,e){return{x:t.x-e,y:t.y-e,w:t.w+e*2,h:t.h+e*2}}function Qt(t,e){return t.xe.x&&t.ye.y}function Fd(t){return{x:Yt(t.x),y:Yt(t.y),w:Yt(Math.max(1,t.w)),h:Yt(Math.max(1,t.h))}}function Yt(t){return Math.round((isFinite(t)?t:0)*100)/100}function M(t){return String(Yt(t))}function R(t){let e="",n=String(t??"");for(let o=0;o=55296&&s<=56319){let r=o+1=56320&&r<=57343?(e+=i+n.charAt(o+1),o++):e+="�";continue}if(s>=56320&&s<=57343||s===65534||s===65535){e+="�";continue}i==="&"?e+="&":i==="<"?e+="<":i===">"?e+=">":i==='"'?e+=""":i==="'"?e+="'":i<" "&&i!==` +`&&i!==" "?e+=" ":e+=i}return e}var _v=10,_s=4,Kd=8,Sv=12,Mv=10,Ss=26,Lv=8,Bd=_s+Kd+Ss+Lv,Dd=13,Lt=11,ys=10.5,jd=10.5,$t=13,Iv=.05,Mt=10.5,xs=12,Av=10,Cv=22.5,Gd=38.5,Tv=52.5,Rv=22,Ov=15,Hv=98,Es=14,Wd=3,zd=6,Ms=14,Pv=10,Vd=10,Nv="6 4",Fv="1.5 3.5",Bv=12,ks=34;function Ud(t){let{plan:e,palette:n,theme:o}=t,i=o==="hc",s=Fd(t.region),r=[],a=[],l=[],d=[],c=[],u=[],h=0,p=k=>{u.indexOf(k)<0&&u.push(k)};r.push('');let g=[];for(let k of e.lanes){let E=k.lane,w={x:E.x,y:E.y,w:E.w,h:E.h};if(!Qt(w,s))continue;c.push(E.id);let S=vs(n,k.phase);p(S),g.push(Dv(E,k.counts,S,k.phase,n,i))}let v=[];for(let k of e.nodes){let E=k.visual.box,w={x:E.x-8,y:E.y-8,w:E.w+16,h:E.h+16};if(!Qt(w,s))continue;a.push(k.visual.node.id);let S=vs(n,k.visual.phase);p(S);let C=k.expandedGroup?zv(k.visual,n,i):jv(k.visual,S,n,i);k.expandedGroup?g.push(C):v.push(C)}let b=[];for(let k of e.edges){let E=k.route;if(!Qt(Nd(Pd(E.points),16),s))continue;l.push(E.id);for(let S of E.ids)d.push(S);let w=Vv(k,n,i);b.push(w.markup),w.labelled&&h++}r.push(''+g.join("")+""),r.push(''+b.join("")+""),r.push(''+v.join("")+"");let x='',y=""+R(t.title)+""+(t.desc?""+R(t.desc)+"":"");return{svg:` +`+x+y+r.join("")+` +`,width:s.w,height:s.h,region:s,nodeIds:a,edgeIds:l,edgeDocIds:d,laneIds:c,labels:h,stageColors:u}}function Dv(t,e,n,o,i,s){let r=[''];r.push(''),r.push(''),r.push(''),r.push('');let a=t.y+(t.headerH-8)/2;r.push('');let l=t.y+t.headerH/2+4.5,d=String(o+1),c=t.x+26;r.push(re(d,c,l,{size:$t,fill:i.text2,weight:650}));let u=c+ie(d,$t,!1,!0)+6,h=Y(e),p=h>0?"· "+Pe(h):"",g=p?ie(p,Mt,!1,!1):0,v=et(t.nodeCount),b=ie(v,Mt,!1,!1),x=Math.max(40,t.x+t.w-12-u-b-10-(p?g+96:0)),y=Ue(t.label||t.id,x,$t,!1,!0);r.push(re(y,u,l,{size:$t,fill:i.text,weight:650}));let k=u+ie(y,$t,!1,!0)+10;if(r.push(re(v,k,l,{size:Mt,fill:i.text3})),p){let E=t.x+t.w-12;r.push(re(p,E-g,l,{size:Mt,fill:i.text3})),r.push(xo(e,E-g-6,t.y+t.headerH/2,13,i,s))}return r.push(""),r.join("")}function jv(t,e,n,o){let i=t.node,s=t.box,r=s.collapsed,a=oe(t.counts),l=Ls(i.basis),c=[""];c.push(""+R(jt(t))+"");let u=t.stale?n.sevMedium:a?at(n,a):l?n.text2:n.nodeEdge;c.push('"),c.push('');let h=s.x+_s+Kd,p=s.y+Mv;c.push(''),c.push(ws(Nn(wt(i,r)),h+5,p+5,16/16,n.text2,1.3));let g=s.x+Bd,v=Math.max(24,s.w-Bd-Sv),b=i.label||i.qualname||i.id,x=wi(b,s.w);x.forEach((E,w)=>{c.push(re(Ue(E,v,Dd,!1,!0),g,s.y+Cv+w*18,{size:Dd,fill:n.text,weight:650}))});let y=(x.length-1)*18;if(!r&&Pi(i)===2)Wv(Bn(i),v,2).forEach((E,w)=>{c.push(re(E,g,s.y+Gd+y+w*Gv,{size:Lt,fill:n.text2}))});else{let E=Ue(Bn(i),v,Lt,!1,!1);c.push(re(E,g,s.y+Gd+y,{size:Lt,fill:n.text2}));let w=It(i.loc),S=ie(w.tail,ys,!0,!1),C=Ue(w.head,Math.max(12,v-S),ys,!0,!1)+w.tail;c.push(re(C,g,s.y+Tv+y,{size:ys,fill:n.text3,mono:!0}))}let k=r?[et(t.descendants)]:[];if(k.length&&s.h-y>=Hv){let E=s.y+s.h-Rv,w=g;for(let S of k){let C=ie(S,jd,!1,!1)+12;if(w+C>g+v)break;c.push(''),c.push(re(S,w+6,E+11,{size:jd,fill:n.text2})),w+=C+4}}return a&&!r?c.push(Hd(t.counts,s.x+s.w+6,s.y-6,n,o,t.findings||[])):r&&Y(t.counts)>0&&c.push(xo(t.counts,s.x+s.w+6,s.y+4,14,n,o)),c.push(Xd(i.basis,s.x+Pv,s.y+s.h-Ms/2,n)),c.push(""),c.join("")}var Gv=14.85;function Wv(t,e,n){let o=t.replace(/\s+/g," ").trim().split(" ").filter(Boolean),i=[],s="";for(let r=0;r'+re(i,e+4,n+3.5,{size:Vd,fill:o.text})+""}function zv(t,e,n){let o=t.node,i=t.box,s=oe(t.counts),r=Ls(o.basis),a=['"];a.push(""+R(jt(t))+""),a.push('");let l=i.y+(ks-14)/2;a.push(ws(Nn(wt(o,!1)),i.x+8,l,14/16,e.text2,1.3));let d=i.x+28,c=i.y+ks/2+4,u=Math.max(40,i.w-(s?170:96)),h=Ue(o.label||o.qualname,u,xs,!1,!0);a.push(re(h,d,c,{size:xs,fill:e.text,weight:650}));let p=et(t.descendants),g=ie(p,Mt,!1,!1)+12,v=d+ie(h,xs,!1,!0)+8;return a.push(''),a.push(re(p,v+6,c,{size:Mt,fill:e.text2})),a.push(Xd(o.basis,v+g+6,c-3.5,e)),s&&a.push(xo(t.counts,i.x+i.w-10,i.y+ks/2,13,e,n)),a.push(""),a.join("")}function Vv(t,e,n){let o=t.route,i=t.severity?at(e,t.severity):e.edge,s=Ls(t.basis),r=t.weight&&t.weight>1?Math.floor(t.weight):1,a=r>=2,l=a?Math.max(qd,dn(r)):qd,d=['"];d.push('"),d.push(Kv(o.points,i));let c=!1,u=t.placement,h=!u||!u.hidden&&u.always;if(o.label&&h){let g=u?u.x:o.mid.x,v=u?u.y:o.mid.y-8,b=u?u.text:o.label;d.push(''+R(b)+""),c=!0}let p=u?u.marker:o.mid;if(t.severity){let g=ee(t.severity);d.push(''),d.push(yo(g,p.x-7,p.y-7,14,e,n))}else o.back&&d.push('');return a&&d.push(qv(r,p,o.midAngle,!!t.severity||o.back,e)),d.push(""),{markup:d.join(""),labelled:c}}function qv(t,e,n,o,i){let s=Ot(t),r=un(t),a=cn(e,n,o);return''+R(s)+""}var qd=1.5;function Kv(t,e){let n=Pt;if(t.length<2)return"";let o=t[t.length-1],i=t[t.length-2];for(let l=t.length-2;l>=0;l--)if(t[l].x!==o.x||t[l].y!==o.y){i=t[l];break}let s=Math.atan2(o.y-i.y,o.x-i.x)*180/Math.PI,r="translate("+M(o.x)+","+M(o.y)+") rotate("+M(s)+") translate(-7.65,-4.5) scale(0.9)",a='fill="'+R(e)+'"';return'"}var Uv="image/svg+xml",Xv="image/png";function Yv(t,e,n){let o=n,i=Math.max(t,e);i>0&&i*o>16384&&(o=16384/i);let s=t*e;return s>0&&s*o*o>268435456&&(o=Math.sqrt(268435456/s)),o>=1?o:null}function Zv(t){return"data:"+Uv+";charset=utf-8,"+encodeURIComponent(t)}function Yd(t,e,n,o=2){return new Promise(i=>{if(typeof document>"u"||typeof Image>"u"){i(null);return}let s=Yv(e,n,o);if(s===null){i(null);return}let r=s,a,l;try{a=document.createElement("canvas"),a.width=Math.max(1,Math.round(e*r)),a.height=Math.max(1,Math.round(n*r)),l=typeof a.getContext=="function"?a.getContext("2d"):null}catch{i(null);return}if(!l||typeof a.toDataURL!="function"){i(null);return}let d=new Image,c=!1,u=h=>{c||(c=!0,i(h))};d.onload=()=>{try{l.setTransform(r,0,0,r,0,0),l.drawImage(d,0,0,e,n);let h=a.toDataURL(Xv),p=h.indexOf(","),g=p<0?"":h.slice(p+1);u(h==="data:,"||!g?null:{base64:g,width:a.width,height:a.height,scale:r})}catch{u(null)}},d.onerror=()=>u(null);try{d.src=Zv(t)}catch{u(null)}})}var Eo="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function Qv(t){let e=[];for(let n=0;n=55296&&o<=56319){let i=n+1=56320&&i<=57343?(o=65536+(o-55296<<10)+(i-56320),n++):o=65533}else o>=56320&&o<=57343&&(o=65533);o<128?e.push(o):o<2048?e.push(192|o>>6,128|o&63):o<65536?e.push(224|o>>12,128|o>>6&63,128|o&63):e.push(240|o>>18,128|o>>12&63,128|o>>6&63,128|o&63)}return new Uint8Array(e)}function $v(t){let e=typeof globalThis>"u"?{}:globalThis;if(typeof e.btoa=="function"){let o="";for(let i=0;i>2)+Eo.charAt((i&3)<<4|s>>4),n+=o+1>6):"=",n+=o+2!!o).join("-")+"."+e}function $d(t){return String(t.workspace.root||"")||"workflow"}function tb(t){return String(t).toLowerCase().replace(/[^a-z0-9._-]+/g,"-").replace(/^-+|-+$/g,"").slice(0,48)}function Jd(t,e,n){return{v:1,type:"exportFile",kind:t,name:e,base64:n,data:n,suggestedName:e,scope:"all"}}function ec(t,e,n){return o=>{if(o.outcome==="done"){t.announce(e(o.name||n));return}if(o.outcome==="cancelled"){t.announce("Export cancelled.");return}let i="Export failed: "+(o.message||"the host did not say why");t.toast(i),t.announce(i)}}function tc(t,e,n){let o=Zd(e.svg);if(!o){t.toast("This browser could not encode the SVG.");return}let i=e.nodeIds.length,s=e.edgeIds.length;t.announce("Saving SVG…"),t.request(Jd("svg",n,o),ec(t,r=>"Exported "+i+" cards and "+s+" connections as "+r+".",n))}async function nc(t,e,n){let o=await nb(e);if(!o)return t.toast("Could not draw the PNG here — save the SVG instead."),t.announce("PNG export is not available in this host."),!1;let i=o.width+"×"+o.height;return t.announce("Saving PNG…"),t.request(Jd("png",n,o.base64),ec(t,s=>"Exported "+i+" PNG as "+s+".",n)),!0}async function oc(t,e){if(await ob(e.svg)){let s="SVG copied — "+e.nodeIds.length+" cards, "+e.edgeIds.length+" connections.";return t.toast(s),t.announce(s),!0}let o=e.nodeIds.length,i=e.edgeIds.length;return t.request({v:1,type:"copy",text:e.svg},s=>{if(s.outcome==="done"){let r="SVG copied — "+o+" cards, "+i+" connections.";t.toast(r),t.announce(r)}else t.announce("The SVG could not be copied.")}),!1}async function nb(t){try{return await Yd(t.svg,t.width,t.height,Jv)}catch{return null}}async function ob(t){let e=typeof navigator<"u"?navigator:null;if(!e||!e.clipboard||typeof e.clipboard.writeText!="function")return!1;try{return await e.clipboard.writeText(t),!0}catch{return!1}}function ib(t){let e=t.view.scenePlan();return!e||!t.graph?null:{plan:e,palette:Rd(t.root,t.themes.kind),theme:t.themes.kind,graph:t.graph,generatedAt:new Date().toISOString().slice(0,10)}}function ic(t,e){let n={post:s=>t.bridge.post(s),request:(s,r)=>{t.postRequest(s,r)},toast:s=>t.view.toast(s),announce:s=>t.announce(s)},o=ib(t);if(!o){t.view.toast("Nothing is drawn yet — there is nothing to export.");return}let i=Qd(o);e==="svg"?tc(n,i,Is(o,"svg")):e==="png"?nc(n,i,Is(o,"png")):e==="copy-svg"&&oc(n,i)}function sb(t){let e={steps:0,connections:0,findings:0};if(!t.graph)return e;for(let n of t.graph.nodes)ze(n.basis)&&e.steps++;for(let n of t.graph.edges)ze(n.basis)&&e.connections++;for(let n of t.graph.issues)ze(n.basis)&&e.findings++;return e}function rb(t){let e=Me();if(!t.graph)return e;for(let n of t.graph.issues)e[ee(n.severity)]++;return e}function X(t){let e=sb(t);t.exceptionsOn&&e.steps+e.connections+e.findings===0&&(t.exceptionsOn=!1,t.view.canvasEl.removeAttribute("data-exceptions")),t.chrome.update({graph:t.graph,document:t.graph?t.workflowDocument:null,flowOn:t.flowOn,legendOpen:t.legendOpen,hasSelection:!!t.selection,filters:t.filters.value,visibleCounts:rb(t),phaseIndexShown:!t.view.phaseIndexHidden,phaseIndexUnavailable:t.view.phaseIndexUnavailable(),overviewOpen:t.view.overviewOpen,railOpen:t.railOpen,freshness:qs(t.workflowDocument,t.freshness),checking:t.freshness.checking,notObserved:e,exceptionsOn:t.exceptionsOn,railMode:t.railMode,walking:!!t.walk&&t.walk.active})}function Q(t){let e=t.selection,n=t.selectedNodeId(),o=n&&t.index&&t.index.nodeById.get(n)||null,i=e&&e.kind==="edge"&&t.index&&t.index.edgeById.get(e.id)||null,s=e&&e.kind==="issue"&&t.index&&t.index.issueById.get(e.id)||null;t.paneColumns=t.selectionColumns(),t.rail.update({index:t.index,tab:t.railTab,issues:t.graph?t.graph.issues:[],selectedNode:o,selectedEdge:i,selectedIssueId:e&&e.kind==="issue"?e.id:null,selectedIssue:s,collapsed:t.view.collapsed,keep:t.filters.keep,staleReason:r=>t.freshness.reasonOf(r),document:t.graph?t.workflowDocument:null,columns:t.paneColumns,walk:t.walk?t.walk.paneMark():null})}function sc(t,e,n,o=!1){t.graph=e;let i=new ln(e);t.index=i,n&&n.collapsed?t.collapsedState=n.collapsed.slice():!t.collapsedState.length&&t.view.collapsed.size===0&&(t.collapsedState=i.defaultCollapsed());let s=new Set(e.nodes.map(a=>a.id));t.view.setIndex(i),t.view.flow.clear(),t.view.setCollapsed(t.collapsedState.filter(a=>s.has(a)&&i.isGroup(a))),n&&n.selection!==void 0&&(t.selection=n.selection),t.selection&&t.selection.kind==="node"&&!s.has(t.selection.id)&&(t.selection=null),t.selection&&t.selection.kind==="edge"&&!i.edgeById.has(t.selection.id)&&(t.selection=null),t.selection&&t.selection.kind==="issue"&&!i.issueById.has(t.selection.id)&&(t.selection=null),t.view.relayout();let r=n&&n.viewport?n.viewport:null;if(r?t.view.viewport.set(r):t.view.fit(),X(t),Q(t),t.view.applySelection(t.selection),o){let a=(d,c,u)=>d+" "+(d===1?c:u),l=e.stats.issues.high;t.announce("Workflow loaded: "+a(e.nodes.length,"step","steps")+", "+a(e.edges.length,"connection","connections")+", "+a(e.issues.length,"finding","findings")+(l?" ("+l+" high severity)":"")+".")}t.saveSoon()}function ko(t){if(!t.graph)return;let e=new Set(t.graph.nodes.map(o=>o.id)),n=t.collapsedState.filter(o=>!e.has(o));t.collapsedState=n.concat(Array.from(t.view.collapsed)).sort()}function ac(t){let e=Pl(t.root,t.themes.kind);t.releasePage=Hl(t.root),t.liveEl=e.live,t.view=new oo(e,ab(t)),t.chrome=new so({onQuery:n=>t.search.run(n),onSearchKey:n=>t.search.handleKey(n),onSearchOpen:()=>t.focusSearch(),onSeverity:n=>t.applyFilters(()=>t.filters.toggleSeverity(n)),onToggleExceptions:n=>t.setExceptions(n),onAbout:n=>t.showAbout({focus:n}),onZoom:n=>t.view.zoomStep(n),onToggleLegend:n=>t.setLegend(n),onToggleFlow:n=>t.setFlow(n),onTogglePhaseIndex:n=>t.setPhaseIndexShown(n),onOverview:()=>t.toggleOverview(!0),onToggleRail:()=>t.toggleRail(),onFitWhole:()=>t.view.fitWhole(),onZoomToSelection:()=>t.zoomToSelection(),onExport:n=>ic(t,n),onShortcuts:()=>t.toggleShortcuts(!0),onMenuOpen:()=>X(t),onReview:()=>{let n=t.walk.active;t.walk.toggle(),!n&&t.walk.active&&_o(t)}},e.zoomBar),t.root.appendChild(t.chrome.header),t.walkBar=new bo({onFilter:n=>t.walk.setFilter(n),onStep:n=>{t.walk.step(n)},onExit:()=>{t.walk.stop(),_o(t)},onKey:n=>{let o=n.key;return o==="j"||o==="J"?t.walk.step(1):o==="k"||o==="K"?t.walk.step(-1):o==="]"?t.walk.stepQuote(1):o==="["?t.walk.stepQuote(-1):o==="u"||o==="U"?t.walk.jump("notObserved",n.shiftKey):o==="n"||o==="N"||o==="p"||o==="P"?t.walk.jump("findings",o==="p"||o==="P"):!1}}),t.notice=new vo({onWorkspaceHint:n=>t.bridge.post({v:1,type:"workspaceHint",action:n}),onReviewAffected:()=>{t.walk.start("changed")&&_o(t)}}),t.root.appendChild(t.notice.root),t.root.appendChild(e.body),e.body.appendChild(e.main),e.main.appendChild(t.walkBar.root),t.search=new go(t.chrome.searchInput,t.chrome.results,{index:()=>t.index,activate:n=>t.activateHit(n),onQueryChanged:n=>{t.filters.patch({query:n}),t.saveSoon()},blurToCanvas:()=>t.view.canvasEl.focus()}),t.rail=new fo({onTab:n=>t.setRailTab(n),onClearFilters:()=>t.clearFilters(),onSelectIssue:(n,o)=>{t.focusIssue(n,{fromList:"issues"}),t.doubleClick.arm(o,()=>t.openIssue(n,!1))},onOpenIssue:(n,o)=>t.openIssue(n,o),onSelectNode:(n,o)=>{t.select({kind:"node",id:n},{center:!0,reveal:!0,fromList:"outline"}),t.doubleClick.arm(o,()=>t.select({kind:"node",id:n},{center:!0,reveal:!0,open:!0,fromList:"outline"}))},onOpenNode:(n,o)=>t.select({kind:"node",id:n},{center:!0,reveal:!0,open:!0,focusEditor:o,fromList:"outline"}),onSelectEdge:n=>t.select({kind:"edge",id:n},{reveal:!0,fromList:"outline"}),onShowNode:n=>t.focusNode(n,{center:!0,pulse:!0}),onShowEdge:n=>t.select({kind:"edge",id:n},{reveal:!0}),onShowIssue:n=>t.focusIssue(n),onChallenge:()=>rc(t,"challenge"),onRefine:()=>rc(t,null),onOpen:(n,o)=>{t.walk.openQuote(n,!!o)||t.openLocation(n,o)},onResize:n=>t.setRailWidth(n),onSelectLane:n=>t.selectLane(n),onToggleCollapse:n=>{t.index&&t.index.isGroup(n)&&t.view.toggleCollapse(n)},onShowLimitations:()=>t.showAbout({at:"limitations",focus:!0}),onSheetToggle:()=>t.toggleRail(),onSheetCollapse:()=>{t.walk.stop()?_o(t):t.collapseSheet(!0)},onSheetResize:n=>t.setSheetFraction(n),sheetFraction:()=>t.sheetFraction,sheetFractionMax:()=>t.sheetFractionMax(),bodyHeight:()=>e.body.getBoundingClientRect().height}),e.body.appendChild(t.rail.root),_(t.rail.root,"pointerdown",()=>{t.railChosen=!0}),_(t.rail.root,"keydown",()=>{t.railChosen=!0}),t.legend=new po(n=>t.setLegend(n)),e.canvas.appendChild(t.legend.root),t.sheet=new mo(()=>t.toggleShortcuts(!1)),t.root.appendChild(t.sheet.root),t.root.appendChild(t.chrome.more.panel),t.root.appendChild(t.chrome.status),t.root.appendChild(t.liveEl),t.setRailWidth(t.railWidth),t.setRailOpen(t.railOpen)}function _o(t){try{t.view.canvasEl.focus()}catch{}}function ab(t){let e=(o,i)=>t.select({kind:"node",id:o},{showClaim:!0,open:!0,focusEditor:i}),n=(o,i)=>t.select({kind:"edge",id:o},{showClaim:!0,open:!0,focusEditor:i});return{keep:t.filters.keep,activateNode:(o,i)=>{t.select({kind:"node",id:o},{showClaim:!0}),t.doubleClick.arm(i,()=>e(o,!1))},activateEdge:(o,i)=>{t.select({kind:"edge",id:o},{showClaim:!0}),t.doubleClick.arm(i,()=>n(o,!1))},openNode:e,openEdge:n,clearFilters:()=>t.clearFilters(),announce:o=>t.announce(o),afterCollapse:()=>{ko(t),t.view.applySelection(t.selection),Q(t),t.saveSoon()},onViewportChange:o=>{t.viewportState={x:o.x,y:o.y,zoom:o.zoom},t.saveSoon()},onPhaseIndexChanged:()=>{X(t),t.saveSoon()},onKeyDown:o=>t.onKeyDown(o),onBackgroundClick:()=>t.clearSelection(),keptTarget:()=>{let o=t.selection;if(!o)return null;if(o.kind!=="issue")return{kind:o.kind,id:o.id};let i=t.index?t.index.issueById.get(o.id):void 0;return i?i.nodeIds[0]?{kind:"node",id:i.nodeIds[0]}:i.edgeIds.length?{kind:"edge",id:i.edgeIds[0]}:null:null}}}function rc(t,e){let n=t.root.querySelector(".mlv-workflow__refine"),o=t.root.querySelector(".mlv-workflow__composer"),i=t.root.querySelector(".mlv-workflow__intent");!n||!o||!i||(o.hidden||n.click(),n.click(),e&&(i.value=e,i.dispatchEvent(new Event("change",{bubbles:!0}))))}function As(t){let e={v:1,type:"openLocation",file:t.file,absFile:t.absFile,line:t.line,col:t.col,endLine:t.endLine,endCol:t.endCol,preview:!0};return t.evidenceId&&(e.evidenceId=t.evidenceId),t.evidenceId&&t.cell!==void 0&&(e.cell=t.cell),e}function lc(t,e,n=!1){if(!t.caps.canOpenSource)return;let o=e.evidenceId?t.freshness.reasonOf(e.file):void 0;if(o){let r=e.file+": "+me[o]+". Not opened"+(o==="elsewhere"?".":"; the cited lines may no longer be there.");t.view.toast(r),t.announce(r);return}t.bridge.saveState(t.getState());let i=As(e);n&&(i.focus=!0),t.bridge.post(i);let s=we(e);t.announce(n?"Opening "+s+" in the editor.":"Opening "+s+" beside the diagram. Focus stays here; Alt+Enter moves it to the editor."),!n&&!t.openHintShown&&(t.openHintShown=!0,t.view.toast("Opening the cited lines beside the diagram. Focus stays here; Alt+Enter moves it to the editor."))}var lb=["about","issues","inspector","outline"];function So(t){if(!t||typeof t!="object")return null;let{kind:e,id:n}=t;return e!=="node"&&e!=="edge"&&e!=="issue"||typeof n!="string"||!n?null:{kind:e,id:n}}function Mo(t){return typeof t=="string"&&lb.indexOf(t)>=0?t:null}function dc(t){return{focusSearch:()=>t.focusSearch(),escape:()=>t.dismissTopmost(),cycleIssue:e=>{if(t.walking())return t.walkFindings(e);let n=t.visibleIssues();if(!n.length)return!1;let o=t.selectedIssueId(),s=((o?n.findIndex(r=>r.id===o):-1)+(e?-1:1)+n.length)%n.length;return t.focusIssue(n[s].id),!0},zoom:e=>t.zoom(e),fit:()=>t.fit(),toggleFocusMode:()=>t.toggleFocusMode(),zoomToSelection:()=>t.zoomToSelection(),overview:()=>t.overview(),toggleLegend:()=>t.toggleLegend(),toggleFlow:()=>t.toggleFlow(),replayFlow:()=>t.replayFlow(),toggleCollapse:()=>t.collapseSelection(),openSelection:e=>t.openSelection(e),move:e=>t.move(e),toggleSeverity:e=>{let n=ne[e];n&&t.toggleSeverity(n)},toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:()=>t.toggleShortcuts(),cycleConnections:e=>t.cycleConnections(e),walking:()=>t.walking(),review:()=>t.review(),walkStep:e=>t.walkStep(e),walkQuote:e=>t.walkQuote(e),walkNotObserved:e=>t.walkNotObserved(e)}}function cc(t){return{focusSearch:()=>t.focusSearch(),dismissTopmost:()=>{if(t.sheetOpen())t.toggleShortcuts(!1);else{if(t.closeHeaderPanels())return!0;if(t.closePhasePopover())return!0;if(t.legendOpen())t.closeLegend();else{if(t.closeOverview())return!0;if(t.endWalk())return!0;if(t.collapseSheet())return!0;if(t.view().isFocusLocked)t.view().toggleFocusMode(t.selection());else if(t.selection())t.view().blurFocusedEdge(),t.clearSelection();else{let e=t.view().canvasEl,n=e.ownerDocument?e.ownerDocument.activeElement:null;if(!n||!e.contains(n))return!1;e.blur()}}}return!0},visibleIssues:()=>t.visibleIssues(),selectedIssueId:()=>{let e=t.selection();return e&&e.kind==="issue"?e.id:null},focusIssue:e=>t.focusIssue(e),zoom:e=>t.view().zoomStep(e),fit:()=>t.view().fit(),toggleFocusMode:()=>t.view().toggleFocusMode(t.selection()),zoomToSelection:()=>t.zoomToSelection(),overview:()=>t.toggleOverview(),toggleLegend:()=>t.toggleLegend(),toggleFlow:()=>t.toggleFlow(),replayFlow:()=>t.view().flow.replay()?(t.announce("Playing the connection flow again."),!0):!1,collapseSelection:()=>{let e=t.selection(),n=t.index();if(!e||e.kind!=="node"||!n)return!1;let o=n.isGroup(e.id)?e.id:n.parentOf.get(e.id)||null;return!o||!n.isGroup(o)?!1:(t.view().toggleCollapse(o),!0)},openSelection:e=>t.openSelection(e),move:e=>t.move(e),toggleSeverity:e=>t.toggleSeverity(e),toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:()=>t.toggleShortcuts(),cycleConnections:e=>db(t,e),walking:()=>t.walking(),review:()=>t.review(),walkStep:e=>t.walkStep(e),walkQuote:e=>t.walkQuote(e),walkNotObserved:e=>t.walkJump("notObserved",e),walkFindings:e=>t.walkJump("findings",e)}}function db(t,e){let n=t.selection(),o=null;if(n&&n.kind==="node"?o=n.id:n&&n.kind==="edge"&&(o=t.edgeAnchor()),!o)return!1;let i=t.view().incidentRoutes(o);if(!i.length)return t.view().toast("That node has no connections in this view."),!0;let s=n&&n.kind==="edge"?n.id:null,r=-1;for(let l=0;l(t.view.holdHover(),i(...s))}return n}var hb=new Set(["walking","visibleIssues","selectedIssueId","dismissTopmost","overview","toggleShortcuts","focusSearch","toggleLegend","toggleRail","focusRailTabs"]);function fb(t,e){let n={};for(let[o,i]of Object.entries(e))n[o]=hb.has(o)?i:(...s)=>(t.view.overviewOpen&&(t.view.closeOverview(!1),X(t)),i(...s));return n}function pb(t){return cc({view:()=>t.view,index:()=>t.index,selection:()=>t.selection,edgeAnchor:()=>t.edgeAnchor,setEdgeAnchor:e=>{t.edgeAnchor=e},select:e=>t.select(e),clearSelection:()=>t.clearSelection(),focusSearch:()=>t.focusSearch(),visibleIssues:()=>mb(t),focusIssue:e=>t.focusIssue(e,{fromList:"issues"}),openSelection:e=>{let n=t.selection;if(!n)return!1;if(t.walk.active&&t.walkOwns(n))return t.walk.reopen(e);let o=t.locOf(n);return o&&t.openLocation(o,e),!0},zoomToSelection:()=>t.zoomToSelection(),move:e=>gb(t,e),toggleSeverity:e=>t.applyFilters(()=>t.filters.toggleSeverity(e)),toggleRail:()=>t.toggleRail(),focusRailTabs:()=>t.focusRailTabs(),toggleShortcuts:e=>t.toggleShortcuts(e),sheetOpen:()=>t.sheet.open,closeHeaderPanels:()=>t.closeHeaderPanels(),collapseSheet:()=>t.collapseSheet(!1),announce:e=>t.announce(e),toggleLegend:()=>t.setLegend(!t.legendOpen),legendOpen:()=>t.legendOpen,closeLegend:()=>t.setLegend(!1),toggleFlow:()=>t.setFlow(!t.flowOn),closePhasePopover:()=>t.view.closePhasePopover(),closeOverview:()=>t.view.closeOverview(!0)?(X(t),!0):!1,toggleOverview:()=>t.toggleOverview(),walking:()=>t.walk.active,endWalk:()=>t.walk.stop(),review:()=>t.walk.toggle(),walkStep:e=>t.walk.step(e),walkQuote:e=>t.walk.stepQuote(e),walkJump:(e,n)=>t.walk.jump(e,n)})}function mb(t){return t.graph?t.graph.issues.filter(t.filters.keep):[]}function gb(t,e){let n=t.view.takeArrowLane(),o=n&&t.index?t.index.roots(n)[0]:void 0;if(o){let r=t.view.frame?t.view.frame.boxes.get(o):void 0;t.select({kind:"node",id:o},{center:!!r&&!t.view.viewport.isVisible(r)});let a=t.view.nodeElement(o);a&&a.focus();return}let i=t.view.nextSelection(t.selection,e);if(!i)return;t.select({kind:"node",id:i.id},{center:!i.visible});let s=t.view.nodeElement(i.id);s&&s.focus()}function Lo(t,e,n){if(!e||typeof e!="object")return;e.filters&&t.filters.restore(e.filters);let o=Mo(e.railTab);if(o&&(t.railTab=o),Array.isArray(e.collapsed)){t.collapsedState=e.collapsed.slice();let s=t.index;s&&t.view.setCollapsed(e.collapsed.filter(r=>s.isGroup(r)))}let i=So(e.selection);n&&i&&(t.selection=i),e.phaseIndex==="folded"||e.phaseIndex==="hidden"?t.view.setPhaseIndex({folded:e.phaseIndex==="folded",hidden:e.phaseIndex==="hidden"}):e.phaseIndex===void 0&&e.minimapCollapsed===!0?t.view.setPhaseIndex({folded:!0}):e.phaseIndex===void 0&&n&&t.view.setPhaseIndex({folded:!1,hidden:!1}),typeof e.flow=="boolean"&&t.setFlow(e.flow),typeof e.legendOpen=="boolean"&&t.setLegend(e.legendOpen),n&&t.index&&(t.view.relayout(),X(t),Q(t),t.view.applySelection(t.selection)),e.viewport&&t.view.viewport.set(e.viewport)}function hc(t){ko(t);let e={viewport:{...t.viewportState},selection:t.selection?{...t.selection}:null,collapsed:t.collapsedState.slice(),filters:t.filters.snapshot(),railTab:t.railTab};if(t.view.phaseIndexHidden?e.phaseIndex="hidden":t.view.phaseIndexFolded&&(e.phaseIndex="folded"),t.flowOn||(e.flow=!1),t.legendOpen&&(e.legendOpen=!0),t.graph&&t.workflowRevision){e.workflowRevision=t.workflowRevision;let n=br(t.root);n&&(e.composer=n),t.railMode==="sheet"&&t.railOpen&&(e.sheetOpen=!0);let o=t.walk.viewState();o&&(e.walk=o)}return e}function fc(t){try{return t.loadState()}catch{return null}}function pc(t,e){Ks(e,{init:(n,o)=>{t.caps=o||t.caps,t.setTheme(n),X(t),Q(t)},workflow:n=>{n!==t.workflowDocument&&t.setWorkflow(n)},workflowStatus:n=>Er(t,n),hostNotice:(n,o)=>t.showHostNotice(n,o),stale:n=>t.setStale(n),actionResult:n=>t.onActionResult(n),theme:n=>t.setTheme(n),revealNode:(n,o)=>t.revealClaim({kind:"node",id:n},{center:o}),revealEdge:n=>t.revealClaim({kind:"edge",id:n}),revealIssue:n=>t.revealClaim({kind:"issue",id:n}),restoreState:n=>Lo(t,n,!0),onUnknown:n=>t.bridge.post({v:1,type:"log",level:"debug",message:"ignored unknown message type: "+n})})}var vb=150,bb=100;function wb(t){if(!t||typeof t!="object")return null;let e=t,n=typeof e.filter=="string"&&kt.indexOf(e.filter)>=0?e.filter:null,o=e.claim;if(!n||!o||typeof o!="object"||o.kind!=="node"&&o.kind!=="edge"&&o.kind!=="issue"||typeof o.id!="string"||!o.id)return null;let i={filter:n,claim:{kind:o.kind,id:o.id}},s=e.quote;return typeof s=="number"&&Number.isInteger(s)&&s>0&&s!!this.app.freshness.reasonOf(e);this.app=e}index(){return this.app.index}claims(){let e=this.index();return e?(e!==this.orderIndex&&(this.order=Ql(e),this.orderIndex=e),this.order):[]}changedOffered(){return $l(this.app.freshness.list().map(e=>e.reason))}counts(){let e=this.index();return e?td(this.claims(),e,this.isStale):{notObserved:0,findings:0,changed:0,all:0}}offered(){return nd(this.counts(),this.changedOffered())}listFor(e){let n=this.index();return n?ed(this.claims(),e,n,this.isStale):[]}current(){return this.position>=0&&this.position=0?i.filter:o.indexOf("notObserved")>=0?"notObserved":"all";let r=this.listFor(s);if(!r.length)return this.app.announce("This revision has no claims to walk."),!1;let a=this.active;a||this.app.openRailForWalk(),this.filter=s,this.list=r;let l=i&&i.filter===s?i:null;return this.position=Math.max(0,_t(r,l?l.claim:null,this.claims())),this.quote=l&&ue(l.claim,this.current())?Math.min(l.quote||0,Math.max(0,this.currentLocs().length-1)):0,this.active=!0,a||this.app.onWalkShown(!0),this.show("open"),!0}stop(e=!0){return this.active?(this.active=!1,this.cancelTimer(),this.app.bridge.post({v:1,type:"walk",state:"end"}),this.remember(),this.app.onWalkShown(!1),e&&this.app.announce("Review ended. r resumes it here."),this.app.saveSoon(),!0):!1}toggle(){this.active?this.stop():this.start()}step(e){if(!this.active||!this.list.length)return!1;let n=Math.max(0,Math.min(this.list.length-1,this.position+e));return n===this.position?(this.app.announce(e>0?"Claim "+this.list.length+" of "+this.list.length+" is the last one.":"Claim 1 of "+this.list.length+" is the first one."),!0):(this.position=n,this.quote=0,this.show("open"),!0)}stepQuote(e){if(!this.active)return!1;let n=this.currentLocs();if(n.length<2)return this.app.announce(n.length?"This claim has one quote.":"This claim cites no lines."),!0;let o=Math.max(0,Math.min(n.length-1,this.quote+e));return o===this.quote?(this.app.announce(e>0?"Quote "+n.length+" of "+n.length+" is the last one.":"Quote 1 of "+n.length+" is the first one."),!0):(this.quote=o,this.app.announce("Quote "+(o+1)+" of "+n.length+": "+Ji(n[o])+"."),this.scheduleOpen(),this.remember(),this.render("quote"),this.app.saveSoon(),!0)}jump(e,n){let o=this.index();if(!o)return!1;let i=e;if(!this.active)return this.start(i);let s=this.claims(),r=d=>e==="findings"?d.kind==="issue":ze(uo(o,d)),a=s.findIndex(d=>ue(d,this.current())),l=null;for(let d=1;d<=s.length;d++){let c=((a<0?n?0:-1:a)+(n?-d:d)+s.length*2)%s.length;if(r(s[c])){l=s[c];break}}return l?(this.list.some(d=>ue(d,l))||(this.filter=i,this.list=this.listFor(i)),this.position=Math.max(0,this.list.findIndex(d=>ue(d,l))),this.quote=0,this.show("open"),!0):(this.app.announce(e==="findings"?"This revision has no findings.":"No claim is marked inferred or unresolved in this revision."),!0)}setFilter(e){if(!this.active||this.offered().indexOf(e)<0)return;let n=this.current();this.filter=e,this.list=this.listFor(e),this.position=Math.max(0,_t(this.list,n,this.claims()));let o=ue(n,this.current());o||(this.quote=0),this.show(o?"keep":"open"),this.app.announce(co(e,this.list.length)+". "+this.announcement())}reopen(e){return!this.active||!this.current()?!1:(this.cancelTimer(),this.sendOpen(e),!0)}openQuote(e,n){if(!this.active||!e.evidenceId)return!1;let o=this.currentLocs().findIndex(i=>i.evidenceId===e.evidenceId);return o<0?!1:(this.quote=o,this.cancelTimer(),this.sendOpen(n),this.remember(),!0)}followSelection(e){if(!this.active||this.selecting||!e)return;let n=this.list.findIndex(o=>ue(o,e));n<0||n===this.position||(this.position=n,this.quote=0,this.cancelTimer(),this.latestSeq=0,this.app.bridge.post({v:1,type:"walk",state:"clear"}),this.status=this.unansweredStatus("idle"),this.remember(),this.render("claim"))}onDocument(e){let n=this.app.workflowRevision;e&&(this.active&&this.stop(!1),this.remembered=null,this.list=[],this.position=-1);let o=this.restored;if(this.restored=null,o&&o.revision===n&&!this.remembered){this.remembered={revision:n,state:o.state},o.state.active&&this.resume();return}if(!this.active)return;let i=this.current();this.list=this.listFor(this.filter),this.position=Math.max(0,_t(this.list,i,this.claims())),this.render()}resume(){let e=this.remembered?this.remembered.state:null;if(!e)return;let n=this.offered();this.filter=n.indexOf(e.filter)>=0?e.filter:"all",this.list=this.listFor(this.filter),this.list.length&&(this.position=Math.max(0,_t(this.list,e.claim,this.claims())),this.quote=ue(e.claim,this.current())?Math.min(e.quote||0,Math.max(0,this.currentLocs().length-1)):0,this.active=!0,this.app.onWalkShown(!0),this.show("rest"))}onStale(e=[]){if(!this.active)return;this.offered().indexOf(this.filter)<0&&(this.filter="all");let n=this.current();if(this.list=this.listFor(this.filter),this.position=Math.max(0,_t(this.list,n,this.claims())),!ue(n,this.current())){this.quote=0,this.cancelTimer(),this.latestSeq=0,this.app.bridge.post({v:1,type:"walk",state:"clear"}),this.show("rest");return}if(this.timer!==null)this.status=this.unansweredStatus("opening");else if(this.latestSeq===0&&(this.status.state==="idle"||this.status.state==="blocked"))this.status=this.unansweredStatus("idle");else if(this.status.state==="blocked"&&this.status.loc){let o=this.status.loc.file,i=e.find(r=>r.path===o),s=this.app.freshness.reasonOf(o);(i?i.reason:void 0)!==s&&(s||yb(this.blockedReason))&&(this.latestSeq=0,this.status=this.unansweredStatus("idle"))}this.render()}onResult(e,n){if(!(!this.active||n!==this.latestSeq||e.outcome==="cancelled")){if(e.outcome==="done")this.status=this.statusFor("done",void 0,this.latestFocus);else{this.blockedReason=e.outcome==="blocked"?e.reason:void 0;let o=e.message||(e.outcome==="blocked"?"Not opened.":"VS Code could not show the file.");this.status=this.statusFor(e.outcome,o),this.app.announce(ad(e.outcome,o))}this.render()}}cancelTimer(){this.timer!==null&&clearTimeout(this.timer),this.timer=null}statusFor(e,n,o=!1){let i=this.currentLocs(),s=Math.min(this.quote,Math.max(0,i.length-1)),r={state:e,quote:s,quotes:i.length};return i[s]&&e!=="none"&&e!=="unavailable"&&(r.loc=i[s]),n&&(r.message=n),o&&(e==="opening"||e==="done")&&(r.focusEditor=!0),r}unansweredStatus(e){let n=this.currentLocs(),o=n[Math.min(this.quote,n.length-1)];if(!n.length||!o)return this.statusFor("none");if(!this.app.caps.canOpenSource)return this.statusFor("unavailable");let i=this.localBlock(o);return i?this.statusFor("blocked",i):this.statusFor(e)}localBlock(e){let n=e.evidenceId?this.app.freshness.reasonOf(e.file):void 0;return n?e.file+": "+me[n]+"; not opened.":null}scheduleOpen(){this.cancelTimer(),this.latestSeq=0,this.status=this.unansweredStatus("opening"),this.status.state!=="unavailable"&&(this.timer=setTimeout(()=>{this.timer=null,this.sendOpen(!1)},vb))}get pendingOpen(){return this.timer!==null}flushOpen(){this.timer!==null&&(this.cancelTimer(),this.sendOpen(!1))}sendOpen(e){if(!this.active)return;let n=this.currentLocs(),o=n[Math.min(this.quote,n.length-1)];if(!o){this.app.bridge.post({v:1,type:"walk",state:"clear"}),this.status=this.statusFor("none"),this.render();return}if(!this.app.caps.canOpenSource){this.status=this.statusFor("unavailable"),this.render();return}let i=++this.seq;this.latestSeq=i,this.latestFocus=e,this.remember(),this.app.bridge.saveState(this.app.getState());let s=As(o);s.walk=!0,s.seq=i,e&&(s.focus=!0),this.app.postRequest(s,a=>this.onResult(a,i));let r=this.localBlock(o);this.blockedReason=r?this.app.freshness.reasonOf(o.file):void 0,this.status=r?this.statusFor("blocked",r):this.statusFor("opening",void 0,e),this.render()}show(e){let n=this.current();if(n){this.selecting=!0;try{this.app.showWalkClaim(n)}finally{this.selecting=!1}e==="open"?this.scheduleOpen():e==="rest"&&(this.latestSeq=0,this.status=this.unansweredStatus("idle")),this.remember(),this.render(e==="keep"?!1:"claim"),this.app.announce(this.announcement()),this.app.saveSoon()}}announcement(){let e=this.index(),n=this.current();return e&&n?id(this.position,this.list.length,this.filter,e,n):""}render(e=!1){this.app.renderWalkBar(),os(this.app.rail.root,this.paneMark(),e)}paneMark(){if(!this.active)return null;let e=this.current();return!e||!ue(e,this.app.selection)?null:{quote:this.status.quote,status:this.status}}remember(){let e=this.app.workflowRevision,n=this.current();if(!e||!n)return;let o={filter:this.filter,claim:{kind:n.kind,id:n.id}};this.quote>0&&(o.quote=this.quote),this.active&&(o.active=!0),this.remembered={revision:e,state:o}}viewState(){let e=this.app.workflowRevision;if(!this.remembered||!e||this.remembered.revision!==e)return;let n={...this.remembered.state,claim:{...this.remembered.state.claim}};return this.active?n.active=!0:delete n.active,n}restore(e,n){let o=wb(n);this.restored=o?{revision:e,state:o}:null}destroy(){this.cancelTimer()}};var xb=32,Cs=900,Eb=620,kb=.47,mc=.25,gc=.75,_b=1500,Ao=class{constructor(e,n){this.graph=null;this.index=null;this.edgeAnchor=null;this.flowOn=!0;this.exceptionsOn=!1;this.filters=new sn;this.viewportState={x:0,y:0,zoom:1};this.selection=null;this.collapsedState=[];this.railTab="about";this.legendOpen=!1;this.freshness=new on;this.openHintShown=!1;this.railOpen=!0;this.railChosen=!1;this.dockedHidden=!1;this.railWidth=360;this.railMode="docked";this.sheetFraction=kb;this.paneColumns=1;this.workflowDocument=null;this.workflowRevision=null;this.restoredView=null;this.restoredComposer=null;this.restoredTab=null;this.restoredSelection=null;this.restoredSheet=null;this.pending=new Map;this.requestSerial=0;this.disposers=[];this.destroyed=!1;this.releaseRevealFocus=null;this.releasePage=()=>{};this.saveSoon=Fs(()=>this.bridge.saveState(this.getState()),250);this.root=e,this.bridge=n,this.caps=n.capabilities,this.themes=new rn(e,n.theme||"light"),this.doubleClick=new xn(e),this.disposers.push(()=>this.doubleClick.dispose()),this.walk=new Io(this),ac(this);let o=fc(n);o&&Lo(this,o,!1),o&&typeof o.workflowRevision=="string"&&o.viewport&&(this.restoredView={revision:o.workflowRevision,viewport:{...o.viewport}});let i=o&&typeof o.workflowRevision=="string"?yr(o.composer):null;o&&i&&(this.restoredComposer={revision:o.workflowRevision,composer:i});let s=o&&typeof o.workflowRevision=="string"?Mo(o.railTab):null;o&&s&&(this.restoredTab={revision:o.workflowRevision,tab:s});let r=o&&typeof o.workflowRevision=="string"?So(o.selection):null;o&&r&&(this.restoredSelection={revision:o.workflowRevision,selection:r}),o&&typeof o.workflowRevision=="string"&&o.sheetOpen===!0&&(this.restoredSheet=o.workflowRevision),o&&typeof o.workflowRevision=="string"&&this.walk.restore(o.workflowRevision,o.walk),this.disposers.push(n.onMessage(a=>this.onMessage(a))),typeof window<"u"&&this.disposers.push(_(window,"resize",()=>this.onResize())),this.disposers.push(_(e,"keydown",a=>{a.defaultPrevented||!(a.ctrlKey||a.metaKey)||a.altKey||a.key!=="f"&&a.key!=="F"||this.modalOpen(a.target)||(a.preventDefault(),this.focusSearch())})),this.disposers.push(_(e,"keydown",a=>{a.key!=="Escape"||a.defaultPrevented||!this.walk.active||this.modalOpen(a.target)||this.walk.stop()&&a.preventDefault()})),this.chrome.setWidth(this.rootWidth()),this.view.setPanelWidth(this.rootWidth())}rootWidth(){let e=this.root.getBoundingClientRect().width;return e>0?e:0}onResize(){let e=this.chrome.headerLayout;this.chrome.setWidth(this.rootWidth())!==e&&(X(this),this.renderWalkBar()),this.view.setPanelWidth(this.rootWidth()),this.autoRail(),this.paneColumns!==this.selectionColumns()&&Q(this)}selectionColumns(){return this.railMode==="sheet"&&this.rootWidth()>=Eb?2:1}railShown(){return this.railOpen}refreshChrome(){X(this)}focusSearch(){this.chrome.setSearchOpen(!0),this.search.focus()}showAbout(e={}){this.railTab="about",this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),Q(this),this.saveSoon();let n=e.at==="limitations"?this.rail.revealLimitations():e.focus?this.rail.activePanel():null;if(e.focus&&n)try{n.focus()}catch{}this.announce(e.at==="limitations"?"About: the coverage limitations.":"About this revision shown.")}modalOpen(e){if(this.sheet&&this.sheet.open||ni(this))return!0;let n=e;return!!n&&typeof n.closest=="function"&&!!n.closest(".mlv-sheet, .mlv-workflow__composer")}closeHeaderPanels(){return yn(this,!0)}headerPanelOpen(){return ni(this)}setWorkflow(e,n){let o=this.workflowRevision!==e.revision.id,i=n;if(!n||!n.viewport){let r=null;this.graph&&this.workflowRevision===e.revision.id?r={...this.viewportState}:!this.graph&&this.restoredView&&this.restoredView.revision===e.revision.id&&(r={...this.restoredView.viewport}),r&&(i={...n||{},viewport:r})}this.restoredView=null;let s=this.restoredComposer&&this.restoredComposer.revision===e.revision.id?this.restoredComposer.composer:null;if(this.restoredComposer=null,this.workflowRevision!==e.revision.id){let r=!this.graph&&this.restoredTab&&this.restoredTab.revision===e.revision.id?this.restoredTab.tab:null;this.railTab=r||"about"}if(this.restoredTab=null,!this.graph){let r=a=>a===e.revision.id;this.restoredSelection&&r(this.restoredSelection.revision)&&(this.selection=this.restoredSelection.selection),r(this.restoredSheet)&&(this.railChosen=!0,this.railOpen=!0)}this.restoredSelection=null,this.restoredSheet=null,this.workflowDocument=e,this.workflowRevision=e.revision.id,o&&this.view.closeOverview(!1),this.autoRail(),sc(this,wn(e),i,!0),kr(this,e,s),this.legendOtherKinds(),this.view.afterChromeChange(),this.walk.onDocument(o),this.syncNoticeReview()}postRequest(e,n){this.requestSerial+=1;let o="";for(let s=0;s<4;s++)o+=Math.floor(Math.random()*36).toString(36);let i="r"+this.requestSerial.toString(36)+"-"+o;for(this.pending.set(i,n);this.pending.size>xb;){let s=this.pending.keys().next().value;if(s===void 0)break;this.pending.delete(s)}return this.bridge.post({...e,requestId:i}),i}onActionResult(e){if(!e||typeof e.requestId!="string")return;let n=this.pending.get(e.requestId);n&&(this.pending.delete(e.requestId),n(e))}openLocation(e,n=!1){lc(this,e,n)}setStale(e){let n=this.freshness.list();this.freshness.set(e)&&(this.view.setStale(this.freshness.list()),this.index&&this.view.refresh(this.selection),X(this),Q(this),this.walk.onStale(n),this.syncNoticeReview())}showHostNotice(e,n){let o=!!n&&n.length===1&&n[0]==="checking";if(this.freshness.checking!==o&&(this.freshness.checking=o,X(this)),o)return;let i=this.notice.root.hidden;this.notice.update(e,n),this.syncNoticeReview();let s=this.root.querySelector(".mlv-body");s&&this.notice.root.nextSibling!==s&&this.root.insertBefore(this.notice.root,s),this.graph&&i!==this.notice.root.hidden&&this.view.afterChromeChange()}syncNoticeReview(){let e=this.graph&&this.walk.changedOffered()?this.walk.counts().changed:0;this.notice.setReview(e,this.walk.active)}renderWalkBar(){if(!this.walkBar)return;let e=this.walk;if(!e.active){this.walkBar.update(null);return}this.walkBar.update({position:e.position,total:e.list.length,filter:e.filter,counts:e.counts(),offered:e.offered(),status:e.status,narrow:this.chrome.headerLayout==="narrow"})}onWalkShown(e){this.renderWalkBar(),X(this),this.syncNoticeReview(),e||Q(this),this.graph&&this.view.afterSheetToggle()}openRailForWalk(){this.railMode!=="docked"||this.railOpen||(this.railChosen=!0,this.setRailOpen(!0))}walkOwns(e){return ue(this.walk.current(),e)}showWalkClaim(e){let n=this.index;if(!n)return;this.view.holdHover();let o=r=>{let a=n.edgeById.get(r);a&&(this.view.expandAncestors(a.source),this.view.expandAncestors(a.target))};if(e.kind==="issue"){let r=n.issueById.get(e.id);if(!r)return;for(let a of r.nodeIds)this.view.expandAncestors(a);for(let a of r.edgeIds)o(a)}else e.kind==="node"?this.view.expandAncestors(e.id):o(e.id);this.select(e,{tab:"inspector",showClaim:!0}),e.kind==="issue"?this.view.frameIssue(e.id):this.view.revealTarget({kind:e.kind,id:e.id});let i=this.view.canvasEl,s=i.ownerDocument?i.ownerDocument.activeElement:null;if(s&&s!==i&&i.contains(s))try{i.focus()}catch{}}revealClaim(e,n={}){let o=this.index;if(!o)return!1;if(!(e.kind==="node"?o.nodeById.has(e.id):e.kind==="edge"?o.edgeById.has(e.id):o.issueById.has(e.id))){let l="That claim is not in the revision shown here.";return this.view.toast(l),this.announce(l),!1}this.sheet.open&&this.sheet.hide(null),yn(this,!1),this.view.closeOverview(!1)&&X(this);let s=l=>{let d=o.edgeById.get(l);d&&(this.view.expandAncestors(d.source),this.view.expandAncestors(d.target))};if(e.kind==="issue"){let l=o.issueById.get(e.id);for(let d of l.nodeIds)this.view.expandAncestors(d);for(let d of l.edgeIds)s(d)}else e.kind==="node"?this.view.expandAncestors(e.id):s(e.id);this.openRailForWalk();let r=n.center!==!1;e.kind==="node"?this.select(e,{tab:"inspector",showClaim:!0,center:r,reveal:r,pulse:r}):this.select(e,{tab:"inspector",showClaim:!0}),e.kind==="issue"?this.view.frameIssue(e.id):e.kind==="edge"?this.view.frameEdge(e.id):e.kind==="node"&&!r&&this.view.revealTarget({kind:"node",id:e.id});let a=e.kind==="issue"?null:{kind:e.kind,id:e.id};return this.view.focusTarget(a),this.holdRevealFocus(a),!0}holdRevealFocus(e){this.releaseRevealFocus&&this.releaseRevealFocus();let n=this.root.ownerDocument,o=n?n.defaultView:null;if(!n||!o)return;let i=()=>{let a=n.activeElement;(!a||a===n.body||a===n.documentElement)&&this.view.focusTarget(e)};o.addEventListener("focus",i);let s=o.setTimeout(()=>r(),_b),r=()=>{o.removeEventListener("focus",i),o.clearTimeout(s),this.releaseRevealFocus===r&&(this.releaseRevealFocus=null)};this.releaseRevealFocus=r}setPhaseIndexShown(e){this.view.setPhaseIndex({hidden:!e}),X(this),this.saveSoon(),this.announce("Phase index "+(e?"shown":"hidden")+".")}toggleOverview(e=!1){if(this.view.overviewOpen){if(this.view.closeOverview(!0),e)try{this.view.canvasEl.focus()}catch{}}else this.view.openOverview();X(this)}setLegend(e){this.legendOpen=e,this.legend.setOpen(e),X(this),this.saveSoon()}setRailTab(e){this.railTab=e,this.railMode==="sheet"&&!this.railOpen&&(this.railChosen=!0,this.setRailOpen(!0)),Q(this),this.saveSoon()}showRailTab(e){this.railTab=e,this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),Q(this),this.saveSoon(),this.railMode==="sheet"&&this.rail.focusTab(e)}focusRailTabs(){this.railOpen||(this.railChosen=!0,this.setRailOpen(!0)),Q(this),this.rail.focusTab(this.railTab)}toggleRail(){this.railChosen=!0,this.railMode==="docked"&&(this.dockedHidden=this.railOpen),this.setRailOpen(!this.railOpen)}collapseSheet(e=!1){if(this.railMode!=="sheet"||!this.railOpen)return!1;if(this.railChosen=!0,this.setRailOpen(!1),this.announce("Bottom panel collapsed."),e)try{this.view.canvasEl.focus()}catch{}return!0}sheetFractionMax(){let e=this.root.querySelector(".mlv-body"),n=e?e.getBoundingClientRect().height:0;if(!(n>0))return gc;let o=Math.min(240,.45*n);return Math.max(mc,Math.min(gc,(n-o)/n))}setSheetFraction(e){let n=Math.max(mc,Math.min(this.sheetFractionMax(),e));Math.abs(n-this.sheetFraction)<.001||(this.sheetFraction=n,this.rail.setSheetFraction(n),this.railMode==="sheet"&&this.railOpen&&this.graph&&this.view.afterSheetToggle())}autoRail(){let e=this.root.getBoundingClientRect().width;if(!(e>0))return;let n=e-this.railWidth>=Cs?"docked":"sheet",o=this.railOpen;if(n!==this.railMode?o=n==="sheet"?this.railOpen&&this.railChosen:!this.dockedHidden:n==="docked"&&!this.railChosen&&(o=!0),n===this.railMode&&o===this.railOpen)return;let i=n!==this.railMode;this.railMode=n,this.rail.setMode(n,this.sheetFraction),this.setRailOpen(o,!0),i&&(this.saveSoon(),n==="docked"&&this.setRailWidth(this.railWidth),Q(this),X(this)),this.graph&&this.view.handleResize()}legendOtherKinds(){let e=new Map,n=0;for(let i of this.graph?.edges||[])if(i.kind==="unknown")n++;else{let s=i.authoredKind||i.kind;e.set(s,(e.get(s)||0)+1)}let o=Array.from(e,([i,s])=>({kind:i,count:s})).sort((i,s)=>s.count-i.count||(i.kinds.kind?1:0));this.legend.setKinds(o,n)}showRailForFinding(){return this.railChosen=!0,this.railOpen?!1:(this.setRailOpen(!0),!0)}setRailOpen(e,n=!1){let o=this.railOpen!==e;this.railOpen=e,e&&this.railMode==="docked"&&(this.dockedHidden=!1),this.rail.setOpen(e),o&&(this.saveSoon(),this.chrome&&X(this),e&&this.rail&&Q(this),!n&&this.railMode==="sheet"&&this.graph&&this.view.afterSheetToggle())}toggleShortcuts(e){if(typeof e=="boolean"?e:!this.sheet.open){this.sheet.show();return}this.sheet.hide(this.view.canvasEl)}setRailWidth(e){let n=this.rootWidth(),o=this.railMode==="docked"&&n-Cs>=280?n-Cs:560;this.railWidth=Math.max(280,Math.min(560,o,Math.round(e))),this.railMode==="docked"&&(this.rail.root.style.width=this.railWidth+"px")}setExceptions(e){this.exceptionsOn=e,e?this.view.canvasEl.setAttribute("data-exceptions","on"):this.view.canvasEl.removeAttribute("data-exceptions"),X(this),this.announce(e?"Observed claims faded; inferred and unresolved claims stand out.":"Observed claims shown normally.")}setFlow(e){this.flowOn=e,this.view.flow.setEnabled(e),this.view.flow.syncCanvas(),X(this),this.saveSoon()}select(e,n){e.kind!=="edge"&&(this.edgeAnchor=null),this.view.clearArrowLane(),this.selection=e,this.railTab=n&&n.tab?n.tab:this.selectionTab(n&&n.fromList);let o=!1;if(e.kind==="issue"&&(o=this.showRailForFinding()),n&&n.showClaim&&(o=this.showRailForClaim()||o),this.view.applySelection(e),Q(this),n&&n.reveal&&e.kind==="edge"?this.view.revealEdge(e.id):n&&n.center&&n.reveal?this.view.revealNode(e.id,!!n.pulse):n&&n.center?this.view.centerOnNode(e.id,!!n.pulse):o&&e.kind!=="issue"&&this.view.keepInView({kind:e.kind,id:e.id}),this.announceSelection(),this.walk.followSelection(e),n&&n.open)if(this.walk.active&&this.walkOwns(e))this.walk.reopen(!!n.focusEditor);else{let i=this.locOf(e);i&&this.openLocation(i,!!n.focusEditor)}this.saveSoon()}showRailForClaim(){return this.railOpen?(this.railChosen=!0,!1):this.railMode==="docked"&&this.railChosen?!1:(this.railChosen=!0,this.setRailOpen(!0),!0)}selectionTab(e){return e&&this.railTab===e&&this.railShown()?e:"inspector"}clearSelection(){this.selection&&(this.selection=null,this.view.applySelection(null),Q(this),this.saveSoon())}locOf(e){if(!this.index)return null;if(e.kind==="node"){let o=this.index.nodeById.get(e.id);return o&&o.loc.file?o.loc:null}if(e.kind==="edge"){let o=this.index.edgeById.get(e.id);return o&&o.loc.file?o.loc:null}let n=this.index.issueById.get(e.id);return n&&n.loc.file?n.loc:null}selectedNodeId(){let e=this.selection;if(!e||!this.index)return null;if(e.kind==="node")return e.id;if(e.kind!=="issue")return null;let n=this.index.issueById.get(e.id);return n&&n.nodeIds.length?n.nodeIds[0]:null}announceSelection(){let e=this.selection;if(!(!e||!this.index)){if(Za()){let n=hd(this.index,e);n&&this.announce(n);return}if(e.kind==="node"){let n=this.view.labelOf(e.id);n&&this.announce("Selected "+n)}else if(e.kind==="issue"){let n=this.index.issueById.get(e.id);n&&this.announce("Finding "+(n.short?n.short+" ("+n.code+")":n.code)+", "+n.severity+" severity: "+n.title)}}}applyFilters(e){e(),X(this),this.index&&(this.view.render(),this.view.applySelection(this.selection)),Q(this),this.saveSoon()}clearFilters(){this.search.clear(),this.applyFilters(()=>this.filters.reset())}selectLane(e){if(!this.index)return;let n=this.index.roots(e);if(!n.length){this.announce("That phase has no steps.");return}this.select({kind:"node",id:n[0]},{center:!0,pulse:!0,fromList:"outline"})}zoomToSelection(){let e=this.selectedNodeId();e&&this.view.zoomToNode(e)}announce(e){this.liveEl.textContent=e}activateHit(e){e.kind==="node"?this.focusNode(e.id,{center:!0,pulse:!0,showClaim:!0}):this.focusIssue(e.id)}onKeyDown(e){uc(this,e)}onMessage(e){pc(this,e)}focusNode(e,n){if(this.index){if(!this.index.nodeById.has(e)){this.view.toast("Node not found in this graph");return}this.view.expandAncestors(e),this.select({kind:"node",id:e},{center:n?n.center!==!1:!0,pulse:n?!!n.pulse:!1,showClaim:!!(n&&n.showClaim)})}}focusIssue(e,n){if(!this.index)return;let o=this.index.issueById.get(e);if(o){for(let i of o.nodeIds)this.view.expandAncestors(i);this.select({kind:"issue",id:e},{fromList:n&&n.fromList}),this.view.frameIssue(e)}}openIssue(e,n=!1){if(this.focusIssue(e,{fromList:"issues"}),!this.selection||this.selection.kind!=="issue"||this.selection.id!==e)return;if(this.walk.active&&this.walkOwns(this.selection)){this.walk.reopen(n);return}let o=this.locOf({kind:"issue",id:e});o&&this.openLocation(o,n)}setFilters(e){this.applyFilters(()=>this.filters.patch(e))}setTheme(e){this.themes.apply(e)}getState(){return hc(this)}destroy(){if(!this.destroyed){this.destroyed=!0,this.saveSoon.cancel(),this.releaseRevealFocus&&this.releaseRevealFocus(),this.walk.destroy(),this.pending.clear();for(let e of this.disposers)try{e()}catch{}this.disposers=[],this.themes.destroy(),this.chrome.destroy(),this.walkBar.destroy(),this.view.destroy(),this.releasePage(),D(this.root),this.root.classList.remove("mlv-root")}}};var Jt=null;function Sb(){if(Jt)return Jt;let t=typeof window<"u"?window:{};if(typeof t.acquireVsCodeApi=="function")try{Jt=t.acquireVsCodeApi()}catch{Jt=null}return Jt}function Mb(){if(typeof document>"u"||!document.body)return"light";let t=document.body.className||"";return t.indexOf("vscode-high-contrast")>=0?"hc":t.indexOf("vscode-dark")>=0?"dark":"light"}function Lb(t){if(typeof window>"u")return()=>{};let e=n=>{let o=n&&n.data;o&&typeof o=="object"&&t(o)};return window.addEventListener("message",e),()=>window.removeEventListener("message",e)}function vc(){let t=Sb(),e={canOpenSource:!0};return{host:"vscode",theme:Mb(),capabilities:e,post(n){t&&typeof t.postMessage=="function"&&t.postMessage(n)},onMessage(n){return Lb(n)},saveState(n){t&&typeof t.setState=="function"&&t.setState(n)},loadState(){if(t&&typeof t.getState=="function"){let n=t.getState();return n&&typeof n=="object"?n:null}return null}}}var Ib="0.3.0";function Ab(t,e,n){if(!t)throw new Error("MLView.mountWorkflow: a root element is required");if(!n)throw new Error("MLView.mountWorkflow: a HostBridge is required");let o=new Ao(t,n);return o.setWorkflow(e),o}var Cb={vscode(){return vc()}};return Mc(Tb);})(); diff --git a/webview/src/app.ts b/webview/src/app.ts index 947e89d6..46cebbaf 100644 --- a/webview/src/app.ts +++ b/webview/src/app.ts @@ -17,6 +17,7 @@ * `app/keys.ts` the keyboard binding * `app/messages.ts` the host protocol, inbound * `app/state.ts` `ViewState`, both directions + * `app/walk.ts` the review walk (viewer M3) * * Those modules are this class's own halves, not an API: every field and method * below is public only because they reach it, and nothing outside `app/` may @@ -47,6 +48,9 @@ import { applyState, safeLoad, snapshotState } from './app/state.js'; import { sanitizeRailTab, sanitizeSelection } from './ui/commands.js'; import { screenReaderActive } from './motion.js'; import { selectionAnnouncement } from './ui/selection.js'; +import { ReviewWalk } from './app/walk.js'; +import { WalkBar } from './ui/walkbar.js'; +import { sameClaim } from './walk.js'; import type { SearchHit } from './search.js'; import type { ActionResult, @@ -101,6 +105,9 @@ export interface ShowAboutOptions { type RequestFrame = UiToHost & { requestId?: string }; +/** Viewer M3 (step 14): how long after a reveal a window focus puts the keyboard back on the claim. */ +export const REVEAL_FOCUS_HOLD_MS = 1500; + export interface SelectOptions { /** Open the selection's cited source beside the panel (Enter, double-click). Focus stays here. */ open?: boolean; @@ -201,6 +208,8 @@ export class App implements MLViewApp { private requestSerial = 0; private disposers: (() => void)[] = []; private destroyed = false; + /** Viewer M3 (step 14): stops putting the keyboard back on a revealed claim (`holdRevealFocus`). */ + private releaseRevealFocus: (() => void) | null = null; view!: CanvasView; chrome!: Chrome; @@ -214,6 +223,9 @@ export class App implements MLViewApp { notice!: HostNotice; /** Viewer M1 review: the second click of a double-click opens what the first one selected. */ doubleClick: DoubleClickOpener; + /** Viewer M3: the review walk, and its bar at the foot of the diagram (above the bottom sheet). */ + walk: ReviewWalk; + walkBar!: WalkBar; saveSoon = debounce(() => this.bridge.saveState(this.getState()), 250); @@ -224,6 +236,7 @@ export class App implements MLViewApp { this.themes = new ThemeController(root, bridge.theme || 'light'); this.doubleClick = new DoubleClickOpener(root); this.disposers.push(() => this.doubleClick.dispose()); + this.walk = new ReviewWalk(this); buildAppUi(this); const restored = safeLoad(bridge); if (restored) applyState(this, restored, false); @@ -237,6 +250,8 @@ export class App implements MLViewApp { const selection = restored && typeof restored.workflowRevision === 'string' ? sanitizeSelection(restored.selection) : null; if (restored && selection) this.restoredSelection = { revision: restored.workflowRevision as string, selection }; if (restored && typeof restored.workflowRevision === 'string' && restored.sheetOpen === true) this.restoredSheet = restored.workflowRevision; + // Viewer M3: the review walk's place, for the revision it was saved with. + if (restored && typeof restored.workflowRevision === 'string') this.walk.restore(restored.workflowRevision, restored.walk); this.disposers.push(bridge.onMessage((msg) => this.onMessage(msg))); if (typeof window !== 'undefined') this.disposers.push(on(window, 'resize', () => this.onResize())); // Viewer M2: the find key (Cmd+F on macOS, Ctrl+F elsewhere) focuses the search from anywhere @@ -252,6 +267,17 @@ export class App implements MLViewApp { ev.preventDefault(); this.focusSearch(); })); + // Viewer M3: Escape always ends the review walk, wherever the focus is in the viewer (the + // header, a docked rail), not only on the canvas, whose cascade ends it first. A surface that + // used the Escape itself (a menu, the shortcut sheet, the Refine… popover, the search box) + // marks it handled first, and a modal surface keeps it. Marked handled here, so the panel's + // bootstrap keeps VS Code from also acting on it. + this.disposers.push(on(root, 'keydown', (ev: KeyboardEvent) => { + if (ev.key !== 'Escape' || ev.defaultPrevented || !this.walk.active) return; + if (this.modalOpen(ev.target)) return; + if (!this.walk.stop()) return; + ev.preventDefault(); + })); this.chrome.setWidth(this.rootWidth()); this.view.setPanelWidth(this.rootWidth()); // No `ready` here: the host bootstrap posts the one `ready` of a page load @@ -270,7 +296,11 @@ export class App implements MLViewApp { /** The panel was resized: the header's shape and the rail's width rule follow it. */ private onResize(): void { const before = this.chrome.headerLayout; - if (this.chrome.setWidth(this.rootWidth()) !== before) renderChrome(this); + if (this.chrome.setWidth(this.rootWidth()) !== before) { + renderChrome(this); + // Viewer M3: the walk bar's narrow form follows the header's. + this.renderWalkBar(); + } this.view.setPanelWidth(this.rootWidth()); this.autoRail(); // The sheet's Selection pane changes between one and two columns at 620 px. @@ -356,6 +386,7 @@ export class App implements MLViewApp { * for this same revision is restored instead of fitting (VIEWUI-3). */ setWorkflow(document: WorkflowDocument, preserve?: Partial): void { + const revisionChanged = this.workflowRevision !== document.revision.id; let next = preserve; if (!preserve || !preserve.viewport) { let viewport: Viewport | null = null; @@ -390,6 +421,8 @@ export class App implements MLViewApp { this.restoredSheet = null; this.workflowDocument = document; this.workflowRevision = document.revision.id; + // Viewer M3: another revision closes the phase overview drawn for the one before. + if (revisionChanged) this.view.closeOverview(false); // Before the first fit, so the fit sees the canvas the rail leaves (issue 6). this.autoRail(); setGraph(this, normalizeWorkflow(document), next, true); @@ -398,6 +431,9 @@ export class App implements MLViewApp { // The header is built after the fit ran; refit a viewport nobody moved to // the canvas that is actually left (issue 1). this.view.afterChromeChange(); + // Viewer M3: another revision ends the walk and starts it fresh; a remount brings it back. + this.walk.onDocument(revisionChanged); + this.syncNoticeReview(); } /** @@ -439,11 +475,15 @@ export class App implements MLViewApp { * marked, their Open links are disabled with the reason, and the status bar counts them. */ setStale(files: StaleFile[]): void { + const previous = this.freshness.list(); if (!this.freshness.set(files)) return; this.view.setStale(this.freshness.list()); if (this.index) this.view.refresh(this.selection); renderChrome(this); renderRail(this); + // Viewer M3: the walk's Changed files filter and the notice's "Review affected claims" follow. + this.walk.onStale(previous); + this.syncNoticeReview(); } /** @@ -460,17 +500,209 @@ export class App implements MLViewApp { if (checking) return; const before = this.notice.root.hidden; this.notice.update(message, codes); + this.syncNoticeReview(); const body = this.root.querySelector('.mlv-body'); if (body && this.notice.root.nextSibling !== body) this.root.insertBefore(this.notice.root, body); if (this.graph && before !== this.notice.root.hidden) this.view.afterChromeChange(); } - /** VIEW-12: the toolbar's copy of the minimap chevron. */ - setMinimapCollapsed(next: boolean): void { - this.view.setMinimapCollapsed(next); + /* ── the review walk (viewer M3) ────────────────────────────────────── */ + + /** The stale notice offers "Review affected claims" while claims cite a changed file. */ + syncNoticeReview(): void { + const count = this.graph && this.walk.changedOffered() ? this.walk.counts().changed : 0; + this.notice.setReview(count, this.walk.active); + } + + /** The walk bar for the walk's state, or hidden. */ + renderWalkBar(): void { + if (!this.walkBar) return; + const walk = this.walk; + if (!walk.active) { + this.walkBar.update(null); + return; + } + this.walkBar.update({ + position: walk.position, + total: walk.list.length, + filter: walk.filter, + counts: walk.counts(), + offered: walk.offered(), + status: walk.status, + narrow: this.chrome.headerLayout === 'narrow', + }); + } + + /** + * The walk started or ended: the bar above the bottom sheet shows or goes, Review is pressed or not, + * the notice's action follows, and the canvas, which changed height, keeps its picture (no refit) + * with the selection in view, as for the bottom sheet. + */ + onWalkShown(shown: boolean): void { + this.renderWalkBar(); + renderChrome(this); + this.syncNoticeReview(); + if (!shown) renderRail(this); + if (this.graph) this.view.afterSheetToggle(); + } + + /** Starting the walk shows a docked rail the reader hid (the walk reads claims in its Selection tab). */ + openRailForWalk(): void { + if (this.railMode !== 'docked' || this.railOpen) return; + this.railChosen = true; + this.setRailOpen(true); + } + + /** The walk's current claim is `sel`. */ + walkOwns(sel: Sel | null): boolean { + return sameClaim(this.walk.current(), sel); + } + + /** + * One step of the walk on the canvas: the claim's boxes are drawn (a collapsed group around it + * opens), it is selected and shown in the Selection tab (a collapsed bottom sheet opens), and it + * is brought into view above the sheet: a finding frames every step it cites; a step or a + * connection is zoomed to reading size when the diagram is below it, else panned the least + * distance that shows it whole. The keyboard stays on the diagram: a card or connection that + * held the focus hands it to the canvas, so the walk's keys keep answering there. The hover card + * goes and stays away until the pointer moves (`CanvasView.holdHover`). + */ + showWalkClaim(claim: Sel): void { + const index = this.index; + if (!index) return; + // Viewer M3 (live check, W3): no hover card for what the step pans under a resting pointer. + this.view.holdHover(); + const expandEnds = (edgeId: string) => { + const edge = index.edgeById.get(edgeId); + if (!edge) return; + this.view.expandAncestors(edge.source); + this.view.expandAncestors(edge.target); + }; + if (claim.kind === 'issue') { + const issue = index.issueById.get(claim.id); + if (!issue) return; + for (const id of issue.nodeIds) this.view.expandAncestors(id); + for (const id of issue.edgeIds) expandEnds(id); + } else if (claim.kind === 'node') this.view.expandAncestors(claim.id); + else expandEnds(claim.id); + this.select(claim, { tab: 'inspector', showClaim: true }); + if (claim.kind === 'issue') this.view.frameIssue(claim.id); + else this.view.revealTarget({ kind: claim.kind, id: claim.id }); + const canvas = this.view.canvasEl; + const active = canvas.ownerDocument ? (canvas.ownerDocument.activeElement as HTMLElement | null) : null; + if (active && active !== canvas && canvas.contains(active)) { + try { + canvas.focus(); + } catch (_e) { + /* the canvas may already be torn down */ + } + } + } + + /** + * Viewer M3 (step 14), MLView: Reveal in Diagram. The reader chose this claim from the code in the + * editor, and VS Code moved the keyboard focus to this panel because they asked for the diagram. + * The shortcut sheet, the Refine… popover (its text is kept) and the phase overview close; the + * claim's boxes are drawn (a collapsed group around it opens); it is selected and shown in the + * Selection tab (a side panel the reader hid opens, as for the walk, and so does a collapsed bottom + * sheet); and it is brought into view above the sheet and clear of the phase index: a step is + * centred, zoomed to reading size when the diagram is smaller (`center` false: panned the least + * distance instead), a connection frames both its ends (or its source, when they are too far + * apart), and a finding frames every step it cites. + * The keyboard lands on the step's card or the connection (the canvas for a finding), so the + * diagram's keys answer at once. A claim the displayed revision lacks changes nothing and says so. + * Returns whether the claim was shown. + */ + revealClaim(sel: Sel, opts: { center?: boolean } = {}): boolean { + const index = this.index; + if (!index) return false; + const known = sel.kind === 'node' ? index.nodeById.has(sel.id) : sel.kind === 'edge' ? index.edgeById.has(sel.id) : index.issueById.has(sel.id); + if (!known) { + const text = 'That claim is not in the revision shown here.'; + this.view.toast(text); + this.announce(text); + return false; + } + if (this.sheet.open) this.sheet.hide(null); + closeComposer(this, false); + if (this.view.closeOverview(false)) renderChrome(this); + const expandEnds = (edgeId: string) => { + const edge = index.edgeById.get(edgeId); + if (!edge) return; + this.view.expandAncestors(edge.source); + this.view.expandAncestors(edge.target); + }; + if (sel.kind === 'issue') { + const issue = index.issueById.get(sel.id)!; + for (const id of issue.nodeIds) this.view.expandAncestors(id); + for (const id of issue.edgeIds) expandEnds(id); + } else if (sel.kind === 'node') this.view.expandAncestors(sel.id); + else expandEnds(sel.id); + this.openRailForWalk(); + const center = opts.center !== false; + if (sel.kind === 'node') this.select(sel, { tab: 'inspector', showClaim: true, center, reveal: center, pulse: center }); + else this.select(sel, { tab: 'inspector', showClaim: true }); + if (sel.kind === 'issue') this.view.frameIssue(sel.id); + else if (sel.kind === 'edge') this.view.frameEdge(sel.id); + else if (sel.kind === 'node' && !center) this.view.revealTarget({ kind: 'node', id: sel.id }); + const target = sel.kind === 'issue' ? null : { kind: sel.kind, id: sel.id }; + this.view.focusTarget(target); + this.holdRevealFocus(target); + return true; + } + + /** + * Viewer M3 (step 14), measured live in VS Code 1.139: the host moves the focus into the panel + * just after it posts the reveal, and VS Code hands it over in two steps (the panel's outer frame, + * then this page's window), which leaves this page's focus on : the card focused on arrival + * lost it 3 ms later, and the window was focused again 50 ms after that with nothing focused in + * it. So for REVEAL_FOCUS_HOLD_MS after a reveal, a window focus that finds nothing focused puts + * the keyboard back on the revealed claim. A focus the reader moved anywhere else is left alone. + */ + private holdRevealFocus(target: { kind: 'node' | 'edge'; id: string } | null): void { + if (this.releaseRevealFocus) this.releaseRevealFocus(); + const doc = this.root.ownerDocument; + const win = doc ? doc.defaultView : null; + if (!doc || !win) return; + const onFocus = () => { + const active = doc.activeElement; + if (!active || active === doc.body || active === doc.documentElement) this.view.focusTarget(target); + }; + win.addEventListener('focus', onFocus); + const timer = win.setTimeout(() => release(), REVEAL_FOCUS_HOLD_MS); + const release = () => { + win.removeEventListener('focus', onFocus); + win.clearTimeout(timer); + if (this.releaseRevealFocus === release) this.releaseRevealFocus = null; + }; + this.releaseRevealFocus = release; + } + + /** Viewer M3: the ... menu shows or hides the phase index (it replaced the minimap). */ + setPhaseIndexShown(shown: boolean): void { + this.view.setPhaseIndex({ hidden: !shown }); renderChrome(this); this.saveSoon(); - this.announce('Overview minimap ' + (next ? 'hidden' : 'shown') + '.'); + this.announce('Phase index ' + (shown ? 'shown' : 'hidden') + '.'); + } + + /** + * Viewer M3: the phase overview (Shift+0, the ... menu). Open it over the canvas, or close it and + * go back to where the reader was. `fromMenu`: the menu gave the focus back to its button, so the + * focus is moved into the overview (opening) or onto the canvas (closing). + */ + toggleOverview(fromMenu = false): void { + if (this.view.overviewOpen) { + this.view.closeOverview(true); + if (fromMenu) { + try { + this.view.canvasEl.focus(); + } catch (_e) { + /* the canvas may already be torn down */ + } + } + } else this.view.openOverview(); + renderChrome(this); } setLegend(next: boolean): void { @@ -706,6 +938,8 @@ export class App implements MLViewApp { select(sel: Sel, opts?: SelectOptions): void { if (sel.kind !== 'edge') this.edgeAnchor = null; + // Viewer M3: a selection replaces the phase a move to a phase left the arrow keys on. + this.view.clearArrowLane(); this.selection = sel; // Viewer M2: a selection shows its claim in the Selection tab, unless it was made from the // Findings list or the Outline on screen (a list the reader is walking keeps its place). @@ -720,9 +954,15 @@ export class App implements MLViewApp { else if (opts && opts.center) this.view.centerOnNode(sel.id, !!opts.pulse); else if (openedRail && sel.kind !== 'issue') this.view.keepInView({ kind: sel.kind, id: sel.id }); this.announceSelection(); + // Viewer M3: a selection the reader made while walking moves the walk there when it holds it. + this.walk.followSelection(sel); if (opts && opts.open) { - const loc = this.locOf(sel); - if (loc) this.openLocation(loc, !!opts.focusEditor); + // Viewer M3: opening the walk's own claim (Enter, a double-click) opens its current quote. + if (this.walk.active && this.walkOwns(sel)) this.walk.reopen(!!opts.focusEditor); + else { + const loc = this.locOf(sel); + if (loc) this.openLocation(loc, !!opts.focusEditor); + } } this.saveSoon(); } @@ -896,6 +1136,11 @@ export class App implements MLViewApp { openIssue(id: string, focusEditor = false): void { this.focusIssue(id, { fromList: 'issues' }); if (!this.selection || this.selection.kind !== 'issue' || this.selection.id !== id) return; + // Viewer M3: the walk's own claim opens as the walk's current quote. + if (this.walk.active && this.walkOwns(this.selection)) { + this.walk.reopen(focusEditor); + return; + } const loc = this.locOf({ kind: 'issue', id }); if (loc) this.openLocation(loc, focusEditor); } @@ -916,6 +1161,8 @@ export class App implements MLViewApp { if (this.destroyed) return; this.destroyed = true; this.saveSoon.cancel(); + if (this.releaseRevealFocus) this.releaseRevealFocus(); + this.walk.destroy(); this.pending.clear(); for (const dispose of this.disposers) { try { @@ -927,6 +1174,7 @@ export class App implements MLViewApp { this.disposers = []; this.themes.destroy(); this.chrome.destroy(); + this.walkBar.destroy(); this.view.destroy(); this.releasePage(); clear(this.root); diff --git a/webview/src/app/actions.ts b/webview/src/app/actions.ts index 77602442..c061c9e4 100644 --- a/webview/src/app/actions.ts +++ b/webview/src/app/actions.ts @@ -11,7 +11,31 @@ import { STALE_TEXT } from '../freshness.js'; import { fileLine } from '../dom.js'; import type { App } from '../app.js'; -import type { Loc, RelatedLoc } from '../types.js'; +import type { Loc, RelatedLoc, UiToHost } from '../types.js'; + +type OpenFrame = Extract; + +/** + * The `openLocation` frame for a cited range. Preserve the frozen legacy frame byte-for-byte: + * authored evidence adds identifiers only when it actually has them. The review walk (viewer M3) + * adds `seq`, `walk` and a request id to the same frame. + */ +export function locationFrame(loc: Loc | RelatedLoc): OpenFrame { + const message: OpenFrame = { + v: 1, + type: 'openLocation', + file: loc.file, + absFile: loc.absFile, + line: loc.line, + col: loc.col, + endLine: loc.endLine, + endCol: loc.endCol, + preview: true, + }; + if (loc.evidenceId) message.evidenceId = loc.evidenceId; + if (loc.evidenceId && loc.cell !== undefined) message.cell = loc.cell; + return message; +} /** * Ask the host to open a cited range beside the panel (viewer M1). The host selects and @@ -33,21 +57,7 @@ export function openLocation(app: App, loc: Loc | RelatedLoc, focusEditor = fals // the active editor. Persist synchronously before handing control to the // host; the ordinary debounced save can be lost with the document. app.bridge.saveState(app.getState()); - const message: any = { - v: 1, - type: 'openLocation', - file: loc.file, - absFile: loc.absFile, - line: loc.line, - col: loc.col, - endLine: loc.endLine, - endCol: loc.endCol, - preview: true, - }; - // Preserve the frozen legacy frame byte-for-byte. Authored evidence adds - // identifiers only when it actually has them. - if (loc.evidenceId) message.evidenceId = loc.evidenceId; - if (loc.evidenceId && loc.cell !== undefined) message.cell = loc.cell; + const message = locationFrame(loc); if (focusEditor) message.focus = true; app.bridge.post(message); // What the host is asked to do, not a claim that it did it (VW-10). diff --git a/webview/src/app/build.ts b/webview/src/app/build.ts index 811ae8fe..c3208745 100644 --- a/webview/src/app/build.ts +++ b/webview/src/app/build.ts @@ -4,7 +4,7 @@ * * It is one function because the ORDER is the contract — the canvas has to stay * within four Tab presses of the top of the document (VIEW-12), the legend is anchored - * inside the canvas beside the minimap (VIEW-10), and the overlays go on the app + * inside the canvas over the phase index's corner (VIEW-10), and the overlays go on the app * root so they are never inside the world layer that pans and zooms. Reading * those decisions together is the point of keeping them in one place. * @@ -21,6 +21,7 @@ import { buildShell, claimPage } from '../ui/shell.js'; import { ShortcutSheet } from '../ui/shortcuts.js'; import { SearchController } from '../ui/searchcontroller.js'; import { HostNotice } from '../ui/hostnotice.js'; +import { WalkBar } from '../ui/walkbar.js'; import { runExport } from './exporting.js'; import { renderChrome, renderRail } from './surfaces.js'; import { syncCollapsed } from './documents.js'; @@ -48,7 +49,9 @@ export function buildAppUi(app: App): void { onZoom: (dir) => app.view.zoomStep(dir), onToggleLegend: (next) => app.setLegend(next), onToggleFlow: (next) => app.setFlow(next), - onToggleMinimap: (collapsed) => app.setMinimapCollapsed(collapsed), + onTogglePhaseIndex: (shown) => app.setPhaseIndexShown(shown), + // Viewer M3: the ... menu's Phase overview, as Shift+0 (it closes an open one). + onOverview: () => app.toggleOverview(true), onToggleRail: () => app.toggleRail(), onFitWhole: () => app.view.fitWhole(), onZoomToSelection: () => app.zoomToSelection(), @@ -56,17 +59,59 @@ export function buildAppUi(app: App): void { onShortcuts: () => app.toggleShortcuts(true), // The menu reads the state as it opens (the selection changes without a chrome repaint). onMenuOpen: () => renderChrome(app), + // Viewer M3: the header's Review button and the ... menu's item start or end the walk. A walk + // they start gives the keyboard to the diagram, as "Review affected claims" does, so j and k + // step at once instead of reaching a header button (M3 review, A11Y-M3-1). The menu has + // already closed (and handed the focus back to its trigger) when this runs. + onReview: () => { + const wasActive = app.walk.active; + app.walk.toggle(); + if (!wasActive && app.walk.active) focusCanvas(app); + }, }, shell.zoomBar); // The header is the first thing after the skip link; the ... menu's panel goes on the app root, // so the header's roving toolbar (VIEW-12) keeps its single tab stop. app.root.appendChild(app.chrome.header); + // Viewer M3: the review walk's bar, shown only while the walk runs. It goes at the foot of the + // diagram column, after the canvas in the document: directly above the bottom sheet's tabs (or, + // with the side panel, along the bottom of the diagram). So the canvas keeps its place within + // four Tab presses of the top, and Tab from the canvas reaches the walk's controls, then the + // claim in the sheet. + app.walkBar = new WalkBar({ + onFilter: (filter) => app.walk.setFilter(filter), + // Previous and Next: as k and j. The focus stays on the button, so it can be pressed again. + onStep: (delta) => { + app.walk.step(delta); + }, + onExit: () => { + app.walk.stop(); + focusCanvas(app); + }, + onKey: (ev) => { + const key = ev.key; + if (key === 'j' || key === 'J') return app.walk.step(1); + if (key === 'k' || key === 'K') return app.walk.step(-1); + if (key === ']') return app.walk.stepQuote(1); + if (key === '[') return app.walk.stepQuote(-1); + if (key === 'u' || key === 'U') return app.walk.jump('notObserved', ev.shiftKey); + if (key === 'n' || key === 'N' || key === 'p' || key === 'P') return app.walk.jump('findings', key === 'p' || key === 'P'); + return false; + }, + }); // Viewer M1: the host's banner after the mount (stale files, the root hint, a refused update). // It moves under the header when it is first shown (App.showHostNotice). - app.notice = new HostNotice({ onWorkspaceHint: (action) => app.bridge.post({ v: 1, type: 'workspaceHint', action }) }); + app.notice = new HostNotice({ + onWorkspaceHint: (action) => app.bridge.post({ v: 1, type: 'workspaceHint', action }), + // Viewer M3: the stale notice's "Review affected claims" walks the claims that cite those files. + onReviewAffected: () => { + if (app.walk.start('changed')) focusCanvas(app); + }, + }); app.root.appendChild(app.notice.root); app.root.appendChild(shell.body); shell.body.appendChild(shell.main); + shell.main.appendChild(app.walkBar.root); app.search = new SearchController(app.chrome.searchInput, app.chrome.results, { index: () => app.index, @@ -102,7 +147,10 @@ export function buildAppUi(app: App): void { onShowIssue: (id) => app.focusIssue(id), onChallenge: () => openComposer(app, 'challenge'), onRefine: () => openComposer(app, null), - onOpen: (loc, focusEditor) => app.openLocation(loc, focusEditor), + // Viewer M3: an Open link of the claim the walk is on opens it as the walk's quote. + onOpen: (loc, focusEditor) => { + if (!app.walk.openQuote(loc, !!focusEditor)) app.openLocation(loc, focusEditor); + }, onResize: (w) => app.setRailWidth(w), onSelectLane: (laneId) => app.selectLane(laneId), onToggleCollapse: (id) => { @@ -110,7 +158,12 @@ export function buildAppUi(app: App): void { }, onShowLimitations: () => app.showAbout({ at: 'limitations', focus: true }), onSheetToggle: () => app.toggleRail(), - onSheetCollapse: () => app.collapseSheet(true), + // Viewer M3: Escape inside the open sheet ends a running walk first (one Escape always ends it) + // and gives the focus back to the diagram; otherwise it collapses the sheet, as in M2. + onSheetCollapse: () => { + if (app.walk.stop()) focusCanvas(app); + else app.collapseSheet(true); + }, onSheetResize: (fraction) => app.setSheetFraction(fraction), sheetFraction: () => app.sheetFraction, sheetFractionMax: () => app.sheetFractionMax(), @@ -125,7 +178,7 @@ export function buildAppUi(app: App): void { on(app.rail.root, 'pointerdown', () => { app.railChosen = true; }); on(app.rail.root, 'keydown', () => { app.railChosen = true; }); - // Anchored inside the canvas, beside the minimap, so the key sits with the + // Anchored inside the canvas, over the phase index's corner, so the key sits with the // picture it explains rather than in a modal over it (VIEW-10). app.legend = new Legend((open) => app.setLegend(open)); shell.canvas.appendChild(app.legend.root); @@ -140,6 +193,15 @@ export function buildAppUi(app: App): void { app.setRailOpen(app.railOpen); } +/** Give the keyboard back to the diagram. */ +function focusCanvas(app: App): void { + try { + app.view.canvasEl.focus(); + } catch (_e) { + /* the canvas may already be torn down */ + } +} + /** What the canvas is allowed to ask of the application. */ export function canvasHost(app: App): CanvasHost { const openNode = (id: string, focusEditor: boolean) => @@ -175,9 +237,8 @@ export function canvasHost(app: App): CanvasHost { app.viewportState = { x: vp.x, y: vp.y, zoom: vp.zoom }; app.saveSoon(); }, - onMinimapCollapsed: () => { - // The toolbar carries the accessible copy of this toggle (VIEW-12), so - // the pointer affordance inside the panel has to keep it in step. + onPhaseIndexChanged: () => { + // Viewer M3: the phase index's fold chevron; the ... menu and the saved state follow it. renderChrome(app); app.saveSoon(); }, diff --git a/webview/src/app/documents.ts b/webview/src/app/documents.ts index 327b6b02..eeb9e29a 100644 --- a/webview/src/app/documents.ts +++ b/webview/src/app/documents.ts @@ -1,7 +1,7 @@ /** * The DOCUMENT: the graph the canvas draws and the collapse set that goes with it. * - * `setGraph` owns both. Chrome, rail, outline, minimap and layout all read only + * `setGraph` owns both. Chrome, rail, outline, phase index and layout all read only * the index, so a new document needs no further edits (FEATURES 5.1). Viewer M2 * removed the scope picker and its projection: the whole document is always drawn. */ diff --git a/webview/src/app/keys.ts b/webview/src/app/keys.ts index 5d0dbc53..97bb6291 100644 --- a/webview/src/app/keys.ts +++ b/webview/src/app/keys.ts @@ -11,12 +11,64 @@ import { handleCanvasKey } from '../ui/keymap.js'; import { canvasCommands } from '../ui/commands.js'; import { commandPortFor } from '../ui/appkeys.js'; +import { renderChrome } from './surfaces.js'; import type { CommandPort } from '../ui/commands.js'; import type { App } from '../app.js'; import type { Issue } from '../types.js'; export function onCanvasKey(app: App, ev: KeyboardEvent): void { - handleCanvasKey(ev, canvasCommands(commandPort(app))); + const port = commandPort(app); + handleCanvasKey(ev, canvasCommands(holdingHover(app, app.view.overviewOpen ? closingOverview(app, port) : port))); +} + +/** + * Viewer M3 (live check, W3): the keys that move the selection or the view (the arrows, `e` / + * Shift+E, `n` / `p`, zoom, fit and `z`). Each first holds the hover back until the pointer moves + * (`CanvasView.holdHover`), so a card the diagram pans under a resting pointer shows no hover card. + * The review walk's steps do the same in `App.showWalkClaim`. + */ +const HOLDS_HOVER: ReadonlySet = new Set(['move', 'cycleConnections', 'focusIssue', 'zoom', 'fit', 'zoomToSelection']); + +function holdingHover(app: App, port: CommandPort): CommandPort { + const out = { ...port } as Record; + for (const name of HOLDS_HOVER) { + const fn = (port as unknown as Record unknown>)[name]; + out[name] = (...args: unknown[]) => { + app.view.holdHover(); + return fn(...args); + }; + } + return out as unknown as CommandPort; +} + +/** + * What leaves the phase overview open: the port's questions (asking must not close it), Escape and + * Shift+0 (they close it themselves), and the panels that open over it or beside it (the shortcut + * sheet, the search, the legend, the side panel and its tabs). + */ +const KEEP_OPEN: ReadonlySet = new Set(['walking', 'visibleIssues', 'selectedIssueId', 'dismissTopmost', 'overview', + 'toggleShortcuts', 'focusSearch', 'toggleLegend', 'toggleRail', 'focusRailTabs']); + +/** + * Viewer M3: while the phase overview is open, a key the canvas acts on (r, 0, f, n, a severity + * toggle…) first closes the overview without moving the diagram, then acts on it, as if the reader + * had pressed Escape first. `KEEP_OPEN` lists what does not, and a key the canvas does not answer + * leaves the overview open too. + */ +function closingOverview(app: App, port: CommandPort): CommandPort { + const out = {} as Record; + for (const [name, fn] of Object.entries(port)) { + out[name] = KEEP_OPEN.has(name) + ? fn + : (...args: unknown[]) => { + if (app.view.overviewOpen) { + app.view.closeOverview(false); + renderChrome(app); + } + return (fn as (...a: unknown[]) => unknown)(...args); + }; + } + return out as unknown as CommandPort; } /** Everything the keyboard model is allowed to reach (see ui/commands.ts). */ @@ -41,6 +93,8 @@ function commandPort(app: App): CommandPort { openSelection: (focusEditor) => { const sel = app.selection; if (!sel) return false; + // Viewer M3: in the review walk, Enter opens the walk's current quote of this claim again. + if (app.walk.active && app.walkOwns(sel)) return app.walk.reopen(focusEditor); const loc = app.locOf(sel); if (loc) app.openLocation(loc, focusEditor); return true; @@ -59,6 +113,19 @@ function commandPort(app: App): CommandPort { legendOpen: () => app.legendOpen, closeLegend: () => app.setLegend(false), toggleFlow: () => app.setFlow(!app.flowOn), + closePhasePopover: () => app.view.closePhasePopover(), + closeOverview: () => { + if (!app.view.closeOverview(true)) return false; + renderChrome(app); + return true; + }, + toggleOverview: () => app.toggleOverview(), + walking: () => app.walk.active, + endWalk: () => app.walk.stop(), + review: () => app.walk.toggle(), + walkStep: (delta) => app.walk.step(delta), + walkQuote: (delta) => app.walk.stepQuote(delta), + walkJump: (kind, backwards) => app.walk.jump(kind, backwards), }); } @@ -67,10 +134,24 @@ function filteredIssues(app: App): Issue[] { return app.graph.issues.filter(app.filters.keep); } -/** Arrows move the selection among visible siblings, in spatial order. */ +/** + * Arrows move the selection among visible siblings, in spatial order. Viewer M3: right after a move + * to a phase (the overview, the phase index), the first arrow selects that phase's first step; from + * a connection an arrow selects the end that lies that way, and from a finding it moves as from the + * step marked for it (`CanvasView.nextSelection`). + */ function moveSelection(app: App, key: string): void { - const sel = app.selection; - const next = app.view.nextSelection(sel && sel.kind === 'node' ? sel.id : null, key); + const lane = app.view.takeArrowLane(); + const first = lane && app.index ? app.index.roots(lane)[0] : undefined; + if (first) { + const box = app.view.frame ? app.view.frame.boxes.get(first) : undefined; + app.select({ kind: 'node', id: first }, { center: !!box && !app.view.viewport.isVisible(box) }); + const element = app.view.nodeElement(first); + if (element) element.focus(); + return; + } + // Viewer M3 (live check, W5): from a connection or a finding too, not only from a step. + const next = app.view.nextSelection(app.selection, key); if (!next) return; app.select({ kind: 'node', id: next.id }, { center: !next.visible }); const element = app.view.nodeElement(next.id); diff --git a/webview/src/app/messages.ts b/webview/src/app/messages.ts index 77984683..f7137e70 100644 --- a/webview/src/app/messages.ts +++ b/webview/src/app/messages.ts @@ -35,8 +35,10 @@ export function onHostMessage(app: App, msg: HostToUi): void { stale: (files) => app.setStale(files), actionResult: (result) => app.onActionResult(result), theme: (kind) => app.setTheme(kind), - revealNode: (nodeId, center) => app.focusNode(nodeId, { center, pulse: true }), - revealIssue: (issueId) => app.focusIssue(issueId), + // Viewer M3 (step 14): MLView: Reveal in Diagram. + revealNode: (nodeId, center) => app.revealClaim({ kind: 'node', id: nodeId }, { center }), + revealEdge: (edgeId) => app.revealClaim({ kind: 'edge', id: edgeId }), + revealIssue: (issueId) => app.revealClaim({ kind: 'issue', id: issueId }), restoreState: (state) => applyState(app, state, true), onUnknown: (type) => app.bridge.post({ v: 1, type: 'log', level: 'debug', message: 'ignored unknown message type: ' + type }), diff --git a/webview/src/app/state.ts b/webview/src/app/state.ts index b38639f2..6562c7ca 100644 --- a/webview/src/app/state.ts +++ b/webview/src/app/state.ts @@ -31,7 +31,11 @@ export function applyState(app: App, state: ViewState, rerender: boolean): void // saved with; a state the host posts (`restoreState`) applies at once. const selection = sanitizeSelection(state.selection); if (rerender && selection) app.selection = selection; - if (typeof state.minimapCollapsed === 'boolean') app.view.setMinimapCollapsed(state.minimapCollapsed); + // Viewer M3: the phase index (it replaced the minimap): folded to its pill, or hidden. A state + // saved before M3 with the minimap collapsed opens with the index folded. + if (state.phaseIndex === 'folded' || state.phaseIndex === 'hidden') app.view.setPhaseIndex({ folded: state.phaseIndex === 'folded', hidden: state.phaseIndex === 'hidden' }); + else if (state.phaseIndex === undefined && state.minimapCollapsed === true) app.view.setPhaseIndex({ folded: true }); + else if (state.phaseIndex === undefined && rerender) app.view.setPhaseIndex({ folded: false, hidden: false }); if (typeof state.flow === 'boolean') app.setFlow(state.flow); if (typeof state.legendOpen === 'boolean') app.setLegend(state.legendOpen); // Keys this viewer no longer writes (`railGroupBy`, `answersOpen`, `diffOnly`, @@ -54,8 +58,10 @@ export function snapshotState(app: App): ViewState { collapsed: app.collapsedState.slice(), filters: app.filters.snapshot(), railTab: app.railTab, - minimapCollapsed: app.view.minimapCollapsed, }; + // Viewer M3: absent at its default (shown, unfolded); `minimapCollapsed` is no longer written. + if (app.view.phaseIndexHidden) state.phaseIndex = 'hidden'; + else if (app.view.phaseIndexFolded) state.phaseIndex = 'folded'; if (!app.flowOn) state.flow = false; // Absent at its default, like `flow`: an older host round-trips // a state it has never seen, and a newer one restores to the documented @@ -70,6 +76,9 @@ export function snapshotState(app: App): ViewState { if (composer) state.composer = composer; // Viewer M2 live fix: an open bottom sheet, absent when it is collapsed or the rail is docked. if (app.railMode === 'sheet' && app.railOpen) state.sheetOpen = true; + // Viewer M3: the review walk's place (filter, claim, quote, running), absent before a walk. + const walk = app.walk.viewState(); + if (walk) state.walk = walk; } return state; } diff --git a/webview/src/app/surfaces.ts b/webview/src/app/surfaces.ts index a77f7bee..d6df8582 100644 --- a/webview/src/app/surfaces.ts +++ b/webview/src/app/surfaces.ts @@ -10,6 +10,7 @@ import { emptyCounts, normalizeSeverity } from '../markers.js'; import { freshnessStatus } from '../freshness.js'; +import { isNotObserved } from '../walk.js'; import type { App } from '../app.js'; import type { NotObservedCounts } from '../ui/chrome.js'; import type { IssueCounts, Severity } from '../types.js'; @@ -21,10 +22,10 @@ import type { IssueCounts, Severity } from '../types.js'; export function notObservedCounts(app: App): NotObservedCounts { const out: NotObservedCounts = { steps: 0, connections: 0, findings: 0 }; if (!app.graph) return out; - const exception = (basis: string | undefined) => !!basis && basis !== 'observed'; - for (const node of app.graph.nodes) if (exception(node.basis)) out.steps++; - for (const edge of app.graph.edges) if (exception(edge.basis)) out.connections++; - for (const issue of app.graph.issues) if (exception(issue.basis)) out.findings++; + // Viewer M3: the same predicate as the review walk's Not observed filter, so the two counts agree. + for (const node of app.graph.nodes) if (isNotObserved(node.basis)) out.steps++; + for (const edge of app.graph.edges) if (isNotObserved(edge.basis)) out.connections++; + for (const issue of app.graph.issues) if (isNotObserved(issue.basis)) out.findings++; return out; } @@ -52,14 +53,16 @@ export function renderChrome(app: App): void { hasSelection: !!app.selection, filters: app.filters.value, visibleCounts: visibleCounts(app), - minimapCollapsed: app.view.minimapCollapsed, - minimapUnavailable: app.view.minimapUnavailable(), + phaseIndexShown: !app.view.phaseIndexHidden, + phaseIndexUnavailable: app.view.phaseIndexUnavailable(), + overviewOpen: app.view.overviewOpen, railOpen: app.railOpen, freshness: freshnessStatus(app.workflowDocument, app.freshness), checking: app.freshness.checking, notObserved, exceptionsOn: app.exceptionsOn, railMode: app.railMode, + walking: !!app.walk && app.walk.active, }); } @@ -85,5 +88,6 @@ export function renderRail(app: App): void { staleReason: (file) => app.freshness.reasonOf(file), document: app.graph ? app.workflowDocument : null, columns: app.paneColumns, + walk: app.walk ? app.walk.paneMark() : null, }); } diff --git a/webview/src/app/walk.ts b/webview/src/app/walk.ts new file mode 100644 index 00000000..e96b874a --- /dev/null +++ b/webview/src/app/walk.ts @@ -0,0 +1,661 @@ +/** + * The review walk (viewer M3, roadmap step 11): claim by claim through the displayed revision, + * each claim selected and revealed on the canvas, read in the Selection pane, and its cited lines + * shown and highlighted in the editor beside while the keyboard stays on the diagram. + * + * What it does on each step: + * - the canvas selects the claim and brings it into view above the bottom sheet; + * - the Selection pane shows it (a collapsed sheet opens); + * - about 150 ms after the reader stops moving (`WALK_OPEN_DEBOUNCE_MS`) it asks the host to open + * the claim's current quote beside the panel (`openLocation` with `walk: true`, a `seq` that + * rises with every numbered open of this page, and a request id), focus kept here. The host + * answers once: done, blocked (the reason, nothing opened, no notification), cancelled (a later + * open overtook it) or failed. Only the answer to the latest open is shown. A claim with + * nothing to open sends `walk: clear`, so the editor never keeps an earlier claim's highlight; + * - a live region announces "Claim 3 of 16, not observed: Step …, inferred.". + * + * Owner decisions (M3): each step opens AUTOMATICALLY with preserveFocus; the walk records NO + * verdict and no "checked" mark, only its place, per revision, in the webview's saved view state + * (`ViewState.walk`). Nothing is written to evals or decisions. Opening, filtering and walking + * never call a model. + */ + +import { locationFrame } from './actions.js'; +import { applyWalkMark } from '../ui/selection.js'; +import type { WalkReveal } from '../ui/selection.js'; +import { + changedOffered, + claimBasis, + claimLocs, + claimOrder, + filterClaims, + filterCountText, + filterCounts, + isNotObserved, + offeredFilters, + positionFor, + sameClaim, + walkAnnouncement, + walkLocText, + walkResultAnnouncement, + WALK_FILTERS, +} from '../walk.js'; +import { STALE_TEXT } from '../freshness.js'; +import type { App } from '../app.js'; +import type { GraphIndex } from '../layout/model.js'; +import type { Claim, WalkFilter, WalkOpenStatus } from '../walk.js'; +import type { ActionResult, Loc, OpenBlockReason, Sel, StaleFile, WalkViewState } from '../types.js'; + +/** The pause after the last step before the walk asks the host to open the cited lines. */ +export const WALK_OPEN_DEBOUNCE_MS = 150; + +/** A saved quote index is never trusted past this (a claim cites at most 100 evidence ids). */ +const MAX_QUOTE = 100; + +/** A saved walk, validated field by field (saved state comes from the webview's storage). */ +export function sanitizeWalk(value: unknown): WalkViewState | null { + if (!value || typeof value !== 'object') return null; + const record = value as Record; + const filter = typeof record.filter === 'string' && (WALK_FILTERS as readonly string[]).indexOf(record.filter) >= 0 ? (record.filter as WalkFilter) : null; + const claim = record.claim as { kind?: unknown; id?: unknown } | null | undefined; + if (!filter || !claim || typeof claim !== 'object') return null; + if ((claim.kind !== 'node' && claim.kind !== 'edge' && claim.kind !== 'issue') || typeof claim.id !== 'string' || !claim.id) return null; + const out: WalkViewState = { filter, claim: { kind: claim.kind, id: claim.id } }; + const quote = record.quote; + if (typeof quote === 'number' && Number.isInteger(quote) && quote > 0 && quote < MAX_QUOTE) out.quote = quote; + if (record.active === true) out.active = true; + return out; +} + +/** + * A block the stale list explains: the host's reason is one the stale list carries, or it gave + * none (the file was on the list). + */ +function staleBlock(reason: OpenBlockReason | undefined): boolean { + return !reason || reason === 'changed' || reason === 'missing' || reason === 'unreadable' || reason === 'too-large' || reason === 'elsewhere'; +} + +export class ReviewWalk { + /** The walk is running: the bar is shown and the walk keys answer. */ + active = false; + filter: WalkFilter = 'notObserved'; + /** The claims of `filter`, in the drawn order, and the walk's place among them. */ + list: Claim[] = []; + position = -1; + /** The current claim's quote the editor is asked for. */ + quote = 0; + status: WalkOpenStatus = { state: 'none', quote: 0, quotes: 0 }; + + private app: App; + private order: Claim[] = []; + private orderIndex: GraphIndex | null = null; + /** Every numbered open of this page; the host drops one that is not above the last it saw. */ + private seq = 0; + /** + * The `seq` of the walk's latest open: only its answer is shown. Reset to 0 whenever the walk + * moves (another claim or quote) before it asks again, so a late answer for the claim it left is + * never shown as the current claim's (M3 review, A11Y-M3-4). + */ + private latestSeq = 0; + /** Whether that open asked VS Code to move the focus to the editor (Alt+Enter). */ + private latestFocus = false; + /** + * Why the latest open was not opened: the host's reason, or the stale reason the viewer already + * knew when it asked. `onStale` reads it to tell a block that a fixed file ends from one it does + * not (unsaved edits that lost the lines, a missing cell). + */ + private blockedReason: OpenBlockReason | undefined; + private timer: ReturnType | null = null; + /** The place the walk resumes from, for `revision` (kept after the walk ends). */ + private remembered: { revision: string; state: WalkViewState } | null = null; + /** A remount's saved place, applied by the first document of the revision it was saved for. */ + private restored: { revision: string; state: WalkViewState } | null = null; + /** While the walk itself selects, the App's selection hook must not treat it as the reader's move. */ + selecting = false; + + constructor(app: App) { + this.app = app; + } + + /* ── the claims ────────────────────────────────────────────────────── */ + + private index(): GraphIndex | null { + return this.app.index; + } + + /** The drawn order for the current document, computed once per index. */ + claims(): Claim[] { + const index = this.index(); + if (!index) return []; + if (index !== this.orderIndex) { + this.order = claimOrder(index); + this.orderIndex = index; + } + return this.order; + } + + private isStale = (file: string): boolean => !!this.app.freshness.reasonOf(file); + + changedOffered(): boolean { + return changedOffered(this.app.freshness.list().map((file) => file.reason)); + } + + counts(): Record { + const index = this.index(); + if (!index) return { notObserved: 0, findings: 0, changed: 0, all: 0 }; + return filterCounts(this.claims(), index, this.isStale); + } + + offered(): WalkFilter[] { + return offeredFilters(this.counts(), this.changedOffered()); + } + + private listFor(filter: WalkFilter): Claim[] { + const index = this.index(); + return index ? filterClaims(this.claims(), filter, index, this.isStale) : []; + } + + /** The claim the walk is on, or null. */ + current(): Claim | null { + return this.position >= 0 && this.position < this.list.length ? this.list[this.position] : null; + } + + private currentLocs(): Loc[] { + const index = this.index(); + const claim = this.current(); + return index && claim ? claimLocs(index, claim) : []; + } + + /* ── starting, stepping, ending ────────────────────────────────────── */ + + /** + * Start the walk, or carry on with it (`r`, Review, the ⋯ menu; `u` asks for Not observed and the + * stale notice for Changed files). Without a filter it resumes the place remembered for this + * revision, else starts on Not observed (All when no claim is marked inferred or unresolved). + * A filter asked for resumes the remembered place only when that place was on the same filter; + * any other filter starts at its first claim, so "Review affected claims" after a walk elsewhere + * never skips the claims before the old place (M3 review, F2). A filter asked for that holds no + * claim is said, and nothing starts. True when it runs. + */ + start(filter?: WalkFilter): boolean { + const index = this.index(); + if (!index) return false; + const offered = this.offered(); + const saved = this.remembered && this.remembered.revision === this.app.workflowRevision ? this.remembered.state : null; + let next: WalkFilter; + if (filter) { + if (offered.indexOf(filter) < 0) { + this.app.announce(filter === 'notObserved' + ? 'No claim is marked inferred or unresolved in this revision.' + : filter === 'changed' ? 'No claim cites a file that changed or went missing.' : 'No claims to walk with that filter.'); + return false; + } + next = filter; + } else { + next = saved && offered.indexOf(saved.filter) >= 0 ? saved.filter : offered.indexOf('notObserved') >= 0 ? 'notObserved' : 'all'; + } + const list = this.listFor(next); + if (!list.length) { + this.app.announce('This revision has no claims to walk.'); + return false; + } + const wasActive = this.active; + // The walk reads each claim in the Selection pane: a docked rail the reader hid is shown again. + if (!wasActive) this.app.openRailForWalk(); + this.filter = next; + this.list = list; + const resume = saved && saved.filter === next ? saved : null; + this.position = Math.max(0, positionFor(list, resume ? resume.claim : null, this.claims())); + this.quote = resume && sameClaim(resume.claim, this.current()) ? Math.min(resume.quote || 0, Math.max(0, this.currentLocs().length - 1)) : 0; + this.active = true; + if (!wasActive) this.app.onWalkShown(true); + this.show('open'); + return true; + } + + /** End the walk (Escape, Exit, Review again). Its place is kept for this revision. */ + stop(announce = true): boolean { + if (!this.active) return false; + this.active = false; + this.cancelTimer(); + this.app.bridge.post({ v: 1, type: 'walk', state: 'end' }); + this.remember(); + this.app.onWalkShown(false); + if (announce) this.app.announce('Review ended. r resumes it here.'); + this.app.saveSoon(); + return true; + } + + toggle(): void { + if (this.active) this.stop(); + else this.start(); + } + + /** + * j / k, ↓ / ↑ and the bar's Next and Previous (M3 review, A11Y-M3-2): the next or previous + * claim. At either end the walk stays and says so. + */ + step(delta: number): boolean { + if (!this.active || !this.list.length) return false; + const next = Math.max(0, Math.min(this.list.length - 1, this.position + delta)); + if (next === this.position) { + this.app.announce(delta > 0 + ? 'Claim ' + this.list.length + ' of ' + this.list.length + ' is the last one.' + : 'Claim 1 of ' + this.list.length + ' is the first one.'); + return true; + } + this.position = next; + this.quote = 0; + this.show('open'); + return true; + } + + /** [ and ]: the current claim's previous or next quote, opened beside the panel. */ + stepQuote(delta: number): boolean { + if (!this.active) return false; + const locs = this.currentLocs(); + if (locs.length < 2) { + this.app.announce(locs.length ? 'This claim has one quote.' : 'This claim cites no lines.'); + return true; + } + const next = Math.max(0, Math.min(locs.length - 1, this.quote + delta)); + if (next === this.quote) { + this.app.announce(delta > 0 ? 'Quote ' + locs.length + ' of ' + locs.length + ' is the last one.' : 'Quote 1 of ' + locs.length + ' is the first one.'); + return true; + } + this.quote = next; + this.app.announce('Quote ' + (next + 1) + ' of ' + locs.length + ': ' + walkLocText(locs[next]) + '.'); + this.scheduleOpen(); + this.remember(); + // The pane brings the marked quote into view (live: a second quote sat below the fold). + this.render('quote'); + this.app.saveSoon(); + return true; + } + + /** + * `u` / `U` (not observed) and, while walking, `n` / `p` (findings): the next or previous claim of + * that kind after the current one in the drawn order, wrapping as `n` and `p` always have. The + * filter stays when it holds that claim; otherwise it becomes Not observed or Findings. `u` + * starts the walk when it is not running. + */ + jump(kind: 'notObserved' | 'findings', backwards: boolean): boolean { + const index = this.index(); + if (!index) return false; + const filter: WalkFilter = kind; + if (!this.active) return this.start(filter); + const order = this.claims(); + const matches = (claim: Claim): boolean => (kind === 'findings' ? claim.kind === 'issue' : isNotObserved(claimBasis(index, claim))); + const here = order.findIndex((claim) => sameClaim(claim, this.current())); + let target: Claim | null = null; + for (let step = 1; step <= order.length; step++) { + const at = ((here < 0 ? (backwards ? 0 : -1) : here) + (backwards ? -step : step) + order.length * 2) % order.length; + if (matches(order[at])) { + target = order[at]; + break; + } + } + if (!target) { + this.app.announce(kind === 'findings' ? 'This revision has no findings.' : 'No claim is marked inferred or unresolved in this revision.'); + return true; + } + if (!this.list.some((claim) => sameClaim(claim, target))) { + this.filter = filter; + this.list = this.listFor(filter); + } + this.position = Math.max(0, this.list.findIndex((claim) => sameClaim(claim, target))); + this.quote = 0; + this.show('open'); + return true; + } + + /** + * A filter chosen in the bar. The walk keeps its claim when the new filter holds it, and then + * also keeps what the bar says about the editor (M3 review, F4); another claim is opened. + */ + setFilter(filter: WalkFilter): void { + if (!this.active || this.offered().indexOf(filter) < 0) return; + const before = this.current(); + this.filter = filter; + this.list = this.listFor(filter); + this.position = Math.max(0, positionFor(this.list, before, this.claims())); + const same = sameClaim(before, this.current()); + if (!same) this.quote = 0; + this.show(same ? 'keep' : 'open'); + // "Not observed, 5 claims. Claim 3 of 5: …" or "All 14 claims. Claim 7 of 14: …". + this.app.announce(filterCountText(filter, this.list.length) + '. ' + this.announcement()); + } + + /** Enter (and an Open link of the current claim): open the current quote again, now. */ + reopen(focusEditor: boolean): boolean { + if (!this.active || !this.current()) return false; + this.cancelTimer(); + this.sendOpen(focusEditor); + return true; + } + + /** + * An Open link in the Selection pane while the walk shows that claim: the walk opens it as its + * own quote (no notification when it is blocked; the reason goes to the bar and the pane). + */ + openQuote(loc: Loc, focusEditor: boolean): boolean { + if (!this.active || !loc.evidenceId) return false; + const at = this.currentLocs().findIndex((item) => item.evidenceId === loc.evidenceId); + if (at < 0) return false; + this.quote = at; + this.cancelTimer(); + this.sendOpen(focusEditor); + this.remember(); + return true; + } + + /** + * The reader selected something else (a click, an arrow, a search hit, a link) while walking. + * When the walk's list holds it, the walk follows: its place moves there, nothing is opened (a + * click selects only; Enter opens), and any highlight of the claim it left is cleared. + */ + followSelection(sel: Sel | null): void { + if (!this.active || this.selecting || !sel) return; + const at = this.list.findIndex((claim) => sameClaim(claim, sel)); + if (at < 0 || at === this.position) return; + this.position = at; + this.quote = 0; + this.cancelTimer(); + this.latestSeq = 0; + this.app.bridge.post({ v: 1, type: 'walk', state: 'clear' }); + this.status = this.unansweredStatus('idle'); + this.remember(); + // The pane, which the selection started at its top, brings the walk's quote into view when it + // fits with the claim's title. + this.render('claim'); + } + + /* ── host answers and document changes ─────────────────────────────── */ + + /** A new document (`setWorkflow`). Another revision ends the walk and starts fresh. */ + onDocument(revisionChanged: boolean): void { + const revision = this.app.workflowRevision; + if (revisionChanged) { + if (this.active) this.stop(false); + this.remembered = null; + this.list = []; + this.position = -1; + } + // A remount brings the saved place back for the revision it was saved with. + const restored = this.restored; + this.restored = null; + if (restored && restored.revision === revision && !this.remembered) { + this.remembered = { revision, state: restored.state }; + if (restored.state.active) this.resume(); + return; + } + if (!this.active) return; + // The same revision again (a refresh): the claims are the same; keep the place. + const before = this.current(); + this.list = this.listFor(this.filter); + this.position = Math.max(0, positionFor(this.list, before, this.claims())); + this.render(); + } + + /** + * A remounted page brings back a walk that was running: the bar and the selection, without + * asking the host to open anything (it still shows the last open); Enter opens it again. + */ + private resume(): void { + const saved = this.remembered ? this.remembered.state : null; + if (!saved) return; + const offered = this.offered(); + this.filter = offered.indexOf(saved.filter) >= 0 ? saved.filter : 'all'; + this.list = this.listFor(this.filter); + if (!this.list.length) return; + this.position = Math.max(0, positionFor(this.list, saved.claim, this.claims())); + this.quote = sameClaim(saved.claim, this.current()) ? Math.min(saved.quote || 0, Math.max(0, this.currentLocs().length - 1)) : 0; + this.active = true; + this.app.onWalkShown(true); + this.show('rest'); + } + + /** + * The host's stale files changed: the Changed files filter follows, and the walk keeps its claim + * while the list holds it. When the claim left the list (its file is unchanged again), the walk + * is on the next claim of the list: that claim is selected, shown and announced, so the bar + * never names a claim that is not on screen and `j` never skips it (M3 review, F3). It is not + * opened by itself (the reader may be editing beside the panel); Enter opens it, and the + * highlight of the claim it left is cleared. A claim not asked for yet is said again, so a quote + * whose file just went stale says why it will not open (F4). + * + * A quote the host already answered "not opened" for is said again when the stale list changes + * for its file (M3 live check, W2): restored, the bar and the pane said "… changed after revision + * … was published; not opened." with the warning mark until Enter. Now they say "Enter shows …" + * (nothing is opened by itself); a file that went from changed to missing says so. A block for + * another reason (unsaved edits that lost the cited lines, a cell that no longer exists) stays + * until the next open. `previous` is the host's stale list before this change. + */ + onStale(previous: readonly StaleFile[] = []): void { + if (!this.active) return; + if (this.offered().indexOf(this.filter) < 0) this.filter = 'all'; + const before = this.current(); + this.list = this.listFor(this.filter); + this.position = Math.max(0, positionFor(this.list, before, this.claims())); + if (!sameClaim(before, this.current())) { + this.quote = 0; + this.cancelTimer(); + this.latestSeq = 0; + this.app.bridge.post({ v: 1, type: 'walk', state: 'clear' }); + this.show('rest'); + return; + } + if (this.timer !== null) this.status = this.unansweredStatus('opening'); + else if (this.latestSeq === 0 && (this.status.state === 'idle' || this.status.state === 'blocked')) this.status = this.unansweredStatus('idle'); + else if (this.status.state === 'blocked' && this.status.loc) { + const file = this.status.loc.file; + const was = previous.find((item) => item.path === file); + const now = this.app.freshness.reasonOf(file); + // The host's answer is about the file as it was; a later answer to that open is dropped. + if ((was ? was.reason : undefined) !== now && (now || staleBlock(this.blockedReason))) { + this.latestSeq = 0; + this.status = this.unansweredStatus('idle'); + } + } + this.render(); + } + + private onResult(result: ActionResult, seq: number): void { + if (!this.active || seq !== this.latestSeq || result.outcome === 'cancelled') return; + if (result.outcome === 'done') this.status = this.statusFor('done', undefined, this.latestFocus); + else { + this.blockedReason = result.outcome === 'blocked' ? result.reason : undefined; + const message = result.message || (result.outcome === 'blocked' ? 'Not opened.' : 'VS Code could not show the file.'); + this.status = this.statusFor(result.outcome, message); + this.app.announce(walkResultAnnouncement(result.outcome, message)); + } + this.render(); + } + + /* ── opening ───────────────────────────────────────────────────────── */ + + private cancelTimer(): void { + if (this.timer !== null) clearTimeout(this.timer); + this.timer = null; + } + + private statusFor(state: WalkOpenStatus['state'], message?: string, focusEditor = false): WalkOpenStatus { + const locs = this.currentLocs(); + const quote = Math.min(this.quote, Math.max(0, locs.length - 1)); + const out: WalkOpenStatus = { state, quote, quotes: locs.length }; + if (locs[quote] && state !== 'none' && state !== 'unavailable') out.loc = locs[quote]; + if (message) out.message = message; + if (focusEditor && (state === 'opening' || state === 'done')) out.focusEditor = true; + return out; + } + + /** + * What the bar says before the host has answered for the current quote: nothing to open, no way + * to open, the viewer's own reason for a file the host already reported stale, or `state`: + * `opening` while an open waits for the pause after a step, `idle` ("Enter shows …") for a claim + * the walk is on but will not open by itself (a remount, a click, a claim it moved to). Never + * "Enter shows" for a quote that Enter would not open (M3 review, F4). + */ + private unansweredStatus(state: 'opening' | 'idle'): WalkOpenStatus { + const locs = this.currentLocs(); + const loc = locs[Math.min(this.quote, locs.length - 1)]; + if (!locs.length || !loc) return this.statusFor('none'); + if (!this.app.caps.canOpenSource) return this.statusFor('unavailable'); + const local = this.localBlock(loc); + return local ? this.statusFor('blocked', local) : this.statusFor(state); + } + + /** + * The viewer's own reason for a quote whose file the host already reported stale, shown at once; + * the host's answer (it never opens such a file) replaces it when it comes. + */ + private localBlock(loc: Loc): string | null { + const reason = loc.evidenceId ? this.app.freshness.reasonOf(loc.file) : undefined; + if (!reason) return null; + return loc.file + ': ' + STALE_TEXT[reason] + '; not opened.'; + } + + /** After the pause, ask the host for the current quote (or, with none, to clear the highlight). */ + private scheduleOpen(): void { + this.cancelTimer(); + // The walk moved: an answer to an earlier open is no longer about what the bar shows. + this.latestSeq = 0; + this.status = this.unansweredStatus('opening'); + if (this.status.state === 'unavailable') return; + this.timer = setTimeout(() => { + this.timer = null; + this.sendOpen(false); + }, WALK_OPEN_DEBOUNCE_MS); + } + + /** Waiting to ask the host (tests flush it instead of sleeping). */ + get pendingOpen(): boolean { + return this.timer !== null; + } + + /** Send the pending open now (Enter does; a test may). */ + flushOpen(): void { + if (this.timer === null) return; + this.cancelTimer(); + this.sendOpen(false); + } + + private sendOpen(focusEditor: boolean): void { + if (!this.active) return; + const locs = this.currentLocs(); + const loc = locs[Math.min(this.quote, locs.length - 1)]; + if (!loc) { + this.app.bridge.post({ v: 1, type: 'walk', state: 'clear' }); + this.status = this.statusFor('none'); + this.render(); + return; + } + if (!this.app.caps.canOpenSource) { + this.status = this.statusFor('unavailable'); + this.render(); + return; + } + const seq = ++this.seq; + this.latestSeq = seq; + this.latestFocus = focusEditor; + // VS Code may tear down a hidden webview when the active editor changes: save first, as every + // open does (app/actions.ts). + this.remember(); + this.app.bridge.saveState(this.app.getState()); + const frame = locationFrame(loc); + frame.walk = true; + frame.seq = seq; + if (focusEditor) frame.focus = true; + this.app.postRequest(frame, (result) => this.onResult(result, seq)); + const local = this.localBlock(loc); + this.blockedReason = local ? this.app.freshness.reasonOf(loc.file) : undefined; + this.status = local ? this.statusFor('blocked', local) : this.statusFor('opening', undefined, focusEditor); + this.render(); + } + + /* ── showing ───────────────────────────────────────────────────────── */ + + /** + * Select and reveal the current claim and announce it. `open`: schedule its open (a step). + * `rest`: not asked yet (`unansweredStatus('idle')`). `keep`: the same claim as before, so the bar keeps + * what it says about the editor. + */ + private show(mode: 'open' | 'rest' | 'keep'): void { + const claim = this.current(); + if (!claim) return; + this.selecting = true; + try { + this.app.showWalkClaim(claim); + } finally { + this.selecting = false; + } + if (mode === 'open') this.scheduleOpen(); + else if (mode === 'rest') { + this.latestSeq = 0; + this.status = this.unansweredStatus('idle'); + } + this.remember(); + // A move to a claim starts the pane at its top (M3 live check, W1) and brings its quote into + // view when it fits with the title. `keep` (a filter on the same claim) leaves the reader's place. + this.render(mode === 'keep' ? false : 'claim'); + this.app.announce(this.announcement()); + this.app.saveSoon(); + } + + private announcement(): string { + const index = this.index(); + const claim = this.current(); + return index && claim ? walkAnnouncement(this.position, this.list.length, this.filter, index, claim) : ''; + } + + /** + * Repaint the bar, the header's Review state and the pane's quote mark. `reveal`: scroll the pane + * the least distance that shows the quote, at once (`walkRevealTop`); after a step to another + * claim (`'claim'`) the title stays in view and wins when both do not fit, after `[` or `]` + * (`'quote'`) the quote wins. + */ + render(reveal: WalkReveal | false = false): void { + this.app.renderWalkBar(); + applyWalkMark(this.app.rail.root, this.paneMark(), reveal); + } + + /** The Selection pane's mark for the walk's quote, while the pane shows the walk's claim. */ + paneMark(): { quote: number; status: WalkOpenStatus } | null { + if (!this.active) return null; + const claim = this.current(); + if (!claim || !sameClaim(claim, this.app.selection)) return null; + return { quote: this.status.quote, status: this.status }; + } + + /* ── saved place ───────────────────────────────────────────────────── */ + + private remember(): void { + const revision = this.app.workflowRevision; + const claim = this.current(); + if (!revision || !claim) return; + const state: WalkViewState = { filter: this.filter, claim: { kind: claim.kind, id: claim.id } }; + if (this.quote > 0) state.quote = this.quote; + if (this.active) state.active = true; + this.remembered = { revision, state }; + } + + /** `ViewState.walk`: the place for the displayed revision, or undefined before the first walk. */ + viewState(): WalkViewState | undefined { + const revision = this.app.workflowRevision; + if (!this.remembered || !revision || this.remembered.revision !== revision) return undefined; + const state: WalkViewState = { ...this.remembered.state, claim: { ...this.remembered.state.claim } }; + if (this.active) state.active = true; + else delete state.active; + return state; + } + + /** A remount's saved walk, for the revision it was saved with (applied by `onDocument`). */ + restore(revision: string, value: unknown): void { + const state = sanitizeWalk(value); + this.restored = state ? { revision, state } : null; + } + + destroy(): void { + this.cancelTimer(); + } +} diff --git a/webview/src/canvas/emphasis.ts b/webview/src/canvas/emphasis.ts index ca9b598a..20073f7f 100644 --- a/webview/src/canvas/emphasis.ts +++ b/webview/src/canvas/emphasis.ts @@ -53,6 +53,12 @@ export interface EmphasisContext { keep(issue: Issue): boolean; announce(text: string): void; toast(text: string): void; + /** + * Viewer M3 (live check, W3): true while a keyboard move holds the hover back, until the pointer + * really moves (`CanvasView.holdHover`). A card that only passed under a resting pointer while + * the diagram panned gets no hover card. + */ + hoverHeld?(): boolean; } export class Emphasis { @@ -208,6 +214,7 @@ export class Emphasis { * the diagram costs nothing until the pointer actually settles. */ hoverIntent(id: string | null): void { + if (id && this.ctx.hoverHeld && this.ctx.hoverHeld()) return; if (this.hoverTimer !== null) { // Already on the way to this target: keep the timer that is running. if (this.pendingHover === id) return; @@ -232,6 +239,19 @@ export class Emphasis { }, delay); } + /** + * Viewer M3 (live check, W3): drop the hover now, without the close delay: the pending open, the + * card's trace and its hover card. Nothing happens when nothing is hovered or on the way. + */ + dropHover(): void { + if (this.hoverTimer !== null) { + clearTimeout(this.hoverTimer); + this.hoverTimer = null; + this.pendingHover = null; + } + if (this.hoverId) this.setHover(null); + } + setHover(id: string | null): void { if (this.hoverId === id) return; this.hoverId = id; diff --git a/webview/src/canvas/host.ts b/webview/src/canvas/host.ts index 1bfcaef3..7cf553a3 100644 --- a/webview/src/canvas/host.ts +++ b/webview/src/canvas/host.ts @@ -25,21 +25,18 @@ export const DOUBLE_CLICK_MS = 220; export const HOVER_OPEN_MS = 400; export const HOVER_CLOSE_MS = 120; -/** The graph size at which an overview earns the corner it occupies (UX_DESIGN §1). */ -export const MINIMAP_MIN_NODES = 30; - /** - * Below this canvas height the minimap is not drawn (Campaign 3, issue 6): at - * 270-310 px its 200x132 panel covered the lower right of the diagram. + * Viewer M3: the phase index is a panel of rows when the panel is this wide or wider, and a pill + * below it (the bottom sheet's layout, beside the code). It replaced the minimap and its rules + * (30 or more cards, a panel wider than 900 px). */ -export const MINIMAP_MIN_CANVAS_H = 350; +export const PHASE_INDEX_LIST_MIN_W = 1000; /** - * A panel this wide or narrower has no room for the minimap beside the diagram (it was an - * `@media (max-width: 900px)` rule). Viewer M2 live fix: the App applies it from the panel's - * width (`CanvasView.setPanelWidth`), so the ... menu can say why the map is not shown. + * Below this canvas height the phase index is its pill whatever the width (Campaign 3, issue 6: + * a panel over a 270-310 px canvas covered a third of the diagram it was meant to summarise). */ -export const MINIMAP_NARROW_W = 900; +export const PHASE_INDEX_LIST_MIN_H = 350; export interface CanvasHost { /** The App's issue filter — a marker is drawn only for issues this keeps. */ @@ -64,8 +61,8 @@ export interface CanvasHost { /** After a collapse/expand: the App re-applies selection, rail and state. */ afterCollapse(): void; onViewportChange(vp: Viewport): void; - /** The minimap was collapsed or expanded; the App persists the flag. */ - onMinimapCollapsed(collapsed: boolean): void; + /** Viewer M3: the phase index was folded or unfolded; the App saves it and repaints the menu. */ + onPhaseIndexChanged(): void; onKeyDown(ev: KeyboardEvent): void; onBackgroundClick(): void; /** The node or connection the current selection points at, kept in view across a resize. */ diff --git a/webview/src/canvasview.ts b/webview/src/canvasview.ts index 39f38b58..964b95bf 100644 --- a/webview/src/canvasview.ts +++ b/webview/src/canvasview.ts @@ -2,7 +2,8 @@ * The canvas view — everything inside the diagram surface. * * It owns the layout frame, the routed edges, the scene DOM, the viewport, the - * minimap, the tooltip and the transient states (hover, focus mode, collapse). + * phase index and the phase overview (viewer M3), the tooltip and the transient states (hover, + * focus mode, collapse). * The App owns the chrome, the rail, the filters and the host protocol, and * drives this class through the `CanvasHost` callbacks. * @@ -17,17 +18,23 @@ * `canvas/emphasis.ts` selection, hover, the lineage trace and focus mode */ +import { on } from './dom.js'; import { GraphIndex } from './layout/model.js'; import { layoutGraph, LayoutFrame } from './layout/layout.js'; +import type { LayoutBox } from './layout/layout.js'; import { routeEdges, RoutedEdge } from './layout/routing.js'; import type { Point } from './layout/routing.js'; import { planLabels, LabelPlan } from './layout/labels.js'; -import { firstBox, nextBox } from './layout/navigate.js'; -import { minimapDots, renderScene } from './render/scene.js'; +import { connectionEnd, firstBox, nextBox } from './layout/navigate.js'; +import { renderScene } from './render/scene.js'; import { planScene, ScenePlan, ScenePlanOptions } from './render/plan.js'; import { markerPoint, nextMountSerial } from './render/edges.js'; import { BundleBinding } from './render/bundles.js'; -import { FRAME_MIN_ZOOM, LOD_FULL_ZOOM, Minimap, READABLE_ZOOM, ViewportController } from './render/canvas.js'; +import { FRAME_MIN_ZOOM, LOD_FULL_ZOOM, READABLE_ZOOM, ViewportController, phasePlan } from './render/canvas.js'; +import { PhaseIndex } from './render/phaseindex.js'; +import type { PhaseRow } from './render/phaseindex.js'; +import { overviewInput } from './render/phaseoverview.js'; +import { PhaseOverview } from './ui/overview.js'; import { FlowBinding } from './render/flowbinding.js'; import { EdgeHover } from './render/edgehover.js'; import { Tooltip } from './render/tooltip.js'; @@ -35,7 +42,7 @@ import { Toasts, buildEmptyState, buildFilterEmptyState } from './ui/states.js'; import { wireCanvasGestures } from './ui/shell.js'; import { Emphasis } from './canvas/emphasis.js'; import { wireEdgeEvents, wireNodeEvents } from './canvas/wiring.js'; -import { HOVER_CLOSE_MS, HOVER_OPEN_MS, MINIMAP_MIN_CANVAS_H, MINIMAP_MIN_NODES, MINIMAP_NARROW_W } from './canvas/host.js'; +import { HOVER_CLOSE_MS, HOVER_OPEN_MS, PHASE_INDEX_LIST_MIN_H, PHASE_INDEX_LIST_MIN_W } from './canvas/host.js'; import type { CanvasHost, NextSelection } from './canvas/host.js'; import type { Shell } from './ui/shell.js'; import type { Sel, StaleFile, StaleReason } from './types.js'; @@ -58,7 +65,25 @@ export class CanvasView { private zoomLevelEl: HTMLElement; private stateHost: HTMLElement; private tooltip: Tooltip; - private minimap: Minimap; + /** Viewer M3: the labelled phase index (it replaced the minimap) and the phase overview. */ + private phaseIndex: PhaseIndex; + private overview: PhaseOverview; + /** Where the keyboard was when the overview opened, so Escape gives it back. */ + private overviewReturn: HTMLElement | null = null; + /** The panel's width as the App last measured it (0 when it cannot be measured). */ + private panelWidth = 0; + private indexFolded = false; + private indexHidden = false; + /** Viewer M3: the phase a move to a phase landed on, where the next arrow key starts. */ + private arrowLane: string | null = null; + /** + * Viewer M3 (live check, W3): a keyboard move or a walk step holds the hover back until the + * pointer really moves (`holdHover`). `pointerAt` is the pointer's last place over the canvas; + * `heldAt` the place it rested at when the hold began, null until it is known. + */ + private hoverHeld = false; + private pointerAt: { x: number; y: number } | null = null; + private heldAt: { x: number; y: number } | null = null; private host: CanvasHost; private index: GraphIndex | null = null; @@ -109,20 +134,28 @@ export class CanvasView { ); this.canvasEl.appendChild(this.tooltip.root); - this.minimap = new Minimap( - (x, y) => { - const size = this.viewport.size(); - this.viewport.set({ x: size.w / 2 - x * this.viewport.vp.zoom, y: size.h / 2 - y * this.viewport.vp.zoom }); + // Viewer M3: the phase index, inside the canvas (so in the bottom sheet's layout it stays above + // the sheet) and outside the world layer. Its rows are not Tab stops; the phase overview is + // the keyboard's way to the same moves. The viewport treats what it covers as out of view. + this.phaseIndex = new PhaseIndex({ + go: (id) => this.goToPhase(id, { focusCanvas: true }), + fold: (folded) => { + this.indexFolded = folded; + this.syncPhaseIndex(); + this.host.onPhaseIndexChanged(); }, - (collapsed) => this.host.onMinimapCollapsed(collapsed), - ); - // VIEW-12: BEFORE the canvas, and outside it. The minimap duplicates a - // diagram that is already fully navigable from the keyboard, so it is - // `aria-hidden` — and an `aria-hidden` subtree may not contain a tab stop, - // which is why its in-panel chevron is pointer-only and the keyboard's - // toggle lives in the toolbar. It is absolutely positioned either way, so - // it is drawn exactly where it always was. - shell.main.insertBefore(this.minimap.root, this.canvasEl); + }); + this.canvasEl.appendChild(this.phaseIndex.root); + // The phase overview (Shift+0): an overlay over the whole canvas, drawn from its own geometry. + this.overview = new PhaseOverview({ + go: (id, byKeyboard) => { + // Enter on a block pans the diagram under a pointer that may rest over it. + if (byKeyboard) this.holdHover(); + this.goToPhase(id, { focusCanvas: true }); + }, + back: () => this.closeOverview(true), + }); + this.canvasEl.appendChild(this.overview.root); this.toasts = new Toasts(); this.canvasEl.appendChild(this.toasts.root); @@ -130,10 +163,11 @@ export class CanvasView { this.viewport = new ViewportController(this.canvasEl, this.worldEl, (vp) => { this.zoomLevelEl.textContent = Math.round(vp.zoom * 100) + '%'; const size = this.viewport.size(); - this.minimap.setViewport(vp, size.w, size.h); + this.phaseIndex.setInView(vp, size.w, size.h); this.lastArea = this.viewport.visibleArea(); this.host.onViewportChange(vp); }); + this.viewport.setCovered(() => this.indexCovered()); for (const dispose of wireCanvasGestures(this.canvasEl, this.viewport, { onKeyDown: (ev) => this.host.onKeyDown(ev), @@ -179,6 +213,7 @@ export class CanvasView { keep: (issue) => this.host.keep(issue), announce: (text) => this.host.announce(text), toast: (text) => this.toasts.show(text), + hoverHeld: () => this.hoverHeld, }); // Which cable the pointer owns is decided by geometry, not by document @@ -204,10 +239,64 @@ export class CanvasView { changed: () => this.syncBundles(), openDelayMs: HOVER_OPEN_MS, closeDelayMs: HOVER_CLOSE_MS, + held: () => this.hoverHeld, }); + // Before the cable resolver's own pointermove, so a real move ends a hold before it resolves. + this.disposers.push(on(this.canvasEl, 'pointermove', (ev: PointerEvent) => this.trackPointer(ev))); + this.disposers.push(on(this.canvasEl, 'pointerdown', () => this.releaseHover())); this.disposers.push(this.edgeHover.wire()); } + /* ── the hover hold (viewer M3, live check W3) ────────────────────────── */ + + /** + * A keyboard move or a walk step is about to pan the diagram, or just did: the hover card goes, + * with a card's trace and a cable's highlight, and no card or cable that passes under a resting + * pointer takes the hover until the pointer moves again. Measured live (vit-cc, VS Code 1.139): + * the walk and the arrow keys panned cards under the resting pointer, whose hover cards then + * covered part of the diagram. A click also ends the hold. + */ + holdHover(): void { + this.hoverHeld = true; + this.heldAt = this.pointerAt; + this.emphasis.dropHover(); + this.edgeHover.drop(); + this.tooltip.hide(); + } + + /** True while the hover is held back (tests read it). */ + get hoverIsHeld(): boolean { + return this.hoverHeld; + } + + private releaseHover(): void { + this.hoverHeld = false; + this.heldAt = null; + } + + /** + * Every pointer move over the canvas. While the hover is held, a move to the place the pointer + * rested at is not the reader's (the browser sends one when the picture moves under it); the + * first move somewhere else ends the hold, and the card under the pointer gets its hover, whose + * own pointerenter came during the hold. + */ + private trackPointer(ev: PointerEvent): void { + const at = { x: ev.clientX, y: ev.clientY }; + this.pointerAt = at; + if (!this.hoverHeld) return; + if (!this.heldAt) { + this.heldAt = at; + return; + } + if (at.x === this.heldAt.x && at.y === this.heldAt.y) return; + this.releaseHover(); + const target = ev.target as Element | null; + const hit = target && typeof target.closest === 'function' ? target.closest('.mlv-node[data-node-id], .mlv-group__header') : null; + const card = hit && hit.classList.contains('mlv-group__header') ? hit.closest('[data-node-id]') : hit; + const id = card ? card.getAttribute('data-node-id') : null; + if (id && this.canvasEl.contains(card)) this.emphasis.hoverIntent(id); + } + /* ── data ──────────────────────────────────────────────────────────── */ get frame(): LayoutFrame | null { @@ -226,35 +315,186 @@ export class CanvasView { this.collapsedSet = new Set(ids); } - get minimapCollapsed(): boolean { - return this.minimap.collapsed; + /* ── the phase index (viewer M3; it replaced the minimap) ─────────────── */ + + /** The reader folded the wide phase index to its pill (saved per viewer). */ + get phaseIndexFolded(): boolean { + return this.indexFolded; } - setMinimapCollapsed(collapsed: boolean): void { - this.minimap.setCollapsed(collapsed); + /** The reader hid the phase index (the ... menu; saved per viewer). */ + get phaseIndexHidden(): boolean { + return this.indexHidden; + } + + setPhaseIndex(state: { folded?: boolean; hidden?: boolean }): void { + if (typeof state.folded === 'boolean') this.indexFolded = state.folded; + if (typeof state.hidden === 'boolean') this.indexHidden = state.hidden; + this.syncPhaseIndex(); + } + + /** The phase index's element (tests and the harness read it). */ + get phaseIndexElement(): HTMLElement { + return this.phaseIndex.root; + } + + /** The phases the phase index marks as in view, in drawn order. */ + phasesInView(): string[] { + return this.phaseIndex.inView(); } /** - * Viewer M2 live fix: the panel's width decides whether the minimap has room (`.is-narrow`, - * styles/workflow.css; MINIMAP_NARROW_W). An unmeasurable panel (jsdom, a detached mount) - * leaves it as it was. + * The panel's width decides the phase index's form: the panel of rows from PHASE_INDEX_LIST_MIN_W + * (1000 px), the pill below. An unmeasurable panel (jsdom, a detached mount) keeps the panel. */ setPanelWidth(width: number): void { if (!(width > 0)) return; - this.minimap.root.classList.toggle('is-narrow', width <= MINIMAP_NARROW_W); + this.panelWidth = width; + this.syncPhaseIndex(); + } + + /** + * Why the phase index is not drawn now, in words for the ... menu, or null when it is (as the + * panel of rows or as the pill). It needs two or more phases; nothing else hides it. + */ + phaseIndexUnavailable(): string | null { + return this.phaseIndex.available ? null : 'Shown when the diagram has two or more phases'; + } + + /** Close the pill's open phase list (an Escape rung); false when it was not open. */ + closePhasePopover(): boolean { + return this.phaseIndex.closePopover(); + } + + private syncPhaseIndex(): void { + const height = this.canvasEl.getBoundingClientRect().height; + const wide = !(this.panelWidth > 0) || this.panelWidth >= PHASE_INDEX_LIST_MIN_W; + const tall = !(height > 0) || height >= PHASE_INDEX_LIST_MIN_H; + this.phaseIndex.setMode({ roomy: wide && tall, folded: this.indexFolded, hidden: this.indexHidden, suppressed: this.overview.open }); + } + + /** What the phase index covers, in canvas pixels (the viewport keeps targets out of it). */ + private indexCovered(): { x: number; y: number; w: number; h: number } | null { + const r = this.canvasEl.getBoundingClientRect(); + const size = this.viewport.size(); + return this.phaseIndex.coveredRect(size.w, size.h, r.width > 0 ? r.left : 0, r.width > 0 ? r.top : 0); + } + + private renderPhaseIndex(): void { + if (!this.index || !this.frameData) return; + const rows: PhaseRow[] = this.frameData.lanes.map((lane) => ({ + id: lane.id, + label: lane.label, + index: this.index!.phaseIndexOf(lane.id), + steps: lane.nodeCount, + counts: this.index!.laneCounts(lane.id, (issue) => this.host.keep(issue)), + rect: { x: lane.x, y: lane.y, w: lane.w, h: lane.h }, + })); + this.phaseIndex.setRows(rows); + this.syncPhaseIndex(); + const size = this.viewport.size(); + this.phaseIndex.setInView(this.viewport.vp, size.w, size.h); + } + + /* ── the phase overview (viewer M3, Shift+0) ─────────────────────────── */ + + get overviewOpen(): boolean { + return this.overview.open; + } + + /** The overview's element and layout (tests and the harness read them). */ + get overviewElement(): HTMLElement { + return this.overview.root; + } + + overviewLayout() { + return this.overview.layout; + } + + /** + * Open the overview over the canvas, on the phase most in view. The diagram under it does not + * move, so closing it is going back to exactly where the reader was. False with no phases. + */ + openOverview(): boolean { + if (!this.index || !this.frameData || this.overview.open || !this.index.lanes.length) return false; + const doc = this.canvasEl.ownerDocument; + const active = doc ? (doc.activeElement as HTMLElement | null) : null; + this.overviewReturn = active && active !== doc!.body ? active : null; + this.tooltip.hide(); + const input = overviewInput(this.index, (issue) => this.host.keep(issue)); + const size = this.viewport.size(); + const current = this.phaseIndex.currentPhase(); + const at = Math.max(0, input.phases.findIndex((p) => p.id === current)); + this.overview.show(input, size.w, size.h, at); + this.syncPhaseIndex(); + const n = input.phases.length; + this.host.announce('Phase overview: ' + n + (n === 1 ? ' phase' : ' phases') + ', ' + input.totalSteps + (input.totalSteps === 1 ? ' step' : ' steps') + + '. Arrow keys move between phases, Enter goes to one, Escape goes back.'); + return true; + } + + /** + * Close the overview. `back` (Escape, Shift+0, the Back button): the keyboard returns to where it + * was, and the diagram is as it was. Otherwise (a phase was chosen, another key acted) the caller + * decides where the focus goes. False when it was not open. + */ + closeOverview(back: boolean): boolean { + if (!this.overview.open) return false; + const doc = this.canvasEl.ownerDocument; + const hadFocus = !!doc && !!doc.activeElement && this.overview.root.contains(doc.activeElement); + this.overview.hide(); + this.syncPhaseIndex(); + const target = this.overviewReturn; + this.overviewReturn = null; + if (back) { + this.host.announce('Back to the diagram.'); + if (hadFocus) this.focusSafely(target && target.isConnected && !this.overview.root.contains(target) ? target : this.canvasEl); + } else if (hadFocus) this.focusSafely(this.canvasEl); + return true; + } + + toggleOverview(): void { + if (this.overview.open) this.closeOverview(true); + else this.openOverview(); } /** - * Viewer M2 live fix: why the minimap is not drawn now, in words for the ... menu, or null when - * it is drawn (collapsed to its tab or not). Before, the menu showed "Overview map" checked - * while the stylesheet hid the map beside the code. + * Go to a phase at reading size (`phasePlan`), animated over VIEW_ANIMATION_MS unless motion is + * reduced: a block of the overview, or a row of the phase index. The selection is unchanged; the + * next arrow key starts at the phase's first step. */ - minimapUnavailable(): string | null { - const root = this.minimap.root; - if (root.hidden) return 'Shown when ' + MINIMAP_MIN_NODES + ' or more cards are drawn'; - if (root.classList.contains('is-narrow')) return 'No room in a panel ' + MINIMAP_NARROW_W + ' px wide or narrower'; - if (root.classList.contains('is-short')) return 'No room in a canvas under ' + MINIMAP_MIN_CANVAS_H + ' px tall'; - return null; + goToPhase(laneId: string, opts: { focusCanvas?: boolean } = {}): void { + if (!this.index || !this.frameData) return; + const k = this.frameData.lanes.findIndex((lane) => lane.id === laneId); + if (k < 0) return; + const wasOpen = this.closeOverview(false); + const size = this.viewport.size(); + const plan = phasePlan(this.frameData, k, size.w, size.h); + if (plan) this.viewport.animateTo({ x: plan.x, y: plan.y, zoom: plan.zoom }); + this.arrowLane = laneId; + const lane = this.frameData.lanes[k]; + this.host.announce('Phase ' + (this.index.phaseIndexOf(laneId) + 1) + ': ' + lane.label + ', ' + lane.nodeCount + (lane.nodeCount === 1 ? ' step' : ' steps') + '.'); + if (opts.focusCanvas || wasOpen) this.focusSafely(this.canvasEl); + } + + /** The phase a move to a phase landed on, once (the next arrow key starts there), or null. */ + takeArrowLane(): string | null { + const lane = this.arrowLane; + this.arrowLane = null; + return lane; + } + + /** Forget the arrow start (any selection replaces it). */ + clearArrowLane(): void { + this.arrowLane = null; + } + + private focusSafely(target: HTMLElement): void { + try { + target.focus(); + } catch (_e) { + /* a detached element cannot take focus */ + } } setStale(files: StaleFile[]): void { @@ -265,6 +505,21 @@ export class CanvasView { return this.nodeEls.get(id); } + /** + * Viewer M3 (step 14): put the keyboard on a step's card (a group's header), on a connection, or, + * with `null` or a target that is not drawn on its own, on the canvas. + */ + focusTarget(target: { kind: 'node' | 'edge'; id: string } | null): void { + let element: Element | null | undefined = null; + if (target && target.kind === 'node' && this.index) { + const card = this.nodeEls.get(this.index.visibleRepresentative(target.id, this.collapsedSet)); + element = card && card.classList.contains('mlv-group') ? card.querySelector('.mlv-group__header') : card; + } else if (target && target.kind === 'edge') { + element = this.edgeEls.get(target.id)?.querySelector('.mlv-edge__hit'); + } + this.focusSafely((element as HTMLElement | null) || this.canvasEl); + } + /** Re-run layout + routing. The only thing that moves boxes. */ relayout(): void { if (!this.index) return; @@ -342,28 +597,25 @@ export class CanvasView { // controller's memory and re-stamp the canvas (CONTRACTS 11.14 C1); a // latched pulse is re-applied by the applySelection that follows. this.flow.clear(); - this.renderMinimap(); + this.renderPhaseIndex(); this.renderEmptyState(); - } - - private renderMinimap(): void { - if (!this.index || !this.frameData) return; - const dots = minimapDots(this.index, this.frameData, (issue) => this.host.keep(issue)); - this.minimap.render(dots, this.frameData.width, this.frameData.height); - this.minimap.root.hidden = dots.length < MINIMAP_MIN_NODES; - this.syncShortCanvas(); + // Viewer M3: an open overview follows the new data (a severity toggle changes its F labels). + if (this.overview.open && this.index) { + const size = this.viewport.size(); + this.overview.redraw(overviewInput(this.index, (issue) => this.host.keep(issue)), size.w, size.h); + } } /** - * Issue 6: a 200x132 minimap over a canvas under MINIMAP_MIN_CANVAS_H tall - * covers a third of the diagram it is meant to summarise, so it is not drawn - * there (`.is-short`, styles/workflow.css). The reader's own collapse choice - * is untouched. An unmeasurable canvas (jsdom, before first layout) keeps it. + * Issue 6, viewer M3: a canvas under PHASE_INDEX_LIST_MIN_H tall has no room for the panel of + * rows, so the phase index is its pill there; an open overview is drawn for the new size. */ private syncShortCanvas(): void { - const height = this.canvasEl.getBoundingClientRect().height; - if (!(height > 0)) return; - this.minimap.root.classList.toggle('is-short', height < MINIMAP_MIN_CANVAS_H); + this.syncPhaseIndex(); + if (this.overview.open && this.index) { + const size = this.viewport.size(); + this.overview.redraw(overviewInput(this.index, (issue) => this.host.keep(issue)), size.w, size.h); + } } /** @@ -538,32 +790,6 @@ export class CanvasView { ); } - /** - * Overview mode (VIEW-10, `Shift+0`): every group folded to its card, then a - * WHOLE fit — the picture a reviewer actually wants to paste. The aggregated - * severity markers survive, because a collapsed group already carries them. - * - * Measured on the emitted demo in Chromium after the ANA-1 re-baseline: 22 - * cards, all of them inside the canvas at 1440x900 and at 1600x1000. VIEW-10's - * "<= 14 visible cards" was written against the 45-node demo; the count is a - * property of how many top-level units the workspace has, not of this method. - */ - overview(): number { - if (!this.index) return 0; - const groups: string[] = []; - for (const node of this.index.graph.nodes || []) { - if (this.index.isGroup(node.id)) groups.push(node.id); - } - this.collapsedSet = new Set(groups); - this.relayout(); - // fitWhole, not fit: Overview must never take the top-anchored tall branch, - // or the whole-diagram picture it exists to produce opens clipped. - this.viewport.fitWhole(); - this.host.afterCollapse(); - this.host.announce('Overview: ' + groups.length + (groups.length === 1 ? ' group' : ' groups') + ' collapsed, whole diagram fitted.'); - return groups.length; - } - /** Expand every collapsed ancestor of `id`. Returns true when it relaid out. */ expandAncestors(id: string): boolean { if (!this.index) return false; @@ -678,6 +904,34 @@ export class CanvasView { if (issue.nodeIds[0]) this.pulseNode(this.index.visibleRepresentative(issue.nodeIds[0], this.collapsedSet)); } + /** + * Viewer M3 (step 14), a connection revealed from the code: frame both its ends as a finding's + * cited cards are framed (`ViewportController.frameRect`). At full detail the zoom is kept when + * they fit; otherwise it is the largest that fits them, between FRAME_MIN_ZOOM and READABLE_ZOOM; + * ends too far apart even at that floor centre on the source card, where the connection starts. + * The source card is kept clear of the phase index. When keeping it clear pushed the target card + * out of view, the frame zooms out (never below FRAME_MIN_ZOOM) until both ends fit above the + * index or beside it. + */ + frameEdge(id: string): void { + if (!this.index || !this.frameData) return; + const edge = this.index.edgeById.get(id); + const union = this.targetRect({ kind: 'edge', id }); + if (!edge || !union) return; + const box = (nodeId: string) => this.frameData!.boxes.get(this.index!.visibleRepresentative(nodeId, this.collapsedSet)); + const source = box(edge.source); + const target = box(edge.target); + this.viewport.frameRect(union, source || union, READABLE_ZOOM); + const covered = this.viewport.covered(); + if (!target || !covered || this.viewport.isVisible(target)) return; + const margin = 48; + const { w, h } = this.viewport.visibleArea(); + const above = Math.min((w - margin) / Math.max(1, union.w), (h - covered.h - margin) / Math.max(1, union.h)); + const beside = Math.min((w - covered.w - margin) / Math.max(1, union.w), (h - margin) / Math.max(1, union.h)); + const zoom = Math.min(this.viewport.vp.zoom, Math.max(above, beside)); + if (zoom >= FRAME_MIN_ZOOM) this.viewport.centerOn(union, zoom); + } + /** Issue 6: bring a connection's two ends into view, zooming in when below the detail threshold. */ revealEdge(id: string): void { const union = this.targetRect({ kind: 'edge', id }); @@ -686,6 +940,23 @@ export class CanvasView { else if (!this.viewport.isVisible(union)) this.viewport.centerOn(union); } + /** + * Viewer M3, a step of the review walk: below the detail threshold, zoom to reading size on the + * step or the connection's two ends (`revealNode`, `revealEdge`); at reading size, pan the least + * distance that shows it whole (`revealRect`), so stepping through neighbours does not swing the + * picture to centre every card. The visible area is the canvas above the bottom sheet. + */ + revealTarget(target: { kind: 'node' | 'edge'; id: string }): void { + const rect = this.targetRect(target); + if (!rect) return; + if (this.viewport.vp.zoom < LOD_FULL_ZOOM) { + if (target.kind === 'node') this.revealNode(target.id, false); + else this.revealEdge(target.id); + return; + } + if (!this.viewport.isVisible(rect)) this.viewport.revealRect(rect); + } + /** The laid-out rect of a node's visible card, or of both ends of a connection. */ private targetRect(target: { kind: 'node' | 'edge'; id: string }): { x: number; y: number; w: number; h: number } | null { if (!this.index || !this.frameData) return null; @@ -739,11 +1010,39 @@ export class CanvasView { if (box) this.viewport.zoomToBox(box); } - /** The next node an arrow key should select, in spatial sibling order. */ - nextSelection(currentId: string | null, key: string): NextSelection | null { - if (!this.index || !this.frameData) return null; - const anchored = currentId && this.frameData.boxes.has(currentId) ? currentId : null; - const next = anchored ? nextBox(this.index, this.frameData, anchored, key) : firstBox(this.frameData); + /** + * The card an arrow key selects from `sel`. From a step: the next card that way, in spatial + * sibling order (`nextBox`), counted from its drawn card (the collapsed group around it, if any). + * Viewer M3 (live check, W5): from a connection, the one of its two cards that lies further that + * way (`connectionEnd`); from a finding, as from the step the diagram marks for it (its first + * cited step), else as from its first cited connection. Only with nothing to start from (no + * selection, or a finding that cites neither) is it the diagram's first card; before, a selected + * connection or finding also went there, so → on a connection in vit-cc selected the first step + * of phase 1. + */ + nextSelection(sel: Sel | null, key: string): NextSelection | null { + const index = this.index; + const frame = this.frameData; + if (!index || !frame) return null; + const drawn = (id: string): string | null => (index.nodeById.has(id) ? index.visibleRepresentative(id, this.collapsedSet) : null); + const fromStep = (id: string): LayoutBox | null | undefined => { + const card = drawn(id); + return card && frame.boxes.has(card) ? nextBox(index, frame, card, key) : undefined; + }; + const fromEdge = (id: string): LayoutBox | null | undefined => { + const edge = index.edgeById.get(id); + return edge ? connectionEnd(frame, drawn(edge.source), drawn(edge.target), key) || undefined : undefined; + }; + let next: LayoutBox | null | undefined; + if (sel && sel.kind === 'node') next = fromStep(sel.id); + else if (sel && sel.kind === 'edge') next = fromEdge(sel.id); + else if (sel && sel.kind === 'issue') { + const issue = index.issueById.get(sel.id); + if (issue && issue.nodeIds.length) next = fromStep(issue.nodeIds[0]); + if (next === undefined && issue && issue.edgeIds.length) next = fromEdge(issue.edgeIds[0]); + } + // `undefined`: nothing to count from, so the first card. `null`: no card further that way. + if (next === undefined) next = firstBox(frame); if (!next) return null; return { id: next.id, visible: this.viewport.isVisible(next) }; } @@ -759,6 +1058,8 @@ export class CanvasView { } destroy(): void { + this.phaseIndex.destroy(); + this.overview.destroy(); this.emphasis.destroy(); this.edgeHover.destroy(); for (const dispose of this.disposers) { diff --git a/webview/src/icons.ts b/webview/src/icons.ts index 83a22046..39566b2d 100644 --- a/webview/src/icons.ts +++ b/webview/src/icons.ts @@ -185,10 +185,14 @@ const UI_PATHS: Record = { /** The legend key (VIEW-10): a list with a swatch beside each row. */ legend: 'M2.4 3.6h2.4v2.4H2.4ZM2.4 10h2.4v2.4H2.4ZM6.8 4.8h6.8M6.8 11.2h6.8', copy: 'M5.8 5.8h7.6v7.6H5.8ZM2.6 10.2V2.6h7.6v3.2', - /** The overview minimap: a frame with its viewport. */ - minimap: 'M2.4 3.6h11.2v8.8H2.4ZM8.4 7.4h4.4v4.4H8.4Z', + /** Viewer M3: the phase overview, three stacked blocks joined by a line. */ + phases: 'M2.6 2.2h10.8v3H2.6ZM2.6 6.5h10.8v3H2.6ZM2.6 10.8h10.8v3H2.6ZM4.6 5.2v1.3M4.6 9.5v1.3', + /** Viewer M3: the phase index (it replaced the minimap): numbered rows, one marked. */ + phaseindex: 'M2.4 3.4h2v2h-2ZM2.4 7h2v2h-2ZM2.4 10.6h2v2h-2ZM6.4 4.4h7.2M6.4 8h7.2M6.4 11.6h7.2', /** VIEW-07: a picture in a frame — "export the diagram". */ image: 'M2.4 3.4h11.2v9.2H2.4ZM2.4 10.6 5.8 7.4l2.4 2.2 2.2-2 3.2 3M10.3 5.3a1.05 1.05 0 1 0 0 2.1 1.05 1.05 0 0 0 0-2.1Z', + /** Viewer M3: the review walk: a pointer at the first of three rows. */ + review: 'M2.4 3.2 4.8 5.4 2.4 7.6M7.2 5.4h6.4M7.2 9.4h6.4M2.6 13h11', /** The shortcut sheet: a keyboard. */ keyboard: 'M1.8 4.2h12.4v7.6H1.8ZM4.2 6.6h.1M6.7 6.6h.1M9.2 6.6h.1M11.7 6.6h.1M4.8 9.4h6.4', }; diff --git a/webview/src/layout/layout.ts b/webview/src/layout/layout.ts index 5027bb00..eaf52636 100644 --- a/webview/src/layout/layout.ts +++ b/webview/src/layout/layout.ts @@ -182,8 +182,8 @@ export function layoutGraph(index: GraphIndex, collapsed: Set): LayoutFr // maxContentW` made the world as wide as its widest band and left the others // 56-87 % empty, so `fit()` scaled the whole document down to the one lane // that needed the room. A lane now ends where its own content ends; the world - // is still as wide as the widest lane, which is what the band striping and - // the minimap letterbox measure themselves against. + // is still as wide as the widest lane, which is what the band striping + // measures itself against. const width = laneX + maxContentW + CANVAS_MARGIN; const height = Math.max(cursorY - LANE_GUTTER + CANVAS_MARGIN, CANVAS_MARGIN * 2); diff --git a/webview/src/layout/navigate.ts b/webview/src/layout/navigate.ts index 69737bdb..4045b8ef 100644 --- a/webview/src/layout/navigate.ts +++ b/webview/src/layout/navigate.ts @@ -31,3 +31,23 @@ export function nextBox(index: GraphIndex, frame: LayoutFrame, currentId: string }; return step(siblings) || step(boxes); } + +/** + * Viewer M3 (live check, W5): from a selected connection, an arrow key moves to whichever of its two + * drawn cards lies further that way: → the one further right, ← further left, ↓ lower, ↑ higher, + * by their centres. On a tie (both ends in one column for → and ←, one row for ↓ and ↑) → and ↓ + * take the connection's target and ← and ↑ its source. Both ids are the cards as drawn (a collapsed + * group stands for the steps inside it); a card that is not drawn is skipped. + */ +export function connectionEnd(frame: LayoutFrame, sourceId: string | null, targetId: string | null, key: string): LayoutBox | null { + const source = sourceId ? frame.boxes.get(sourceId) || null : null; + const target = targetId ? frame.boxes.get(targetId) || null : null; + if (!source || !target) return source || target; + const horizontal = key === 'ArrowRight' || key === 'ArrowLeft'; + const forward = key === 'ArrowRight' || key === 'ArrowDown'; + const centre = (b: LayoutBox) => (horizontal ? b.x + b.w / 2 : b.y + b.h / 2); + const delta = centre(target) - centre(source); + if (delta === 0) return forward ? target : source; + return (delta > 0) === forward ? target : source; +} + diff --git a/webview/src/protocol.ts b/webview/src/protocol.ts index 1cd5efc0..d7f0fd05 100644 --- a/webview/src/protocol.ts +++ b/webview/src/protocol.ts @@ -28,9 +28,12 @@ export interface ProtocolHandlers { /** The answer to a request that carried a `requestId` (§1e). */ actionResult(result: ActionResult): void; theme(kind: ThemeKind): void; - /** Not sent by the host today; kept for the planned "Reveal in Diagram" (M3). */ + /** + * Viewer M3 (step 14), MLView: Reveal in Diagram (the `reveal` frame, and the older `revealNode` + * and `revealIssue` frames): show the step, connection or finding the reader chose in the editor. + */ revealNode(nodeId: string, center: boolean): void; - /** Not sent by the host today; kept for the planned "Reveal in Diagram" (M3). */ + revealEdge(edgeId: string): void; revealIssue(issueId: string): void; /** Applies a saved `ViewState` (the geometry golden and the protocol tests drive collapse with it). */ restoreState(state: ViewState): void; @@ -62,11 +65,20 @@ export function dispatchHostMessage(msg: HostToUi, h: ProtocolHandlers): void { case 'theme': h.theme(msg.kind); return; + case 'reveal': { + // Viewer M3 (step 14): one frame for the three kinds. A malformed one is dropped. + const id = (msg as { id?: unknown }).id; + if (typeof id !== 'string' || !id) return; + if (msg.kind === 'node') h.revealNode(id, true); + else if (msg.kind === 'edge') h.revealEdge(id); + else if (msg.kind === 'issue') h.revealIssue(id); + return; + } case 'revealNode': - h.revealNode(msg.nodeId, msg.center !== false); + if (typeof msg.nodeId === 'string' && msg.nodeId) h.revealNode(msg.nodeId, msg.center !== false); return; case 'revealIssue': - h.revealIssue(msg.issueId); + if (typeof msg.issueId === 'string' && msg.issueId) h.revealIssue(msg.issueId); return; case 'restoreState': h.restoreState(msg.state); diff --git a/webview/src/render/canvas.ts b/webview/src/render/canvas.ts index 23eb3bcf..07f10c8c 100644 --- a/webview/src/render/canvas.ts +++ b/webview/src/render/canvas.ts @@ -1,14 +1,13 @@ /** * Viewport control: pan by dragging the background, zoom by wheel or buttons, - * the readable first view, fit, zoom-to-selection, and the minimap. Zoom writes + * the readable first view, fit, zoom-to-selection, and (viewer M3) the move to a phase and the + * part of the canvas the phase index covers. Zoom writes * at most one attribute (data-lod) and one bucketed custom property (--mlv-z) * per frame, so no component re-renders and nothing is laid out again while * zooming (R4.3). */ -import { svg, el, iconButton, on } from '../dom.js'; -import { uiIcon } from '../icons.js'; -import { stampPhase } from './phase.js'; +import { motionMode } from '../motion.js'; import type { Viewport } from '../types.js'; export const MIN_ZOOM = 0.15; @@ -143,6 +142,72 @@ export function readablePlan(frame: ReadableFrame, w: number, h: number, padding return { zoom, x, y, mode: phaseFit ? 'phase-fit' : 'phase-anchor' }; } +/** + * Viewer M3: how long a move to a phase takes (the phase overview's blocks, the phase index's + * rows). Instant under VS Code's Reduce Motion, the OS setting or VS Code's screen-reader mode + * (`motionMode`). + */ +export const VIEW_ANIMATION_MS = 240; + +/** + * Viewer M3: the view of phase `k` (its position among the lanes) at reading size, as a pure + * function of the frame and the canvas size, for the phase overview and the phase index. The rule + * is the readable plan's for phase 1: the lane (with the left routing channel, where the trunks + * that leave or enter it run) fitted whole when that zoom is PHASE_FIT_MIN_ZOOM (0.75) or more, + * capped at READABLE_ZOOM (0.9); otherwise READABLE_ZOOM anchored at its top-left. A document + * narrower (or shorter) than the canvas at that zoom is centred on that axis. A lane that fits is + * never left with empty canvas under the end of the document: the view stops where the world ends. + */ +export function phasePlan(frame: ReadableFrame, k: number, w: number, h: number, padding = 24): ReadablePlan | null { + const lane = frame.lanes[k]; + if (!lane) return null; + const contentW = Math.max(1, frame.width); + const contentH = Math.max(1, frame.height); + const left = lane.x - Math.max(0, frame.channelW || 0); + const rectW = Math.max(1, lane.x + lane.w - left); + const rectH = Math.max(1, lane.h); + const fits = Math.min((w - padding * 2) / rectW, (h - padding * 2) / rectH); + const phaseFit = fits >= PHASE_FIT_MIN_ZOOM; + const zoom = clamp(phaseFit ? Math.min(fits, READABLE_ZOOM) : READABLE_ZOOM, MIN_ZOOM, MAX_ZOOM); + const x = contentW * zoom <= w ? (w - contentW * zoom) / 2 : padding - left * zoom; + let y: number; + if (contentH * zoom <= h) y = (h - contentH * zoom) / 2; + else { + y = padding - lane.y * zoom; + // The last phases: keep the end of the world at the foot of the canvas when the lane fits. + const floor = h - padding - contentH * zoom; + if (rectH * zoom + 2 * padding <= h && y < floor) y = floor; + } + return { zoom, x, y, mode: phaseFit ? 'phase-fit' : 'phase-anchor' }; +} + +/** True when two rectangles overlap (touching edges do not). */ +export function rectsOverlap(a: Rect, b: Rect): boolean { + return a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h; +} + +/** + * Viewer M3: the least pan that moves a screen rectangle `r` (canvas pixels) off `covered` while + * keeping it inside a `w` x `h` canvas: to the left of it, above it, to its right or below it, + * whichever is shortest, with `gap` px to spare. Null when it is already clear, or when no such pan + * keeps it inside the canvas (a card wider and taller than the room around the phase index). + */ +export function clearOf(r: Rect, covered: Rect, w: number, h: number, gap = 8): { dx: number; dy: number } | null { + if (!rectsOverlap(r, covered)) return null; + const options: Array<{ dx: number; dy: number }> = []; + const left = covered.x - gap - (r.x + r.w); + if (r.x + left >= 0) options.push({ dx: left, dy: 0 }); + const up = covered.y - gap - (r.y + r.h); + if (r.y + up >= 0) options.push({ dx: 0, dy: up }); + const right = covered.x + covered.w + gap - r.x; + if (r.x + r.w + right <= w) options.push({ dx: right, dy: 0 }); + const down = covered.y + covered.h + gap - r.y; + if (r.y + r.h + down <= h) options.push({ dx: 0, dy: down }); + if (!options.length) return null; + options.sort((p, q) => Math.abs(p.dx) + Math.abs(p.dy) - (Math.abs(q.dx) + Math.abs(q.dy))); + return options[0]; +} + /** * A resize this large, in either dimension, refits a viewport the reader has * not moved since the last fit (issue 6): the panel opening narrow beside the @@ -181,6 +246,15 @@ export class ViewportController { private fitSize: { w: number; h: number; whole: boolean; padding: number } | null = null; /** The frame the readable plan anchors on (viewer M2); null before the first layout. */ private frame: ReadableFrame | null = null; + /** + * Viewer M3: the part of the canvas an overlay covers (the phase index), in canvas pixels, read + * when it is needed. Reveals, centring and the visibility test keep a target out of it. + */ + private coveredFn: (() => Rect | null) | null = null; + /** Viewer M3: bumped by every move, so a running animation stops as soon as anything else moves the view. */ + private animToken = 0; + /** The token of the animation in flight, 0 when none is. */ + private animLive = 0; constructor(canvas: HTMLElement, world: HTMLElement, onChange: (vp: Viewport) => void) { this.canvas = canvas; this.world = world; @@ -210,6 +284,7 @@ export class ViewportController { } set(vp: Partial): void { + this.animToken++; this.fitted = false; if (typeof vp.x === 'number' && isFinite(vp.x)) this.vp.x = vp.x; if (typeof vp.y === 'number' && isFinite(vp.y)) this.vp.y = vp.y; @@ -218,6 +293,7 @@ export class ViewportController { } panBy(dx: number, dy: number): void { + this.animToken++; this.fitted = false; this.vp.x += dx; this.vp.y += dy; @@ -226,6 +302,7 @@ export class ViewportController { /** Zoom about a point in canvas-local coordinates. */ zoomAt(factor: number, px: number, py: number): void { + this.animToken++; this.fitted = false; const next = clamp(this.vp.zoom * factor, MIN_ZOOM, MAX_ZOOM); const k = next / this.vp.zoom; @@ -272,6 +349,7 @@ export class ViewportController { } private applyFit(padding: number, whole: boolean): void { + this.animToken++; const { w, h } = this.size(); if (whole || !this.frame) { const { zoom } = fitPlan(this.contentW, this.contentH, w, h, padding); @@ -311,8 +389,8 @@ export class ViewportController { /** * A resized canvas (issue 6). A fitted viewport is refitted when the size * changed by at least REFIT_MIN_PX and REFIT_MIN_RATIO in either dimension; - * anything else only refreshes the derived chrome (zoom readout, minimap - * rectangle) without moving what the reader is looking at. + * anything else only refreshes the derived chrome (zoom readout, the phase + * index's in-view marks) without moving what the reader is looking at. */ onResize(): boolean { if (this.fitted && this.fitSize) { @@ -369,20 +447,45 @@ export class ViewportController { const zoom = this.vp.zoom; if (zoom >= LOD_FULL_ZOOM && fits >= zoom) { this.centerOn(rect); + this.keepClear(anchor); return; } const ceiling = zoom >= LOD_FULL_ZOOM ? Math.max(FRAME_MAX_ZOOM, zoom) : readable; const next = Math.max(FRAME_MIN_ZOOM, Math.min(ceiling, fits)); this.centerOn(fits >= FRAME_MIN_ZOOM ? rect : anchor, next); + this.keepClear(anchor); + } + + /** + * Viewer M3: a frame too large to move off the phase index as a whole (`centerOn` leaves it) still + * keeps its anchor, the first cited card, out from under the index: the least pan off it. + */ + private keepClear(anchor: Rect): void { + const covered = this.covered(); + if (!covered) return; + const { w, h } = this.visibleArea(); + const z = this.vp.zoom; + const shift = clearOf({ x: anchor.x * z + this.vp.x, y: anchor.y * z + this.vp.y, w: anchor.w * z, h: anchor.h * z }, covered, w, h); + if (shift) this.panBy(shift.dx, shift.dy); } centerOn(rect: Rect, zoom?: number): void { + this.animToken++; this.fitted = false; const { w, h } = this.visibleArea(); if (typeof zoom === 'number') this.vp.zoom = clamp(zoom, MIN_ZOOM, MAX_ZOOM); const z = this.vp.zoom; this.vp.x = w / 2 - (rect.x + rect.w / 2) * z; this.vp.y = h / 2 - (rect.y + rect.h / 2) * z; + // Viewer M3: a centred target the phase index would cover moves the least distance off it. + const covered = this.covered(); + if (covered) { + const shift = clearOf({ x: rect.x * z + this.vp.x, y: rect.y * z + this.vp.y, w: rect.w * z, h: rect.h * z }, covered, w, h); + if (shift) { + this.vp.x += shift.dx; + this.vp.y += shift.dy; + } + } this.apply(); } @@ -402,8 +505,17 @@ export class ViewportController { if (start + size > extent) return extent - pad - (start + size); return 0; }; - const dx = shift(rect.x * z + this.vp.x, rect.w * z, w); - const dy = shift(rect.y * z + this.vp.y, rect.h * z, h); + let dx = shift(rect.x * z + this.vp.x, rect.w * z, w); + let dy = shift(rect.y * z + this.vp.y, rect.h * z, h); + // Viewer M3: and off the part of the canvas the phase index covers, the least distance again. + const covered = this.covered(); + if (covered) { + const off = clearOf({ x: rect.x * z + this.vp.x + dx, y: rect.y * z + this.vp.y + dy, w: rect.w * z, h: rect.h * z }, covered, w, h, Math.min(margin, 8)); + if (off) { + dx += off.dx; + dy += off.dy; + } + } if (dx || dy) this.panBy(dx, dy); } @@ -413,167 +525,88 @@ export class ViewportController { this.centerOn(rect, z); } - /** True when the rect is fully inside the visible area (by default the current one). */ + /** + * True when the rect is fully inside the visible area (by default the current one). Viewer M3: and + * not under the phase index, so a card the index covers counts as out of view and is revealed. + */ isVisible(rect: Rect, area: { w: number; h: number } = this.visibleArea()): boolean { const { w, h } = area; const x = rect.x * this.vp.zoom + this.vp.x; const y = rect.y * this.vp.zoom + this.vp.y; - return x >= 0 && y >= 0 && x + rect.w * this.vp.zoom <= w && y + rect.h * this.vp.zoom <= h; + const sw = rect.w * this.vp.zoom; + const sh = rect.h * this.vp.zoom; + if (!(x >= 0 && y >= 0 && x + sw <= w && y + sh <= h)) return false; + const covered = this.covered(); + return !covered || !rectsOverlap({ x, y, w: sw, h: sh }, covered); } -} - -export function clamp(v: number, lo: number, hi: number): number { - return v < lo ? lo : v > hi ? hi : v; -} -export interface MinimapDot { - x: number; - y: number; - w: number; - h: number; - stage: string; - /** Viewer M2: the phase's document position, the key of its colour. */ - phase?: number; - severity: string | null; -} - -/** - * The minimap's drawing transform: a uniform letterboxed fit, CENTRED in the - * widget. Drawing, the viewport rectangle and click-to-jump all go through this - * one object — they used to disagree, so a click landed nowhere near the dot - * under the cursor (MLV-R1-008). - */ -export interface MinimapFit { - scale: number; - ox: number; - oy: number; - w: number; - h: number; - contentW: number; - contentH: number; -} - -export function minimapFit(w: number, h: number, contentW: number, contentH: number): MinimapFit { - const cw = Math.max(1, contentW); - const ch = Math.max(1, contentH); - const scale = Math.min(w / cw, h / ch); - return { scale, ox: (w - cw * scale) / 2, oy: (h - ch * scale) / 2, w, h, contentW: cw, contentH: ch }; -} - -/** Widget coordinates (viewBox units) -> world coordinates, clamped to content. */ -export function minimapToWorld(fit: MinimapFit, vx: number, vy: number): { x: number; y: number } { - return { - x: clamp((vx - fit.ox) / fit.scale, 0, fit.contentW), - y: clamp((vy - fit.oy) / fit.scale, 0, fit.contentH), - }; -} - -/** World coordinates -> widget coordinates. The inverse of minimapToWorld. */ -export function minimapFromWorld(fit: MinimapFit, x: number, y: number): { x: number; y: number } { - return { x: fit.ox + x * fit.scale, y: fit.oy + y * fit.scale }; -} - -export class Minimap { - readonly root: HTMLElement; - private svgEl: SVGElement; - private nodesG: SVGElement; - private viewRect: SVGElement; - private toggleBtn: HTMLButtonElement; - private collapsedState = false; - private w = 200; - private h = 130; - private fit: MinimapFit = minimapFit(200, 130, 1, 1); - - constructor(onJump: (x: number, y: number) => void, onToggle?: (collapsed: boolean) => void) { - this.root = el('div', 'mlv-minimap'); - // VIEW-12. The minimap is a DUPLICATE of a canvas that is already fully - // navigable — one focus stop, `aria-activedescendant` roving, a live region - // announcing every selection — so it is hidden from assistive tech rather - // than described twice. - this.root.setAttribute('aria-hidden', 'true'); - // The widget sits on top of live diagram, so it must be dismissable - // (UX_DESIGN section 1: "collapsible to a 28 px chevron tab") — MLV-R2-W12. - this.toggleBtn = iconButton('mlv-btn mlv-btn--icon mlv-minimap__toggle', 'Collapse minimap'); - this.toggleBtn.appendChild(uiIcon('chevron', 12)); - this.toggleBtn.setAttribute('aria-expanded', 'true'); - // ...and an `aria-hidden` subtree may not hold a tab stop, so this chevron - // is the POINTER affordance only. The keyboard's copy of it is the labelled - // "Minimap" toggle in the toolbar, which is before the canvas in DOM order - // instead of the tab stop after it that this button used to be (VIEW-12). - this.toggleBtn.tabIndex = -1; - on(this.toggleBtn, 'click', (ev: MouseEvent) => { - ev.preventDefault(); - ev.stopPropagation(); - this.setCollapsed(!this.collapsedState); - if (onToggle) onToggle(this.collapsedState); - }); - this.root.appendChild(this.toggleBtn); - this.svgEl = svg('svg', { class: 'mlv-minimap__svg', viewBox: '0 0 200 130', 'aria-hidden': 'true' }); - this.nodesG = svg('g'); - this.viewRect = svg('rect', { class: 'mlv-minimap__view', x: 0, y: 0, width: 0, height: 0, rx: 2 }); - this.svgEl.appendChild(this.nodesG); - this.svgEl.appendChild(this.viewRect); - this.root.appendChild(this.svgEl); - this.svgEl.addEventListener('pointerdown', (ev: Event) => { - const e = ev as PointerEvent; - const box = (this.svgEl as unknown as HTMLElement).getBoundingClientRect(); - // The widget is drawn in viewBox units; convert the click into those units - // first, then invert the SAME transform the dots were drawn with. - const vx = box.width ? ((e.clientX - box.left) / box.width) * this.w : 0; - const vy = box.height ? ((e.clientY - box.top) / box.height) * this.h : 0; - const world = minimapToWorld(this.fit, vx, vy); - onJump(world.x, world.y); - }); + /** Viewer M3: say what part of the canvas is covered (the phase index), read on demand. */ + setCovered(fn: (() => Rect | null) | null): void { + this.coveredFn = fn; } - get collapsed(): boolean { - return this.collapsedState; - } - - setCollapsed(next: boolean): void { - this.collapsedState = next; - if (next) this.root.classList.add('is-collapsed'); - else this.root.classList.remove('is-collapsed'); - this.toggleBtn.setAttribute('aria-expanded', next ? 'false' : 'true'); - const label = next ? 'Expand minimap' : 'Collapse minimap'; - this.toggleBtn.title = label; - this.toggleBtn.setAttribute('aria-label', label); + /** The covered part of the canvas now, or null. */ + covered(): Rect | null { + if (!this.coveredFn) return null; + try { + const r = this.coveredFn(); + return r && r.w > 0 && r.h > 0 ? r : null; + } catch (_e) { + return null; + } } - render(dots: MinimapDot[], contentW: number, contentH: number): void { - this.fit = minimapFit(this.w, this.h, contentW, contentH); - const { scale, ox, oy } = this.fit; - while (this.nodesG.firstChild) this.nodesG.removeChild(this.nodesG.firstChild); - for (const d of dots) { - const r = svg('rect', { - class: 'mlv-minimap__node' + (d.severity ? ' mlv-minimap__node--issue' : ''), - x: round2(ox + d.x * scale), - y: round2(oy + d.y * scale), - width: round2(Math.max(2, d.w * scale)), - height: round2(Math.max(2, d.h * scale)), - rx: 1, - }); - r.setAttribute('data-stage', d.stage); - if (d.phase !== undefined) stampPhase(r, d.phase); - if (d.severity) r.setAttribute('data-sev', d.severity); - this.nodesG.appendChild(r); + /** + * Viewer M3: move to `target` over VIEW_ANIMATION_MS (the move to a phase), or at once under + * reduced motion (VS Code's Reduce Motion, the OS setting, VS Code's screen-reader mode) or where + * there is no animation frame. The point at the canvas centre travels in a straight line while + * the zoom changes on a log scale, eased out. Any other move (a pan, a wheel, a key, a reveal) + * stops it where it is. Returns once the move is set up; `done` runs when it lands (not when it + * is stopped). + */ + animateTo(target: Viewport, ms = VIEW_ANIMATION_MS, done?: () => void): void { + const token = ++this.animToken; + this.fitted = false; + const to: Viewport = { x: target.x, y: target.y, zoom: clamp(target.zoom, MIN_ZOOM, MAX_ZOOM) }; + const raf = typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function' ? window.requestAnimationFrame.bind(window) : null; + const land = () => { + this.animLive = 0; + this.vp = { ...to }; + this.apply(); + if (done) done(); + }; + if (!raf || !(ms > 0) || motionMode() === 'reduced') { + land(); + return; } + const from: Viewport = { ...this.vp }; + const { w, h } = this.size(); + const c0 = { x: (w / 2 - from.x) / from.zoom, y: (h / 2 - from.y) / from.zoom }; + const c1 = { x: (w / 2 - to.x) / to.zoom, y: (h / 2 - to.y) / to.zoom }; + const start = Date.now(); + this.animLive = token; + const frame = () => { + if (token !== this.animToken) return; + const t = Math.min(1, (Date.now() - start) / ms); + if (t >= 1) { + land(); + return; + } + const e = 1 - Math.pow(1 - t, 3); + const z = from.zoom * Math.pow(to.zoom / from.zoom, e); + this.vp = { x: w / 2 - (c0.x + (c1.x - c0.x) * e) * z, y: h / 2 - (c0.y + (c1.y - c0.y) * e) * z, zoom: z }; + this.apply(); + raf(frame); + }; + raf(frame); } - setViewport(vp: Viewport, viewW: number, viewH: number): void { - const { scale, ox, oy } = this.fit; - const topLeft = minimapFromWorld(this.fit, -vp.x / vp.zoom, -vp.y / vp.zoom); - const w = (viewW / vp.zoom) * scale; - const h = (viewH / vp.zoom) * scale; - const x = clamp(topLeft.x, ox, ox + this.fit.contentW * scale); - const y = clamp(topLeft.y, oy, oy + this.fit.contentH * scale); - this.viewRect.setAttribute('x', String(round2(x))); - this.viewRect.setAttribute('y', String(round2(y))); - this.viewRect.setAttribute('width', String(round2(Math.max(0, Math.min(w, ox + this.fit.contentW * scale - x))))); - this.viewRect.setAttribute('height', String(round2(Math.max(0, Math.min(h, oy + this.fit.contentH * scale - y))))); + /** True while an `animateTo` is under way (not landed, not stopped by another move). */ + get animating(): boolean { + return this.animLive !== 0 && this.animLive === this.animToken; } } -function round2(v: number): number { - return Math.round(v * 100) / 100; +export function clamp(v: number, lo: number, hi: number): number { + return v < lo ? lo : v > hi ? hi : v; } diff --git a/webview/src/render/edgehover.ts b/webview/src/render/edgehover.ts index f898cf23..ba3e84b4 100644 --- a/webview/src/render/edgehover.ts +++ b/webview/src/render/edgehover.ts @@ -30,7 +30,7 @@ import type { ViewportController } from './canvas.js'; export const EDGE_PICK_PX = 10; /** Anything on this list owns its own pixels outright; cables never win there. */ -const OPAQUE = '.mlv-node, .mlv-group, .mlv-minimap, .mlv-zoom, .mlv-tooltip, .mlv-toasts, .mlv-state'; +const OPAQUE = '.mlv-node, .mlv-group, .mlv-phaseindex, .mlv-overview, .mlv-zoom, .mlv-tooltip, .mlv-toasts, .mlv-state'; export interface EdgeHoverHost { canvas: HTMLElement; @@ -57,6 +57,11 @@ export interface EdgeHoverHost { changed?(route: RoutedEdge | null): void; openDelayMs: number; closeDelayMs: number; + /** + * Viewer M3 (live check, W3): true while a keyboard move holds the hover back, until the pointer + * really moves; no cable takes the pointer meanwhile. + */ + held?(): boolean; } export class EdgeHover { @@ -80,9 +85,31 @@ export class EdgeHover { /** A hit path took the pointer. The resolver may still overrule it. */ enter(route: RoutedEdge): void { + if (this.host.held && this.host.held()) return; this.set(route); } + /** + * Viewer M3 (live check, W3): give the pointer up now, without the close delay: the cable's + * `.is-hover`, a pending open and its hover card. Nothing happens when no cable has it. + */ + drop(): void { + const pending = this.timer !== null; + if (pending) { + clearTimeout(this.timer!); + this.timer = null; + } + if (!this.currentId) { + if (pending) this.host.close(); + return; + } + const previous = this.host.edges().get(this.currentId); + if (previous) previous.classList.remove('is-hover'); + this.currentId = null; + if (this.host.changed) this.host.changed(null); + this.host.close(); + } + /** Only the cable that OWNS the pointer gives it up. */ leave(route: RoutedEdge): void { if (this.currentId === route.id) this.set(null); @@ -108,6 +135,7 @@ export class EdgeHover { } private resolve(ev: PointerEvent): void { + if (this.host.held && this.host.held()) return; const routes = this.host.routes(); if (!routes.length) return; if (this.host.canvas.classList.contains('is-panning')) return; diff --git a/webview/src/render/phase.ts b/webview/src/render/phase.ts index d9ea63ff..1040fe7e 100644 --- a/webview/src/render/phase.ts +++ b/webview/src/render/phase.ts @@ -1,7 +1,7 @@ /** * Viewer M2: phase colour by document order. * - * Every element drawn for a phase (lane, card, group, connection, trunk, minimap dot) + * Every element drawn for a phase (lane, card, group, connection, trunk, phase-index row) * carries `data-phase-index` (its phase's 0-based position in the document) and `data-phase-tone` * (that position modulo `PHASE_TONES`). The stylesheet binds `--mlv-stage` from the tone * (styles/node.css), so the colour never depends on how the author spelled the phase id: the diff --git a/webview/src/render/phaseindex.ts b/webview/src/render/phaseindex.ts new file mode 100644 index 00000000..9245453d --- /dev/null +++ b/webview/src/render/phaseindex.ts @@ -0,0 +1,389 @@ +/** + * Viewer M3, roadmap step 13: the labelled phase index. It replaces the unlabelled minimap. + * + * A panel in the lower right corner of the canvas with one row per phase: the phase number (in + * its colour), the label, the findings touching it by severity (the PR #14 rule: a finding on two + * phases counts in both) and its step count. The phases in view are marked. A click on a row goes + * to that phase at reading size (the same move as the phase overview's blocks). + * + * Below 1000 px (and on a canvas under 350 px tall) it is a pill instead: "4/6 Objective, + * optimizer & scheduler ▴", the phase most in view. The pill opens the rows above itself. Wide, + * the panel's chevron folds it to the pill and the pill unfolds it. The ... menu shows or hides it. + * + * It never hides the card the keyboard is on: `coveredRect` is the part of the canvas it covers, + * and the viewport's reveal, centring and visibility test keep a target out of it + * (`ViewportController.setCovered`). It sits inside the canvas, so in the bottom sheet's layout it + * stays above the sheet (and above the review walk's bar), never on it. + * + * Keyboard: its rows are not Tab stops; the phase overview (Shift+0) is the keyboard's way to the + * same moves, with arrows between phases. The rows are labelled buttons, so a screen reader can + * still read and press them. + */ + +import { add, clear, el, on } from '../dom.js'; +import { uiIcon } from '../icons.js'; +import { clusterTitle, countsTotal, severityCluster } from '../markers.js'; +import { stampPhase } from './phase.js'; +import type { IssueCounts, Viewport } from '../types.js'; + +export interface PhaseRow { + id: string; + label: string; + /** The phase's position among the declared phases (0-based): its number and colour. */ + index: number; + steps: number; + /** Findings touching the phase, by severity (PR #14). */ + counts: IssueCounts; + /** The lane's box in world coordinates. */ + rect: { x: number; y: number; w: number; h: number }; +} + +export interface CoveredRect { + x: number; + y: number; + w: number; + h: number; +} + +/** + * The panel's narrowest and widest size, row and head heights, the pill's height and widest size, + * and the corner margin (CSS px). The panel grows from INDEX_W to fit its longest phase name, up to + * INDEX_MAX_W (M3 review, A11Y-M3-5: names were cut at 288 px on a 1440 px canvas). + */ +export const INDEX_W = 288; +export const INDEX_MAX_W = 360; +export const INDEX_HEAD_H = 26; +export const INDEX_ROW_H = 24; +export const INDEX_PAD_Y = 4; +export const PILL_H = 28; +export const PILL_MAX_W = 360; +export const INDEX_MARGIN = 12; +/** The gap between the pill and the rows it opens above itself. */ +export const POPOVER_GAP = 4; + +/** Which phases a viewport shows, and the one it shows most of. */ +export interface PhasesInView { + ids: string[]; + /** The phase with the largest visible area; null when none is visible. */ + current: string | null; +} + +/** + * The phases whose lanes intersect the canvas at this viewport, and the one the reader is looking + * at: the highest score of (share of the canvas the lane fills) + (share of the lane that is in + * view), the first of equals. Either share alone misleads: by area a tall lane wins while a short + * phase the reader just moved to is wholly on screen under it; by share a sliver of a small lane + * in a corner would win over the lane that fills the canvas. Pure. + */ +export function phasesInView(rows: readonly PhaseRow[], vp: Viewport, w: number, h: number): PhasesInView { + const ids: string[] = []; + let current: string | null = null; + let best = 0; + const canvas = Math.max(1, w * h); + for (const row of rows) { + const x0 = Math.max(0, row.rect.x * vp.zoom + vp.x); + const y0 = Math.max(0, row.rect.y * vp.zoom + vp.y); + const x1 = Math.min(w, (row.rect.x + row.rect.w) * vp.zoom + vp.x); + const y1 = Math.min(h, (row.rect.y + row.rect.h) * vp.zoom + vp.y); + const area = Math.max(0, x1 - x0) * Math.max(0, y1 - y0); + if (area <= 0) continue; + ids.push(row.id); + const lane = Math.max(1, row.rect.w * row.rect.h * vp.zoom * vp.zoom); + const score = area / canvas + area / lane; + if (score > best + 1e-9) { + best = score; + current = row.id; + } + } + return { ids, current }; +} + +/** "2 findings touch this phase", or '' when none does. */ +function findingsWords(counts: IssueCounts): string { + const total = countsTotal(counts); + if (!total) return ''; + return total + (total === 1 ? ' finding touches this phase' : ' findings touch this phase'); +} + +const steps = (n: number): string => n + (n === 1 ? ' step' : ' steps'); + +/** The highest phase number shown, the denominator of "4/6". */ +function lastNumber(rows: readonly PhaseRow[]): number { + let last = rows.length; + for (const row of rows) last = Math.max(last, row.index + 1); + return last; +} + +export interface PhaseIndexCallbacks { + /** A row was chosen: go to that phase. */ + go(id: string): void; + /** The wide panel was folded to its pill, or unfolded (saved per viewer). */ + fold(folded: boolean): void; +} + +export interface PhaseIndexMode { + /** Room for the panel: a panel 1000 px wide or wider and a canvas 350 px tall or taller. */ + roomy: boolean; + /** The reader folded the panel to its pill. */ + folded: boolean; + /** The reader hid the index (the ... menu). */ + hidden: boolean; + /** Something covers the whole canvas (the phase overview). */ + suppressed: boolean; +} + +export class PhaseIndex { + readonly root: HTMLElement; + private panel: HTMLElement; + private headSteps: HTMLElement; + private foldBtn: HTMLButtonElement; + private rowsEl: HTMLElement; + private pill: HTMLButtonElement; + private pillNum: HTMLElement; + private pillLabel: HTMLElement; + private cb: PhaseIndexCallbacks; + private rowsData: PhaseRow[] = []; + private rowEls = new Map(); + private mode: PhaseIndexMode = { roomy: true, folded: false, hidden: false, suppressed: false }; + private popover = false; + private inViewKey = ''; + private currentId: string | null = null; + private disposers: (() => void)[] = []; + + constructor(cb: PhaseIndexCallbacks) { + this.cb = cb; + this.root = el('nav', 'mlv-phaseindex'); + this.root.setAttribute('aria-label', 'Phase index'); + this.root.hidden = true; + + this.panel = add(this.root, el('div', 'mlv-phaseindex__panel')); + const head = add(this.panel, el('div', 'mlv-phaseindex__head')); + add(head, el('span', 'mlv-phaseindex__title', 'Phases')); + this.headSteps = add(head, el('span', 'mlv-phaseindex__total')); + this.foldBtn = add(head, el('button', 'mlv-btn mlv-btn--icon mlv-phaseindex__fold')) as HTMLButtonElement; + this.foldBtn.type = 'button'; + this.foldBtn.tabIndex = -1; + this.foldBtn.setAttribute('aria-label', 'Fold the phase index'); + this.foldBtn.title = 'Fold the phase index to one line'; + this.foldBtn.appendChild(uiIcon('chevron', 12)); + on(this.foldBtn, 'click', (ev: MouseEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + this.cb.fold(true); + }); + this.rowsEl = add(this.panel, el('ol', 'mlv-phaseindex__rows')); + + this.pill = add(this.root, el('button', 'mlv-phaseindex__pill')) as HTMLButtonElement; + this.pill.type = 'button'; + this.pill.tabIndex = -1; + this.pill.setAttribute('aria-expanded', 'false'); + this.pillNum = add(this.pill, el('span', 'mlv-phaseindex__pillnum')); + this.pillLabel = add(this.pill, el('span', 'mlv-phaseindex__pilllabel')); + const chevron = uiIcon('chevron', 12); + chevron.setAttribute('class', 'mlv-uicon mlv-phaseindex__pillchev'); + this.pill.appendChild(chevron); + on(this.pill, 'click', (ev: MouseEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + if (this.mode.roomy && this.mode.folded) this.cb.fold(false); + else this.setPopover(!this.popover); + }); + + // Enter and Space press the focused row or pill themselves; the canvas under the index must + // not also read them as "open the selection". A long index scrolls itself, not the diagram. + this.disposers.push(on(this.root, 'keydown', (ev: KeyboardEvent) => { + if (ev.key === 'Enter' || ev.key === ' ') ev.stopPropagation(); + })); + this.disposers.push(on(this.panel, 'wheel', (ev: WheelEvent) => ev.stopPropagation())); + const doc = typeof document === 'undefined' ? null : document; + if (doc) { + this.disposers.push(on(doc, 'pointerdown', (ev: PointerEvent) => { + if (!this.popover) return; + const target = ev.target as Node | null; + if (target && this.root.contains(target)) return; + this.setPopover(false); + })); + } + } + + /** The rows, as the canvas draws its lanes. */ + setRows(rows: PhaseRow[]): void { + this.rowsData = rows; + this.rowEls.clear(); + clear(this.rowsEl); + const total = rows.reduce((sum, row) => sum + row.steps, 0); + this.headSteps.textContent = steps(total); + const last = lastNumber(rows); + for (const row of rows) { + const li = add(this.rowsEl, el('li', 'mlv-phaseindex__item')); + const btn = add(li, el('button', 'mlv-phaseindex__row')) as HTMLButtonElement; + btn.type = 'button'; + btn.tabIndex = -1; + btn.setAttribute('data-phase-id', row.id); + stampPhase(btn, row.index); + add(btn, el('span', 'mlv-phaseindex__num', String(row.index + 1))).setAttribute('aria-hidden', 'true'); + // The full name on the label's own tooltip (it can still be cut short); the row's tooltip is + // the action, so a screen reader does not hear the name twice (M3 review, A11Y-M3-7). + const label = add(btn, el('span', 'mlv-phaseindex__label', row.label)); + label.title = 'Phase ' + (row.index + 1) + ': ' + row.label; + const cluster = severityCluster(row.counts, 11, findingsWords(row.counts)); + if (cluster) { + cluster.classList.add('mlv-phaseindex__sev'); + cluster.setAttribute('aria-hidden', 'true'); + cluster.title = findingsWords(row.counts) + ' (' + clusterTitle(row.counts) + ')'; + btn.appendChild(cluster); + } + add(btn, el('span', 'mlv-phaseindex__steps', steps(row.steps))).setAttribute('aria-hidden', 'true'); + btn.setAttribute('data-name', 'Phase ' + (row.index + 1) + ' of ' + last + ': ' + row.label + ', ' + steps(row.steps) + + (countsTotal(row.counts) ? ', ' + findingsWords(row.counts) + ' (' + clusterTitle(row.counts).replace(/^\d+ findings?: /, '') + ')' : '')); + btn.title = 'Go to this phase'; + on(btn, 'click', (ev: MouseEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + this.setPopover(false); + this.cb.go(row.id); + }); + this.rowEls.set(row.id, btn); + } + this.inViewKey = '\u0000'; + this.applyMode(); + } + + get rows(): readonly PhaseRow[] { + return this.rowsData; + } + + setMode(mode: Partial): void { + this.mode = { ...this.mode, ...mode }; + this.applyMode(); + } + + /** Whether the index has anything to show: two or more phases. */ + get available(): boolean { + return this.rowsData.length >= 2; + } + + /** The form on screen: the panel, the pill, or nothing. */ + get form(): 'list' | 'pill' | 'none' { + if (this.root.hidden) return 'none'; + return this.root.getAttribute('data-form') === 'list' ? 'list' : 'pill'; + } + + get popoverOpen(): boolean { + return this.popover && this.form === 'pill'; + } + + /** Close the pill's open rows; false when they were not open (an Escape rung). */ + closePopover(): boolean { + if (!this.popoverOpen) return false; + this.setPopover(false); + return true; + } + + private setPopover(open: boolean): void { + this.popover = open; + this.applyMode(); + } + + private applyMode(): void { + const m = this.mode; + const hidden = m.hidden || m.suppressed || !this.available; + this.root.hidden = hidden; + const list = m.roomy && !m.folded; + if (list || hidden) this.popover = false; + this.root.setAttribute('data-form', list ? 'list' : 'pill'); + if (this.popover) this.root.setAttribute('data-open', 'true'); + else this.root.removeAttribute('data-open'); + this.panel.hidden = !list && !this.popover; + this.pill.hidden = list; + this.pill.setAttribute('aria-expanded', this.popover ? 'true' : 'false'); + const unfold = m.roomy && m.folded; + this.pill.title = unfold ? 'Unfold the phase index' : this.popover ? 'Close the phase list' : 'Show every phase'; + this.syncPill(); + } + + /** Mark the phases this viewport shows; only a change of the set touches the DOM. */ + setInView(vp: Viewport, w: number, h: number): void { + const view = phasesInView(this.rowsData, vp, w, h); + const key = view.ids.join('\u0000') + '\u0001' + (view.current || ''); + if (key === this.inViewKey) return; + this.inViewKey = key; + this.currentId = view.current; + const shown = new Set(view.ids); + for (const [id, btn] of this.rowEls) { + const on_ = shown.has(id); + if (on_) btn.setAttribute('data-in-view', 'true'); + else btn.removeAttribute('data-in-view'); + btn.setAttribute('aria-label', (btn.getAttribute('data-name') || '') + (on_ ? ', in view' : '')); + } + this.syncPill(); + } + + /** The phase the viewport shows most of, or null. */ + currentPhase(): string | null { + return this.currentId; + } + + /** The phases marked in view, in drawn order (tests and the harness read it). */ + inView(): string[] { + const out: string[] = []; + for (const [id, btn] of this.rowEls) if (btn.getAttribute('data-in-view') === 'true') out.push(id); + return out; + } + + private syncPill(): void { + const rows = this.rowsData; + const last = lastNumber(rows); + const row = rows.find((r) => r.id === this.currentId) || null; + this.pillNum.textContent = row ? row.index + 1 + '/' + last : String(rows.length); + this.pillLabel.textContent = row ? row.label : rows.length === 1 ? 'phase' : 'phases'; + this.pill.setAttribute('aria-label', (row ? 'Phase ' + (row.index + 1) + ' of ' + last + ' in view: ' + row.label : rows.length + ' phases') + + '. ' + (this.mode.roomy && this.mode.folded ? 'Unfold the phase index.' : this.popover ? 'Close the phase list.' : 'Show every phase.')); + } + + /** + * The part of the canvas the index covers, in canvas pixels, or null when it is not shown. Read + * from the boxes on screen when the host lays them out; otherwise (jsdom, before layout) from the + * same sizes the stylesheet gives them, the panel and the pill at their widest. + */ + coveredRect(canvasW: number, canvasH: number, canvasLeft = 0, canvasTop = 0): CoveredRect | null { + if (this.root.hidden || !(canvasW > 0) || !(canvasH > 0)) return null; + const parts: CoveredRect[] = []; + for (const part of [this.panel, this.pill]) { + if (part.hidden) continue; + const r = part.getBoundingClientRect(); + if (r.width > 0 && r.height > 0) parts.push({ x: r.left - canvasLeft, y: r.top - canvasTop, w: r.width, h: r.height }); + } + if (!parts.length) { + const m = INDEX_MARGIN; + const listH = INDEX_HEAD_H + this.rowsData.length * INDEX_ROW_H + 2 * INDEX_PAD_Y; + if (this.root.getAttribute('data-form') === 'list') { + const w = Math.min(INDEX_MAX_W, canvasW - 2 * m); + const h = Math.min(listH, canvasH - 2 * m); + parts.push({ x: canvasW - m - w, y: canvasH - m - h, w, h }); + } else { + const w = Math.min(PILL_MAX_W, canvasW - 2 * m); + parts.push({ x: canvasW - m - w, y: canvasH - m - PILL_H, w, h: PILL_H }); + if (this.popover) { + const pw = Math.min(INDEX_MAX_W, canvasW - 2 * m); + const ph = Math.min(listH, Math.max(0, canvasH - 2 * m - PILL_H - POPOVER_GAP)); + parts.push({ x: canvasW - m - pw, y: canvasH - m - PILL_H - POPOVER_GAP - ph, w: pw, h: ph }); + } + } + } + const x = Math.min(...parts.map((p) => p.x)); + const y = Math.min(...parts.map((p) => p.y)); + return { x, y, w: Math.max(...parts.map((p) => p.x + p.w)) - x, h: Math.max(...parts.map((p) => p.y + p.h)) - y }; + } + + destroy(): void { + for (const dispose of this.disposers) { + try { + dispose(); + } catch (_e) { + /* already torn down */ + } + } + this.disposers = []; + } +} diff --git a/webview/src/render/phaseoverview.ts b/webview/src/render/phaseoverview.ts new file mode 100644 index 00000000..4b134905 --- /dev/null +++ b/webview/src/render/phaseoverview.ts @@ -0,0 +1,517 @@ +/** + * Viewer M3, roadmap step 13: the phase overview (Shift+0), as a PURE geometry. + * + * One block per phase, in the order the canvas draws its lanes: the phase's number and label, a + * line of counts, then its step titles in reading order (the Outline's and the review walk's + * order: a group before the steps it contains, depth first), each with its basis mark (◌ inferred, + * ? unresolved; an observed step has none) and the F labels of the findings on it. Neighbouring + * phases are joined by an arrow that says how many connections go from one to the next; a + * connection that skips a phase or goes back is drawn as a bracket on the right with its count + * (dashed when it goes back). + * + * Everything here is computed from the authored phases, steps, connections and findings (through + * `GraphIndex`) and the canvas size. Nothing is categorised for the author and nothing is called. + * It is drawn as an overlay over the canvas (`ui/overview.ts`), never inside the world layer, so + * the routed geometry and its golden do not move. + * + * COUNTS. Every step is listed or counted: a block that does not fit lists its first steps and + * ends with "… N more steps", so per block `listed + more = steps`, and the blocks sum to the + * document's step count. The connections are a partition too: inside a phase, from a phase to the + * next one (the arrows), and those that skip ahead or go back (the brackets) sum to the document's + * connection count. A finding touching two phases counts in each (the PR #14 rule, `laneCounts`), + * so the per-phase finding counts are labelled "touch this phase" and are not summed. + * + * FIT. Blocks are trimmed from the one with the most rows until the overview fits the canvas, but + * never below `MIN_SLOTS` title slots (two titles and the count). When even that does not + * fit, the overview scrolls (`height` is then larger than the canvas): beside the code at 541 px a + * six-phase document scrolls a little rather than showing phase names only. + */ + +import { normalizeSeverity } from '../markers.js'; +import type { GraphIndex, IssuePredicate } from '../layout/model.js'; +import type { IssueCounts, Severity } from '../types.js'; + +/* ── input ─────────────────────────────────────────────────────────────── */ + +export interface OverviewTag { + /** The finding's F label (`Issue.short`). */ + short: string; + /** The finding's real id, for the tooltip. */ + id: string; + severity: Severity; +} + +export interface OverviewStep { + id: string; + title: string; + /** `observed`, `inferred` or `unresolved` as authored ('' when absent). */ + basis: string; + /** 0 for a lane root, 1 inside a group, and so on. */ + depth: number; + /** The findings on this step that the severity toggles keep, in document order. */ + tags: OverviewTag[]; +} + +export interface OverviewPhase { + id: string; + label: string; + /** The phase's position among the declared phases (0-based): its number and its colour. */ + index: number; + steps: OverviewStep[]; + /** Findings touching this phase, by severity (PR #14: a finding on two phases counts in both). */ + findings: IssueCounts; + inferred: number; + unresolved: number; +} + +export interface OverviewLink { + /** Positions in `OverviewInput.phases` (drawn order), never equal. */ + from: number; + to: number; + count: number; +} + +export interface OverviewInput { + phases: OverviewPhase[]; + /** Connections between two different phases, one entry per ordered pair, in pair order. */ + links: OverviewLink[]; + /** Connections whose two ends are in the same phase. */ + inside: number; + /** Connections with an end the document lacks (validation refuses them; kept so the sum holds). */ + unplacedConnections: number; + /** Steps in no drawn phase (none today; kept so the sum holds). */ + unplacedSteps: number; + totalSteps: number; + totalConnections: number; +} + +/** The overview's data, from the index the canvas draws. `keep` is the severity toggles' filter. */ +export function overviewInput(index: GraphIndex, keep: IssuePredicate): OverviewInput { + const phases: OverviewPhase[] = []; + const position = new Map(); + for (const lane of index.lanes) { + position.set(lane.id, phases.length); + phases.push({ + id: lane.id, + label: lane.label || lane.id, + index: index.phaseIndexOf(lane.id), + steps: [], + findings: index.laneCounts(lane.id, keep), + inferred: 0, + unresolved: 0, + }); + } + const seen = new Set(); + let unplacedSteps = 0; + const add = (id: string, depth: number): void => { + if (seen.has(id)) return; + const node = index.nodeById.get(id); + if (!node) return; + seen.add(id); + const at = position.get(node.stage); + if (at === undefined) unplacedSteps++; + else { + const phase = phases[at]; + const basis = node.basis || ''; + if (basis === 'inferred') phase.inferred++; + else if (basis === 'unresolved') phase.unresolved++; + phase.steps.push({ + id, + title: node.label || node.qualname || id, + basis, + depth, + tags: index.issuesOf(id, keep).map((issue) => ({ short: issue.short || issue.id, id: issue.id, severity: normalizeSeverity(issue.severity) })), + }); + } + for (const child of index.laneChildren(id)) add(child, depth + 1); + }; + for (const lane of index.lanes) for (const root of index.roots(lane.id)) add(root, 0); + // A step the drawn hierarchy did not reach (none can, today) is still listed in its phase. + for (const node of index.graph.nodes || []) add(node.id, 0); + + const pairs = new Map(); + let inside = 0; + let unplacedConnections = 0; + for (const edge of index.graph.edges || []) { + const source = index.nodeById.get(edge.source); + const target = index.nodeById.get(edge.target); + const a = source ? position.get(source.stage) : undefined; + const b = target ? position.get(target.stage) : undefined; + if (a === undefined || b === undefined) unplacedConnections++; + else if (a === b) inside++; + else { + const key = a + '>' + b; + const link = pairs.get(key); + if (link) link.count++; + else pairs.set(key, { from: a, to: b, count: 1 }); + } + } + const links = Array.from(pairs.values()).sort((p, q) => p.from - q.from || p.to - q.to); + return { + phases, + links, + inside, + unplacedConnections, + unplacedSteps, + totalSteps: (index.graph.nodes || []).length, + totalConnections: (index.graph.edges || []).length, + }; +} + +/* ── wording ───────────────────────────────────────────────────────────── */ + +const plural = (n: number, one: string, many: string): string => n + ' ' + (n === 1 ? one : many); + +/** Whether a link is drawn as an arrow (to the next phase) rather than a bracket. */ +export function isNextLink(link: OverviewLink): boolean { + return link.to === link.from + 1; +} + +/** The connection partition: inside, to the next phase, skipping ahead or going back. */ +export function connectionPartition(input: OverviewInput): { inside: number; next: number; jumps: number; unplaced: number } { + let next = 0; + let jumps = 0; + for (const link of input.links) { + if (isNextLink(link)) next += link.count; + else jumps += link.count; + } + return { inside: input.inside, next, jumps, unplaced: input.unplacedConnections }; +} + +/** + * The header's count sentence, every count with its unit, the connections as a partition that + * sums to the total: "6 phases · 31 steps · 41 connections: 19 inside a phase, 8 to the next + * phase, 14 skip ahead or go back." + */ +export function overviewSummary(input: OverviewInput): string { + const part = connectionPartition(input); + const pieces = [plural(part.inside, 'inside a phase', 'inside a phase'), plural(part.next, 'to the next phase', 'to the next phase'), + plural(part.jumps, 'skips ahead or goes back', 'skip ahead or go back')]; + if (part.unplaced) pieces.push(plural(part.unplaced, 'with an end the document lacks', 'with an end the document lacks')); + return plural(input.phases.length, 'phase', 'phases') + ' · ' + plural(input.totalSteps, 'step', 'steps') + ' · ' + + plural(input.totalConnections, 'connection', 'connections') + ': ' + pieces.join(', ') + '.'; +} + +/** + * What the arrows, brackets and marks mean, under the counts; it says what a bracket's number counts + * (M3 review, A11Y-M3-8). One line at 900 px (at most 127 characters at SUMMARY_CHAR_W). Below + * 620 px a shorter key keeps the marks and the brackets explained on screen (the arrows carry their + * own "N connections" labels). + */ +export const OVERVIEW_KEY = 'Arrows go to the next phase; brackets count connections that skip ahead (solid) or back (dashed). ◌ inferred, ? unresolved.'; +export const OVERVIEW_KEY_NARROW = '◌ inferred, ? unresolved. Brackets count connections that skip ahead (solid) or go back (dashed).'; + +/** "9 steps (1 inferred, 3 unresolved)": the block's step count with its exceptions. */ +export function blockStepsText(phase: OverviewPhase): string { + const extra: string[] = []; + if (phase.inferred) extra.push(phase.inferred + ' inferred'); + if (phase.unresolved) extra.push(phase.unresolved + ' unresolved'); + return plural(phase.steps.length, 'step', 'steps') + (extra.length ? ' (' + extra.join(', ') + ')' : ''); +} + +/* ── geometry ──────────────────────────────────────────────────────────── */ + +/** Outer padding (CSS px); `PAD_NARROW` below `NARROW_W`. */ +export const PAD = 16; +export const PAD_NARROW = 12; +/** Below this canvas width the overview is one column of blocks with one column of titles. */ +export const NARROW_W = 620; +/** A block this wide or wider lists its titles in two columns and has a one-line head. */ +export const TWO_COLUMNS_MIN_W = 600; +/** The widest a block is drawn (wider canvases leave room on the right). */ +export const BLOCK_MAX_W = 980; +/** The head: number, label and counts on one line, or the counts on a second line. */ +export const HEAD_H = 32; +export const HEAD_H_TWO_LINES = 50; +/** One title row (12.5 px text on an 18 px line). */ +export const ROW_H = 18; +export const BLOCK_PAD_B = 8; +/** Inner padding left and right of the title columns, and the gap between the columns. */ +export const BLOCK_PAD_X = 12; +export const COLUMN_GAP = 16; +/** Indentation per group level, at most two levels. */ +export const INDENT = 12; +/** The vertical gap between two blocks, where the "N connections" arrow goes. */ +export const GAP = 26; +/** Brackets: the first slot's distance from the blocks, then one slot every SLOT_STEP. */ +export const BRACKET_INSET = 12; +export const SLOT_STEP = 12; +export const SLOT_STEP_MIN = 7; +/** Room after the last slot for its count. */ +export const BRACKET_TAIL = 14; +/** + * A block never shows fewer title slots than this (unless it has fewer steps): two titles and the + * "… N more steps" row, in as many rows as its columns need. Below that, the overview scrolls. + */ +export const MIN_SLOTS = 3; +/** The header: a title row, then the summary lines. */ +export const HEADER_TITLE_H = 24; +export const HEADER_LINE_H = 17; +export const HEADER_PAD = 8; +/** The space between the header and the first block. */ +export const HEADER_GAP = 12; +/** + * A conservative average character width of the 12 px summary text, so the estimated line count + * is never short (a line too many only leaves a gap; one too few would hide the counts). + */ +export const SUMMARY_CHAR_W = 6.8; + +export interface Rect { + x: number; + y: number; + w: number; + h: number; +} + +export interface OverviewItem { + /** A step's position in its phase's `steps`, or -1 for the "… N more steps" row. */ + step: number; + /** For the "more" row: how many steps it stands for. */ + more: number; + col: number; + row: number; + x: number; + y: number; + w: number; + /** Group depth, clamped to 2 (the item's x already includes the indentation). */ + depth: number; +} + +export interface OverviewBlock { + /** Position in `OverviewInput.phases`. */ + phase: number; + x: number; + y: number; + w: number; + h: number; + headH: number; + twoLineHead: boolean; + cols: number; + rows: number; + /** Rows the block would need to list every step. */ + rowsNeeded: number; + items: OverviewItem[]; + /** Steps listed by title. */ + listed: number; + /** Steps counted in the "… N more steps" row (0 when every step is listed). */ + more: number; +} + +export interface OverviewArrow { + from: number; + to: number; + count: number; + x: number; + y1: number; + y2: number; + labelX: number; + labelY: number; +} + +export interface OverviewBracket { + from: number; + to: number; + count: number; + /** It goes back (to an earlier phase): dashed. */ + back: boolean; + slot: number; + /** The blocks' right edge, where the bracket leaves and enters. */ + x0: number; + /** The slot's vertical line. */ + x: number; + y1: number; + y2: number; + labelX: number; + labelY: number; +} + +export interface OverviewLayout { + /** The canvas size the layout is for. */ + viewW: number; + viewH: number; + /** The content size; `height > viewH` means the overview scrolls. */ + width: number; + height: number; + scrolls: boolean; + /** One column of blocks with one column of titles (below NARROW_W). */ + narrow: boolean; + header: Rect & { lines: number; summary: string; key: string }; + blocks: OverviewBlock[]; + arrows: OverviewArrow[]; + brackets: OverviewBracket[]; + /** Some block lists fewer titles than it has steps. */ + trimmed: boolean; +} + +/** + * The overview for a canvas `w` x `h` (CSS px). Pure: the same input and size always give the same + * picture, so tests pin it at fixed sizes without a DOM. + */ +export function phaseOverviewLayout(input: OverviewInput, w: number, h: number): OverviewLayout { + const viewW = Math.max(1, Math.floor(w)); + const viewH = Math.max(1, Math.floor(h)); + const narrow = viewW < NARROW_W; + const pad = narrow ? PAD_NARROW : PAD; + const n = input.phases.length; + + // Brackets: every link that is not to the next phase, packed into as few slots as possible + // (two brackets share a slot when the phases they span do not overlap). + const jumps = input.links.filter((link) => !isNextLink(link)); + const order = jumps + .map((link, i) => ({ link, i })) + .sort((p, q) => span(p.link) - span(q.link) || Math.min(p.link.from, p.link.to) - Math.min(q.link.from, q.link.to) || p.i - q.i); + const slotOf = new Map(); + const slots: Array> = []; + for (const { link } of order) { + const lo = Math.min(link.from, link.to); + const hi = Math.max(link.from, link.to); + let s = 0; + while (s < slots.length && slots[s].some(([a, b]) => lo <= b && a <= hi)) s++; + if (s === slots.length) slots.push([]); + slots[s].push([lo, hi]); + slotOf.set(link, s); + } + const slotCount = slots.length; + const gutterMax = Math.max(60, Math.min(160, Math.round(viewW * 0.2))); + let step = SLOT_STEP; + if (slotCount && BRACKET_INSET + slotCount * step + BRACKET_TAIL > gutterMax) { + step = Math.max(SLOT_STEP_MIN, (gutterMax - BRACKET_INSET - BRACKET_TAIL) / slotCount); + } + const gutter = slotCount ? Math.ceil(BRACKET_INSET + (slotCount - 1) * step + BRACKET_TAIL) : 0; + + const blockW = Math.max(120, Math.min(BLOCK_MAX_W, viewW - 2 * pad - gutter)); + const twoColumns = !narrow && blockW >= TWO_COLUMNS_MIN_W; + const cols = twoColumns ? 2 : 1; + const twoLineHead = !twoColumns; + const headH = twoLineHead ? HEAD_H_TWO_LINES : HEAD_H; + + // The header: a title row, then the counts and the key (shorter when narrow), at an estimated + // line count. + const summary = overviewSummary(input); + const key = narrow ? OVERVIEW_KEY_NARROW : OVERVIEW_KEY; + const textW = Math.max(80, viewW - 2 * pad); + const linesFor = (text: string) => Math.max(1, Math.ceil((text.length * SUMMARY_CHAR_W) / textW)); + const lines = linesFor(summary) + (key ? linesFor(key) : 0); + const header = { x: pad, y: pad, w: viewW - 2 * pad, h: HEADER_TITLE_H + lines * HEADER_LINE_H + HEADER_PAD, lines, summary, key }; + + // Rows per block, then trim the block with the most rows until the whole thing fits. + const rowsNeeded = input.phases.map((phase) => Math.ceil(phase.steps.length / cols)); + const rows = rowsNeeded.slice(); + const minRows = input.phases.map((phase) => Math.ceil(Math.min(phase.steps.length, MIN_SLOTS) / cols)); + const blockH = (r: number) => headH + (r > 0 ? r * ROW_H : 0) + BLOCK_PAD_B; + const contentH = () => header.y + header.h + HEADER_GAP + rows.reduce((sum, r) => sum + blockH(r), 0) + GAP * Math.max(0, n - 1) + pad; + while (contentH() > viewH) { + let pick = -1; + for (let i = 0; i < n; i++) { + if (rows[i] <= minRows[i]) continue; + if (pick < 0 || rows[i] > rows[pick] || (rows[i] === rows[pick] && input.phases[i].steps.length >= input.phases[pick].steps.length)) pick = i; + } + if (pick < 0) break; + rows[pick]--; + } + + const blocks: OverviewBlock[] = []; + let y = header.y + header.h + HEADER_GAP; + const colW = (blockW - 2 * BLOCK_PAD_X - (cols - 1) * COLUMN_GAP) / cols; + input.phases.forEach((phase, i) => { + const r = rows[i]; + const slotsAvailable = r * cols; + const total = phase.steps.length; + const fits = slotsAvailable >= total; + const listed = fits ? total : Math.max(0, slotsAvailable - 1); + const more = total - listed; + const items: OverviewItem[] = []; + // Column-major, so reading order runs down the first column and on into the second. + for (let k = 0; k < listed + (more ? 1 : 0); k++) { + const col = Math.floor(k / Math.max(1, r)); + const row = k % Math.max(1, r); + const isMore = k === listed; + const depth = isMore ? 0 : Math.min(2, phase.steps[k].depth); + const x0 = pad + BLOCK_PAD_X + col * (colW + COLUMN_GAP); + items.push({ + step: isMore ? -1 : k, + more: isMore ? more : 0, + col, + row, + x: round1(x0 + depth * INDENT), + y: round1(y + headH + row * ROW_H), + w: round1(colW - depth * INDENT), + depth, + }); + } + const height = blockH(r); + blocks.push({ phase: i, x: pad, y: round1(y), w: round1(blockW), h: height, headH, twoLineHead, cols, rows: r, rowsNeeded: rowsNeeded[i], items, listed, more }); + y += height + GAP; + }); + + // Arrows between neighbours, in the gap under each block, at its left. + const arrows: OverviewArrow[] = []; + for (const link of input.links) { + if (!isNextLink(link)) continue; + const a = blocks[link.from]; + const b = blocks[link.to]; + if (!a || !b) continue; + const x = a.x + 22; + const y1 = a.y + a.h + 3; + const y2 = b.y - 3; + arrows.push({ from: link.from, to: link.to, count: link.count, x, y1: round1(y1), y2: round1(y2), labelX: x + 10, labelY: round1((y1 + y2) / 2) }); + } + + // Brackets: each block spreads its ends along its right edge, the ends to earlier phases on top. + const x0 = pad + blockW; + const ends = new Map>(); + for (const link of jumps) { + pushEnd(ends, link.from, { link, other: link.to }); + pushEnd(ends, link.to, { link, other: link.from }); + } + const endY = new Map(); + for (const [at, list] of ends) { + const block = blocks[at]; + if (!block) continue; + list.sort((p, q) => p.other - q.other || (slotOf.get(p.link) || 0) - (slotOf.get(q.link) || 0)); + const top = block.y + 10; + const usable = Math.max(1, block.h - 20); + list.forEach((end, j) => endY.set(endKey(end.link, at), round1(top + ((j + 1) * usable) / (list.length + 1)))); + } + const brackets: OverviewBracket[] = jumps.map((link) => { + const slot = slotOf.get(link) || 0; + const x = round1(x0 + BRACKET_INSET + slot * step); + const y1 = endY.get(endKey(link, link.from)) || 0; + const y2 = endY.get(endKey(link, link.to)) || 0; + return { from: link.from, to: link.to, count: link.count, back: link.to < link.from, slot, x0: round1(x0), x, y1, y2, labelX: x, labelY: round1((y1 + y2) / 2) }; + }); + + const height = Math.ceil(y - GAP + pad); + const width = Math.ceil(Math.min(viewW, x0 + gutter + pad)); + return { + viewW, + viewH, + width: Math.max(width, viewW), + height, + scrolls: height > viewH, + narrow, + header, + blocks, + arrows, + brackets, + trimmed: blocks.some((b) => b.more > 0), + }; +} + +function span(link: OverviewLink): number { + return Math.abs(link.to - link.from); +} + +function endKey(link: OverviewLink, at: number): string { + return link.from + '>' + link.to + '@' + at; +} + +function pushEnd(map: Map, key: number, value: V): void { + const list = map.get(key); + if (list) list.push(value); + else map.set(key, [value]); +} + +function round1(v: number): number { + return Math.round(v * 10) / 10; +} diff --git a/webview/src/render/scene.ts b/webview/src/render/scene.ts index cdddb35c..66dcc425 100644 --- a/webview/src/render/scene.ts +++ b/webview/src/render/scene.ts @@ -11,13 +11,10 @@ */ import { clear } from '../dom.js'; -import { highestSeverity } from '../markers.js'; import { buildBundle } from './bundles.js'; import { buildEdge } from './edges.js'; import { buildGroupBox, buildLane, buildNodeCard } from './nodes.js'; import { planScene, ScenePlan, ScenePlanOptions } from './plan.js'; -import type { GraphIndex, IssuePredicate } from '../layout/model.js'; -import type { LayoutFrame } from '../layout/layout.js'; import type { RoutedEdge } from '../layout/routing.js'; export interface SceneLayers { @@ -92,22 +89,3 @@ export function renderScene(layers: SceneLayers, opts: SceneOptions): SceneResul return { nodeEls, edgeEls, bundleEls, plan }; } - -/** Minimap dots: one per drawn box, coloured by stage and ringed by severity. */ -export function minimapDots(index: GraphIndex, frame: LayoutFrame, keep: IssuePredicate) { - const dots = []; - for (const box of frame.boxes.values()) { - const node = index.nodeById.get(box.id); - if (!node) continue; - dots.push({ - x: box.x, - y: box.y, - w: box.w, - h: box.h, - stage: node.stage, - phase: index.phaseIndexOf(node.stage), - severity: highestSeverity(index.subtreeCounts(box.id, keep)), - }); - } - return dots; -} diff --git a/webview/src/styles/chrome.css b/webview/src/styles/chrome.css index cf0e8a84..6b40dfa4 100644 --- a/webview/src/styles/chrome.css +++ b/webview/src/styles/chrome.css @@ -1,5 +1,6 @@ /* - * The header row, the ... menu, the search box and the status bar (viewer M2), then the minimap. + * The header row, the ... menu, the search box and the status bar (viewer M2). The phase index + * that replaced the minimap is in overview.css (viewer M3). * * The first of three `chrome*.css` layers: the toasts and the empty states * are in `chromestates.css`, the sheet and legend in `chromepanels.css`. @@ -456,23 +457,23 @@ font-size: var(--mlv-fs-11); } -/* Viewer M2 live fix: the overview map's item says why the map is not drawn, on a second line in +/* Viewer M2 live fix: the overview map's item said why the map was not drawn, on a second line in * the description colour, so the disabled item keeps readable text (no fade) and its check mark - * stays on the first line. */ -.mlv-moremenu__item[data-more-item="minimap"] { + * stays on the first line. Viewer M3: the phase index's item, which replaced it, does the same. */ +.mlv-moremenu__item[data-more-item="phaseindex"] { flex-wrap: wrap; row-gap: 0; } -.mlv-moremenu__item[data-more-item="minimap"]::after { +.mlv-moremenu__item[data-more-item="phaseindex"]::after { order: 2; } -.mlv-moremenu__item[data-more-item="minimap"][disabled] { +.mlv-moremenu__item[data-more-item="phaseindex"][disabled] { opacity: 1; } -.mlv-moremenu__item[data-more-item="minimap"][disabled] .mlv-moremenu__label, +.mlv-moremenu__item[data-more-item="phaseindex"][disabled] .mlv-moremenu__label, .mlv-moremenu__note { color: var(--mlv-text-2); } @@ -601,76 +602,3 @@ display: none; } -/* minimap */ -.mlv-minimap { - position: absolute; - right: var(--mlv-s5); - bottom: var(--mlv-s5); - z-index: 20; - width: 200px; - border: 1px solid var(--mlv-border-strong); - border-radius: var(--mlv-r-btn); - background: var(--mlv-surface); - box-shadow: var(--mlv-sh-2); - overflow: hidden; -} - -.mlv-minimap[hidden] { - display: none; -} - -/* collapsed: a 28 px chevron tab instead of 200x130 of occluded diagram. */ -.mlv-minimap.is-collapsed { - width: 28px; - height: 28px; -} - -.mlv-minimap.is-collapsed .mlv-minimap__svg { - display: none; -} - -.mlv-minimap__toggle { - position: absolute; - top: 2px; - right: 2px; - z-index: 1; - width: 24px; - height: 24px; - min-height: 0; - padding: 0; - border-radius: var(--mlv-r-btn); - background: var(--mlv-surface); - color: var(--mlv-text-2); -} - -.mlv-minimap__toggle .mlv-uicon { - transform: rotate(-90deg); -} - -.mlv-minimap.is-collapsed .mlv-minimap__toggle .mlv-uicon { - transform: rotate(90deg); -} - -.mlv-minimap__svg { - display: block; - width: 100%; - height: 130px; - cursor: pointer; -} - -.mlv-minimap__node { - fill: var(--mlv-stage, var(--mlv-stage-unknown)); - opacity: 0.75; -} - -.mlv-minimap__node--issue { - stroke: var(--mlv-sev, var(--mlv-sev-low)); - stroke-width: 1.5; -} - -.mlv-minimap__view { - fill: var(--mlv-accent); - fill-opacity: 0.12; - stroke: var(--mlv-accent); - stroke-width: 1; -} diff --git a/webview/src/styles/export.css b/webview/src/styles/export.css index 635a16d0..8e498794 100644 --- a/webview/src/styles/export.css +++ b/webview/src/styles/export.css @@ -3,7 +3,7 @@ * * `grep '@media print' webview/src` found * nothing before this file, so Ctrl+P gave the current VIEWPORT at the current - * ZOOM with the fixed toolbar, rail, minimap and zoom cluster painted over it — + * ZOOM with the fixed toolbar, rail, minimap (since viewer M3 the phase index) and zoom cluster painted over it — * a page of chrome and a corner of diagram. Here the chrome is removed, the * page-fill chain that clips the canvas is released, and the canvas transform * is dropped so the world prints at the natural size the layout already gave it @@ -23,9 +23,11 @@ .mlv-header, .mlv-workflow, .mlv-hostnotice, + .mlv-walkbar, .mlv-status, .mlv-rail, - .mlv-minimap, + .mlv-phaseindex, + .mlv-overview, .mlv-zoom, .mlv-skiplink, .mlv-legend, diff --git a/webview/src/styles/overview.css b/webview/src/styles/overview.css new file mode 100644 index 00000000..d4a71453 --- /dev/null +++ b/webview/src/styles/overview.css @@ -0,0 +1,582 @@ +/* + * Viewer M3, roadmap step 13: the phase index (it replaced the minimap) and the phase overview. + * + * Both are overlays inside the canvas and outside the world layer, so neither moves the routed + * picture. Sizes here are the ones `render/phaseindex.ts` (INDEX_W, INDEX_MAX_W, INDEX_HEAD_H, INDEX_ROW_H, + * PILL_H, INDEX_MARGIN) and `render/phaseoverview.ts` (HEAD_H, ROW_H, …) compute with; change them + * together. Colours are tokens only: the phase tone (`--mlv-stage`, bound from `data-phase-tone` + * in node.css) on the number and the block's left rule, severity on the glyphs, the accent on + * what is in view. No motion here: the move to a phase is the viewport's (240 ms, none under + * reduced motion). + */ + +/* ── the phase index ───────────────────────────────────────────────────── */ + +.mlv-phaseindex { + position: absolute; + right: 12px; + bottom: 12px; + z-index: 20; + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 4px; + max-width: calc(100% - 24px); + max-height: calc(100% - 24px); + pointer-events: none; +} + +.mlv-phaseindex[hidden] { + display: none; +} + +.mlv-phaseindex__panel, +.mlv-phaseindex__pill { + pointer-events: auto; +} + +.mlv-phaseindex__panel { + display: flex; + flex-direction: column; + /* From 288 px, growing to fit the longest phase name up to 360 px (INDEX_W, INDEX_MAX_W). */ + width: max-content; + min-width: min(288px, 100%); + max-width: min(360px, 100%); + min-height: 0; + padding: 4px 0; + border: 1px solid var(--mlv-border-strong); + border-radius: var(--mlv-r-btn); + background: var(--mlv-surface); + box-shadow: var(--mlv-sh-2); + overflow: hidden; +} + +.mlv-phaseindex__panel[hidden] { + display: none; +} + +.mlv-phaseindex__head { + display: flex; + align-items: center; + gap: var(--mlv-s3); + flex: none; + height: 26px; + padding: 0 4px 0 10px; + color: var(--mlv-text-2); + font-size: var(--mlv-fs-11); +} + +.mlv-phaseindex__title { + color: var(--mlv-text); + font-weight: var(--mlv-fw-semi); +} + +.mlv-phaseindex__total { + flex: 1 1 auto; + font-variant-numeric: tabular-nums; +} + +.mlv-phaseindex__fold { + flex: none; + width: 22px; + height: 22px; + min-height: 0; + padding: 0; + color: var(--mlv-text-2); +} + +/* The chevron points down: fold the panel to its line. */ +.mlv-phaseindex__fold .mlv-uicon { + transform: rotate(90deg); +} + +.mlv-phaseindex__rows { + flex: 1 1 auto; + min-height: 0; + margin: 0; + padding: 0; + overflow-y: auto; + list-style: none; +} + +.mlv-phaseindex__row { + display: flex; + align-items: center; + gap: var(--mlv-s3); + width: 100%; + height: 24px; + padding: 0 10px 0 7px; + border: 0; + border-left: 3px solid transparent; + background: none; + color: var(--mlv-text); + font: inherit; + font-size: var(--mlv-fs-12); + text-align: left; + cursor: pointer; +} + +.mlv-phaseindex__row:hover { + background: var(--mlv-surface-2); +} + +.mlv-phaseindex__row:focus-visible { + outline: 1px solid var(--mlv-focus); + outline-offset: -1px; +} + +/* In view: an accent rule and a faint accent wash, never colour alone (the name says "in view"). */ +.mlv-phaseindex__row[data-in-view="true"] { + border-left-color: var(--mlv-accent); + background: color-mix(in srgb, var(--mlv-accent) 12%, transparent); +} + +.mlv-phaseindex__num, +.mlv-phaseindex__pillnum { + flex: none; + color: var(--mlv-text-2); + font-weight: var(--mlv-fw-semi); + font-variant-numeric: tabular-nums; +} + +.mlv-phaseindex__num { + min-width: 14px; +} + +/* The phase's colour, as the lane's swatch: a small square before the number. */ +.mlv-phaseindex__row::before { + content: ""; + flex: none; + width: 8px; + height: 8px; + border-radius: 2px; + background: var(--mlv-stage, var(--mlv-stage-unknown)); +} + +.mlv-phaseindex__label { + flex: 1 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mlv-phaseindex__sev { + flex: none; +} + +.mlv-phaseindex__steps { + flex: none; + color: var(--mlv-text-2); + font-size: var(--mlv-fs-11); + font-variant-numeric: tabular-nums; +} + +/* The pill: the phase most in view, "4/6 Objective, optimizer & scheduler ▴". */ +.mlv-phaseindex__pill { + display: flex; + align-items: center; + gap: var(--mlv-s3); + max-width: min(360px, 100%); + height: 28px; + padding: 0 8px 0 10px; + border: 1px solid var(--mlv-border-strong); + border-radius: 14px; + background: var(--mlv-surface); + box-shadow: var(--mlv-sh-2); + color: var(--mlv-text); + font: inherit; + font-size: var(--mlv-fs-12); + cursor: pointer; +} + +.mlv-phaseindex__pill[hidden] { + display: none; +} + +.mlv-phaseindex__pill:hover { + background: var(--mlv-surface-2); +} + +.mlv-phaseindex__pill:focus-visible { + outline: 1px solid var(--mlv-focus); + outline-offset: 1px; +} + +.mlv-phaseindex__pilllabel { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* ▴: the rows open above the pill (or unfold the panel). */ +.mlv-phaseindex__pillchev { + flex: none; + color: var(--mlv-text-2); + transform: rotate(-90deg); +} + +.mlv-phaseindex[data-open="true"] .mlv-phaseindex__pillchev { + transform: rotate(90deg); +} + +/* The pill's open rows sit above it, inside the canvas (so above the bottom sheet). */ +.mlv-phaseindex[data-form="pill"] .mlv-phaseindex__panel { + max-height: calc(100% - 32px); +} + +.mlv-phaseindex[data-form="pill"] .mlv-phaseindex__fold { + display: none; +} + +/* ── the phase overview ────────────────────────────────────────────────── */ + +.mlv-overview { + position: absolute; + inset: 0; + z-index: 35; + overflow: auto; + overscroll-behavior: contain; + touch-action: pan-x pan-y; + background: var(--mlv-bg); + cursor: default; +} + +.mlv-overview[hidden] { + display: none; +} + +.mlv-overview__content { + position: relative; +} + +/* + * The header is the content's first box (ui/overview.ts sets its size and padding from the layout). + * M3 live check, W4: while the overview scrolls it sticks to the overlay's top, on the overlay's + * own background and above the blocks, links and counts that scroll under it, so Back, the counts + * and the key stay on screen. A header taller than half the overlay scrolls with the blocks. + */ +.mlv-overview__header { + position: relative; + z-index: 2; + box-sizing: border-box; + background: var(--mlv-bg); +} + +.mlv-overview__header[data-sticky="true"] { + position: sticky; + top: 0; + box-shadow: 0 1px 0 var(--mlv-border); +} + +.mlv-overview__top { + display: flex; + align-items: center; + gap: var(--mlv-s4); + height: 24px; +} + +.mlv-overview__title { + flex: 1 1 auto; + min-width: 0; + margin: 0; + overflow: hidden; + color: var(--mlv-text); + font-size: var(--mlv-fs-13); + font-weight: var(--mlv-fw-semi); + text-overflow: ellipsis; + white-space: nowrap; +} + +.mlv-overview__back { + display: inline-flex; + align-items: center; + gap: var(--mlv-s2); + flex: none; + height: 22px; + min-height: 0; + padding: 0 6px; + color: var(--mlv-text); + font-size: var(--mlv-fs-12); +} + +.mlv-overview__kbd { + padding: 0 4px; + border: 1px solid var(--mlv-border-strong); + border-radius: 3px; + color: var(--mlv-text-2); + font-family: inherit; + font-size: var(--mlv-fs-10); +} + +.mlv-overview__summary, +.mlv-overview__key { + margin: 0; + color: var(--mlv-text-2); + font-size: var(--mlv-fs-12); + line-height: 17px; +} + +.mlv-overview__summary { + color: var(--mlv-text); +} + +.mlv-overview__links { + position: absolute; + top: 0; + left: 0; + overflow: visible; + pointer-events: none; +} + +.mlv-overview__arrow, +.mlv-overview__bracket { + fill: none; + stroke: var(--mlv-edge, var(--mlv-text-2)); + stroke-width: 1.5; +} + +.mlv-overview__arrow { + stroke-width: 2; +} + +.mlv-overview__bracket[data-back="true"] { + stroke-dasharray: 5 3; +} + +.mlv-overview__head { + fill: var(--mlv-edge, var(--mlv-text-2)); +} + +.mlv-overview__arrowlabel { + position: absolute; + transform: translateY(-50%); + color: var(--mlv-text-2); + font-size: var(--mlv-fs-12); + white-space: nowrap; + pointer-events: none; +} + +/* A bracket's count, on a plate so it reads over the lines beside it. */ +.mlv-overview__count { + position: absolute; + min-width: 14px; + padding: 0 3px; + border: 1px solid var(--mlv-border-strong); + border-radius: 7px; + background: var(--mlv-bg); + color: var(--mlv-text); + font-size: var(--mlv-fs-10); + line-height: 13px; + text-align: center; + font-variant-numeric: tabular-nums; + transform: translate(-50%, -50%); +} + +.mlv-overview__count[data-back="true"] { + border-style: dashed; +} + +.mlv-overview__blocks, +.mlv-overview__item { + margin: 0; + padding: 0; + list-style: none; +} + +.mlv-ovblock { + position: absolute; + box-sizing: border-box; + overflow: hidden; + border: 1px solid color-mix(in srgb, var(--mlv-stage, var(--mlv-text-2)) 45%, var(--mlv-border-strong)); + border-radius: 8px; + background: color-mix(in srgb, var(--mlv-stage, var(--mlv-text-2)) 7%, var(--mlv-surface)); + cursor: pointer; +} + +/* The phase's colour as the lane's left rule. */ +.mlv-ovblock::before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 4px; + background: var(--mlv-stage, var(--mlv-stage-unknown)); +} + +.mlv-ovblock:hover { + border-color: var(--mlv-stage, var(--mlv-border-strong)); +} + +.mlv-ovblock:focus-visible, +.mlv-ovblock:focus { + outline: 2px solid var(--mlv-focus); + outline-offset: 1px; +} + +.mlv-ovblock__head { + display: flex; + align-items: center; + gap: var(--mlv-s5); + box-sizing: border-box; + padding: 0 12px 0 16px; +} + +.mlv-ovblock[data-head="two-lines"] .mlv-ovblock__head { + flex-direction: column; + align-items: stretch; + justify-content: center; + gap: 2px; +} + +.mlv-ovblock[data-head="two-lines"] .mlv-ovblock__name { + flex: none; +} + +.mlv-ovblock__name { + display: flex; + align-items: baseline; + gap: var(--mlv-s3); + flex: 1 1 auto; + min-width: 0; +} + +.mlv-ovblock__num { + flex: none; + color: var(--mlv-text-2); + font-size: 15px; + font-weight: var(--mlv-fw-semi); + font-variant-numeric: tabular-nums; +} + +.mlv-ovblock__label { + min-width: 0; + overflow: hidden; + color: var(--mlv-text); + font-size: 15px; + font-weight: var(--mlv-fw-semi); + line-height: 20px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mlv-ovblock__meta { + display: flex; + align-items: center; + gap: var(--mlv-s3); + flex: 0 1 auto; + min-width: 0; + overflow: hidden; + color: var(--mlv-text-2); + font-size: var(--mlv-fs-12); + line-height: 18px; + white-space: nowrap; +} + +.mlv-ovblock__steps { + flex: none; +} + +.mlv-ovblock__meta .mlv-cluster { + flex: none; +} + +.mlv-ovblock__unit { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; +} + +.mlv-ovblock__unit::before { + content: "· "; +} + +.mlv-ovblock__list { + margin: 0; + padding: 0; + list-style: none; +} + +.mlv-ovitem { + position: absolute; + display: flex; + align-items: center; + gap: 2px; + box-sizing: border-box; + height: 18px; + color: var(--mlv-text); + font-size: 12.5px; + line-height: 18px; + white-space: nowrap; +} + +/* The basis mark: nothing for observed (the calm canvas rule), ◌ inferred, ? unresolved. */ +.mlv-ovitem__mark { + flex: none; + width: 14px; + color: var(--mlv-text-2); + text-align: center; +} + +.mlv-ovitem[data-basis] .mlv-ovitem__mark { + color: var(--mlv-text); + font-weight: var(--mlv-fw-semi); +} + +/* ◌ drawn as a dotted ring: macOS system fonts show nothing for the character. */ +.mlv-ovmark { + display: inline-block; + box-sizing: border-box; + width: 9px; + height: 9px; + border: 1.5px dotted currentColor; + border-radius: 50%; + vertical-align: -1px; +} + +.mlv-overview__key .mlv-ovmark { + margin-right: 3px; +} + +.mlv-ovitem__title { + flex: 0 1 auto; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; +} + +.mlv-ovitem__tags { + display: inline-flex; + align-items: center; + gap: var(--mlv-s2); + flex: none; + margin-left: var(--mlv-s3); +} + +.mlv-ovitem__tag { + display: inline-flex; + align-items: center; + gap: 1px; + font-size: var(--mlv-fs-11); + font-weight: var(--mlv-fw-semi); + font-variant-numeric: tabular-nums; +} + +.mlv-ovitem--more { + padding-left: 16px; + color: var(--mlv-text-2); +} + +/* High contrast: no tints; the contrast border carries the shapes. */ +.mlv-root[data-theme="hc"] .mlv-ovblock { + border-color: var(--vscode-contrastBorder, var(--mlv-border-strong)); + background: var(--mlv-surface); +} + +.mlv-root[data-theme="hc"] .mlv-phaseindex__row[data-in-view="true"] { + background: none; + outline: 1px dashed var(--mlv-accent); + outline-offset: -2px; +} diff --git a/webview/src/styles/workflow.css b/webview/src/styles/workflow.css index b249db97..b89add5e 100644 --- a/webview/src/styles/workflow.css +++ b/webview/src/styles/workflow.css @@ -156,10 +156,160 @@ min-height: min(320px, 50vh); } -/* Issue 6: a canvas too short for the minimap to summarise anything (canvasview.ts). Viewer M2 - * live fix: a panel 900 px wide or narrower has no room for it beside the diagram either; the App - * sets `.is-narrow` from the panel's width (it was an @media rule), so the ... menu says why. */ -.mlv-minimap.is-short, -.mlv-minimap.is-narrow { - display: none; +/* + * Viewer M3: the review walk's bar, a band at the foot of the diagram column, shown only while the + * walk runs: directly above the bottom sheet's tabs, or with the side panel (1260 px and wider) + * along the bottom of the diagram. It is after the canvas in the document, so the canvas stays + * within four Tab presses of the top and Tab goes canvas, walk controls, sheet. Wide: the place and the filters, the key hint and Exit on one + * row, the editor line under them. Narrow (data-layout="narrow", under 620 px): "3/16", Previous + * and Next, the filters and Exit on one row; below about 420 px the filters wrap onto a second row + * between Next and Exit rather than scroll sideways (two rows at 320 px). Plain text + * colours; colour only marks a quote the host did not open. No motion. + */ +.mlv-walkbar { + flex: none; + display: flex; + flex-direction: column; + gap: 2px; + padding: 4px var(--mlv-s4) 5px var(--mlv-s5); + border-top: 1px solid var(--mlv-border); + background: var(--mlv-surface-2); + color: var(--mlv-text); + font-size: var(--mlv-fs-12); + line-height: 1.4; +} +.mlv-walkbar[hidden] { display: none; } +.mlv-walkbar__row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--mlv-s2) var(--mlv-s4); + min-width: 0; +} +.mlv-walkbar__pos { + display: inline-flex; + flex: none; + align-items: center; + gap: 4px; + margin: 0; + font-weight: var(--mlv-fw-semi); + font-variant-numeric: tabular-nums; + white-space: nowrap; +} +.mlv-walkbar__mark { display: inline-flex; color: var(--mlv-stale-ink); } +.mlv-walkbar__mark[hidden] { display: none; } +.mlv-walkbar__tools { + display: flex; + flex: 1 1 auto; + flex-wrap: wrap; + align-items: center; + gap: var(--mlv-s2) var(--mlv-s4); + min-width: 0; +} +/* Previous and Next (M3 review, A11Y-M3-2): the claim before or after, as k and j; chevrons up and down. */ +.mlv-walkbar__steps { display: inline-flex; flex: none; gap: 2px; } +.mlv-walkbar__step { + width: 24px; + min-width: 0; + height: 22px; + min-height: 0; + padding: 0; +} +.mlv-walkbar__step--prev .mlv-uicon { transform: rotate(-90deg); } +.mlv-walkbar__step--next .mlv-uicon { transform: rotate(90deg); } +.mlv-walkbar__filters { display: inline-flex; flex-wrap: wrap; gap: var(--mlv-s2); } +.mlv-walkbar__filter { + display: inline-flex; + align-items: center; + gap: 5px; + height: 22px; + padding: 0 var(--mlv-s3); + border: 1px solid var(--mlv-border); + border-radius: var(--mlv-r-chip); + background: var(--mlv-surface); + color: var(--mlv-text); + font-size: var(--mlv-fs-12); + white-space: nowrap; + cursor: pointer; +} +.mlv-walkbar__filter[hidden] { display: none; } +.mlv-walkbar__filter:hover { border-color: var(--mlv-border-strong); } +/* The chosen filter: a stronger border and weight, not colour alone. */ +.mlv-walkbar__filter[aria-pressed="true"] { + border-color: var(--mlv-text-2); + font-weight: var(--mlv-fw-semi); +} +.mlv-walkbar__fcount { color: var(--mlv-text-2); font-variant-numeric: tabular-nums; } +.mlv-walkbar__filter[aria-pressed="true"] .mlv-walkbar__fcount { color: var(--mlv-text); } +.mlv-walkbar__keys { + display: inline-flex; + flex-wrap: wrap; + gap: var(--mlv-s2) var(--mlv-s4); + color: var(--mlv-text-2); + font-size: var(--mlv-fs-11); +} +.mlv-walkbar__keys[hidden] { display: none; } +.mlv-walkbar__key { display: inline-flex; align-items: center; gap: 2px; white-space: nowrap; } +.mlv-walkbar__exit { margin-left: auto; min-height: 22px; padding: 0 var(--mlv-s3); } +.mlv-walkbar__editor { + display: flex; + align-items: flex-start; + gap: 5px; + margin: 0; + color: var(--mlv-text-2); + font-size: var(--mlv-fs-11); + overflow-wrap: anywhere; +} +.mlv-walkbar__editor[hidden] { display: none; } +.mlv-walkbar__editor[data-walk-status="blocked"], +.mlv-walkbar__editor[data-walk-status="failed"] { color: var(--mlv-text); } +.mlv-walkbar__editoricon { display: inline-flex; flex: none; padding-top: 1px; color: var(--mlv-stale-ink); } +.mlv-walkbar__editoricon[hidden] { display: none; } +.mlv-walkbar[data-layout="narrow"] { padding: 3px var(--mlv-s3); } +/* + * Narrow, the controls share the place's row and the filters take the room between Previous and + * Next and Exit, wrapping inside it. At 320 px (the documented floor) the bar was four rows, 102 px: + * the place, then Previous and Next, then the filters, then Exit alone. Now it is two rows, in the + * same order for the eye and for Tab. + */ +.mlv-walkbar[data-layout="narrow"] .mlv-walkbar__tools { flex: 1 1 0; } +.mlv-walkbar[data-layout="narrow"] .mlv-walkbar__filters { flex: 1 1 0; min-width: 0; } +.mlv-walkbar[data-layout="narrow"] .mlv-walkbar__exitlabel { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip: rect(0 0 0 0); +} + +/* The header's Review button: an icon and a word; pressed while the walk runs. */ +.mlv-header__review { + flex: none; + height: 24px; + min-height: 0; + padding: 0 var(--mlv-s3); + gap: 5px; +} +.mlv-header__review[aria-pressed="true"] { border-color: var(--mlv-text-2); } + +/* + * The walk's quote in the Selection pane: a focus-coloured left rule (the line the editor beside + * is asked for) and one line under its head saying what the editor shows, or why nothing was + * opened. Not colour alone: the line says it in words. + */ +.mlv-quote.is-walk { + margin-left: calc(-1 * var(--mlv-s3)); + padding-left: calc(var(--mlv-s3) - 2px); + border-left: 2px solid var(--mlv-focus); +} +.mlv-quote__walk { + display: flex; + align-items: flex-start; + gap: 5px; + margin: 3px 0 0 calc(1.4em + var(--mlv-s3)); + color: var(--mlv-text-2); + font-size: var(--mlv-fs-11); } +.mlv-quote.is-walk[data-walk-status="blocked"] .mlv-quote__walk, +.mlv-quote.is-walk[data-walk-status="failed"] .mlv-quote__walk { color: var(--mlv-text); } +.mlv-quote__walk .mlv-uicon { flex: none; margin-top: 1px; color: var(--mlv-stale-ink); } diff --git a/webview/src/types.ts b/webview/src/types.ts index 172f370c..f0a43e12 100644 --- a/webview/src/types.ts +++ b/webview/src/types.ts @@ -232,9 +232,17 @@ export interface ViewState { * it, and a new revision opens on About. */ railTab: RailTab; - /** Optional: the minimap's collapsed tab survives a reload the way `collapsed` does. */ + /** + * Viewer M3: the phase index folded to its pill, or hidden (the ... menu). Absent at its default + * (shown, unfolded). + */ + phaseIndex?: 'folded' | 'hidden'; + /** + * Written before viewer M3, when the minimap was collapsed to its tab. No longer written; a saved + * `true` with no `phaseIndex` opens the phase index folded. + */ minimapCollapsed?: boolean; - /** Optional: flow animation on/off, like `minimapCollapsed`. Absent = on. */ + /** Optional: flow animation on/off. Absent = on. */ flow?: boolean; /** Optional: the legend panel's open state, remembered per viewer (VIEW-10). */ legendOpen?: boolean; @@ -257,6 +265,23 @@ export interface ViewState { * `railTab` and, since the same fix, `selection`. */ sheetOpen?: boolean; + /** + * Viewer M3: the review walk's place for `workflowRevision`: its filter, the claim it is on, the + * quote of that claim, and whether it was running. Absent until the walk has been started, and + * restored on a remount only when `workflowRevision` matches; another revision starts fresh. It + * records no verdict and no "checked" mark, only the position. + */ + walk?: WalkViewState; +} + +/** Viewer M3: the review walk's saved place (`ViewState.walk`). */ +export interface WalkViewState { + filter: 'notObserved' | 'findings' | 'changed' | 'all'; + claim: Sel; + /** The claim's quote (0-based) the walk last showed; absent at 0. */ + quote?: number; + /** The walk was running; absent when it was not. */ + active?: boolean; } /** A Refine composer's reader-visible state. */ @@ -282,22 +307,36 @@ export interface Capabilities { export type RefineIntent = 'explain' | 'expand' | 'challenge' | 'trace' | 'custom'; /** The requests that carry a `requestId` and are answered by one `actionResult`. */ -export type ResultAction = 'exportFile' | 'copy' | 'refineWorkflow'; +export type ResultAction = 'exportFile' | 'copy' | 'refineWorkflow' | 'openLocation'; /** - * The host's single answer to an `exportFile`, `copy` or `refineWorkflow` - * request that carried a valid `requestId` (§1e). `message` is host-authored + * Why the host did not open a cited range (viewer M3): a stale reason, `elsewhere` (the root + * hint), unsaved text that lost the cited lines, a removed notebook cell, a file that could not be + * checked, or evidence the displayed revision does not have. + */ +export type OpenBlockReason = 'changed' | 'missing' | 'unreadable' | 'too-large' | 'elsewhere' | 'unsaved' | 'cell-missing' | 'unchecked' | 'unknown'; + +/** + * The host's single answer to an `exportFile`, `copy`, `refineWorkflow` or (viewer M3) + * `openLocation` request that carried a valid `requestId` (§1e). `message` is host-authored * and never contains an absolute path; `name` is the saved basename and is sent * only for a completed export. + * + * For `openLocation`: `done` (shown beside the panel), `blocked` (not opened; `reason` and a short + * `message` such as "train.py changed after revision r3 was published; not opened."), `cancelled` + * (a later open, a new revision or a freshness change overtook it) or `failed` (VS Code could not + * show the file). `seq` repeats the request's own `seq` when it had one. */ export interface ActionResult { v: 1; type: 'actionResult'; requestId: string; action: ResultAction; - outcome: 'done' | 'cancelled' | 'failed'; + outcome: 'done' | 'cancelled' | 'failed' | 'blocked'; message?: string; name?: string; + reason?: OpenBlockReason; + seq?: number; } /** @@ -316,9 +355,17 @@ export interface StaleFile { export type HostToUi = | { v: 1; type: 'init'; theme: ThemeKind; capabilities: Capabilities; artifact?: string } | { v: 1; type: 'theme'; kind: ThemeKind } - /** Not sent by the host today; kept for the planned "Reveal in Diagram" (M3). */ + /** + * Viewer M3 (step 14), MLView: Reveal in Diagram: the reader chose this claim from the code in the + * editor, and the host moved the keyboard focus to this panel. The viewer selects it, brings it + * into view (above the bottom sheet, clear of the phase index), shows it in the Selection tab and + * puts the keyboard on it. `issue` is a finding. A frame with another kind, or an id the displayed + * revision lacks, changes nothing. + */ + | { v: 1; type: 'reveal'; kind: 'node' | 'edge' | 'issue'; id: string } + /** The host sends `reveal`; these older single-kind frames are still answered the same way. */ | { v: 1; type: 'revealNode'; nodeId: string; center?: boolean } - /** Not sent by the host today; kept for the planned "Reveal in Diagram" (M3). */ + /** The host sends `reveal`; these older single-kind frames are still answered the same way. */ | { v: 1; type: 'revealIssue'; issueId: string } /** * Viewer M1. The displayed revision's stale files, each with its reason, posted after the @@ -342,8 +389,23 @@ export type UiToHost = /** * `focus: true` (viewer M1) is the explicit open-and-focus gesture (Alt+Enter, Alt+click); every * other open keeps the keyboard on the diagram. + * + * Viewer M3, for the review walk (the host side; the walk debounces its opens about 150 ms): + * `seq` is a positive integer that increases with every numbered open of a page (the host drops + * an open whose `seq` is not above the last one it saw, and one a later open overtook); + * `requestId` asks for one `actionResult`; `walk: true` marks an open from the walk, which never + * raises a VS Code notification when it is blocked (the reason comes back in the result + * instead); `highlight: false` selects the range without the whole-range decoration. + */ + | { v: 1; type: 'openLocation'; file: string; absFile: string; line: number; col: number; endLine: number; endCol: number; preview?: boolean; evidenceId?: string; cell?: number; focus?: boolean; seq?: number; requestId?: string; walk?: boolean; highlight?: boolean } + /** + * Viewer M3: the review walk ended (`end`), or it moved to a claim it opens nothing for (`clear`: + * a claim with no quote, or one it only selected). Either way the host clears the cited-range + * highlight, collapses the selection the walk set if the editor still has it, and drops a walk + * open still on its way, so the editor never shows an earlier claim's lines as if they were this + * one's. */ - | { v: 1; type: 'openLocation'; file: string; absFile: string; line: number; col: number; endLine: number; endCol: number; preview?: boolean; evidenceId?: string; cell?: number; focus?: boolean } + | { v: 1; type: 'walk'; state: 'end' | 'clear' } /** Viewer M1: the workspace-root hint's two actions. The host owns the folder. */ | { v: 1; type: 'workspaceHint'; action: 'add' | 'open' } /** diff --git a/webview/src/ui/appkeys.ts b/webview/src/ui/appkeys.ts index ca19a447..fe4af378 100644 --- a/webview/src/ui/appkeys.ts +++ b/webview/src/ui/appkeys.ts @@ -9,7 +9,9 @@ * nothing else calls them: * * - THE Escape cascade, in this exact order — shortcut sheet -> the Refine… - * popover -> legend -> the open bottom panel (viewer M2) -> focus mode -> selection -> blur. + * popover -> the phase index's open list (viewer M3) -> legend -> the phase overview (viewer + * M3) -> the review walk (viewer M3) -> the open bottom panel (viewer M2) -> focus mode -> + * selection -> blur. * One owner writes that order ONCE * (CONTRACTS v1.1 §10.1, which is where v1.0 §11.13 now lives); whichever * rung fires also stops the flow it owned. @@ -56,6 +58,20 @@ export interface KeyContext { /** Viewer M2: collapse the open bottom panel to its tab strip; false when there is none. */ collapseSheet(): boolean; announce(text: string): void; + /** Viewer M3: close the phase index's open list (the pill's); false when it was not open. */ + closePhasePopover(): boolean; + /** Viewer M3: leave the phase overview for where the reader was; false when it was not open. */ + closeOverview(): boolean; + /** Viewer M3: open or close the phase overview (Shift+0). */ + toggleOverview(): void; + /** Viewer M3: the review walk. */ + walking(): boolean; + /** End the walk; false when it was not running. */ + endWalk(): boolean; + review(): void; + walkStep(delta: number): boolean; + walkQuote(delta: number): boolean; + walkJump(kind: 'notObserved' | 'findings', backwards: boolean): boolean; } export function commandPortFor(ctx: KeyContext): CommandPort { @@ -65,15 +81,24 @@ export function commandPortFor(ctx: KeyContext): CommandPort { dismissTopmost: () => { if (ctx.sheetOpen()) ctx.toggleShortcuts(false); // The panels that open over the diagram from the header, before anything on the canvas. - else if (ctx.closeHeaderPanels()) return; + else if (ctx.closeHeaderPanels()) return true; + // Viewer M3: the phase index's open list (the pill's), a small panel over the diagram. + else if (ctx.closePhasePopover()) return true; // The legend is a PANEL over the diagram, opened with `l` and closed with // its own [x] — and Escape is what a reader presses at a panel. It sits // above focus mode because it is the shallower thing on screen: dismissing // it must never also throw away the focus or selection underneath. else if (ctx.legendOpen()) ctx.closeLegend(); + // Viewer M3: the phase overview covers the whole canvas; Escape goes back to the diagram as it + // was (the overview's own keys do the same when it has the focus). + else if (ctx.closeOverview()) return true; + // Viewer M3: the review walk ends before anything it opened is closed: the claim it was on + // stays selected and the bottom panel stays open on it. One Escape ends it, whatever else is + // on screen below it, so it never takes two presses to leave the walk. + else if (ctx.endWalk()) return true; // Viewer M2: the open bottom panel collapses to its tab strip before anything on the canvas // is undone: the selection it shows stays selected. - else if (ctx.collapseSheet()) return; + else if (ctx.collapseSheet()) return true; else if (ctx.view().isFocusLocked) ctx.view().toggleFocusMode(ctx.selection()); else if (ctx.selection()) { // A connection reached with `e` still holds DOM focus, and focus alone @@ -82,7 +107,15 @@ export function commandPortFor(ctx: KeyContext): CommandPort { // port and charge (MLV-R1-FLOW-011). ctx.view().blurFocusedEdge(); ctx.clearSelection(); - } else ctx.view().canvasEl.blur(); + } else { + // Leave the canvas. Viewer M3: when the focus is already off it, there is nothing left for + // this Escape to do here, and the key goes on to VS Code. + const canvas = ctx.view().canvasEl; + const active = canvas.ownerDocument ? canvas.ownerDocument.activeElement : null; + if (!active || !canvas.contains(active)) return false; + canvas.blur(); + } + return true; }, visibleIssues: () => ctx.visibleIssues(), @@ -95,7 +128,7 @@ export function commandPortFor(ctx: KeyContext): CommandPort { fit: () => ctx.view().fit(), toggleFocusMode: () => ctx.view().toggleFocusMode(ctx.selection()), zoomToSelection: () => ctx.zoomToSelection(), - overview: () => ctx.view().overview(), + overview: () => ctx.toggleOverview(), toggleLegend: () => ctx.toggleLegend(), toggleFlow: () => ctx.toggleFlow(), // Viewer M2: a flow settles after two passes; Shift+A runs it again from its first pass. @@ -123,6 +156,13 @@ export function commandPortFor(ctx: KeyContext): CommandPort { toggleShortcuts: () => ctx.toggleShortcuts(), cycleConnections: (backwards) => cycleConnections(ctx, backwards), + + walking: () => ctx.walking(), + review: () => ctx.review(), + walkStep: (delta) => ctx.walkStep(delta), + walkQuote: (delta) => ctx.walkQuote(delta), + walkNotObserved: (backwards) => ctx.walkJump('notObserved', backwards), + walkFindings: (backwards) => ctx.walkJump('findings', backwards), }; } diff --git a/webview/src/ui/chrome.ts b/webview/src/ui/chrome.ts index ca6a4ffe..5fdc2f43 100644 --- a/webview/src/ui/chrome.ts +++ b/webview/src/ui/chrome.ts @@ -5,7 +5,7 @@ * and the authored header. In order: * * title · provenance chip (host · revision) · search · severity toggles · "N not observed" · - * ... menu · Refine… + * Review (viewer M3) · ... menu · Refine… * * The row never wraps. How much of it shows depends on the panel's width (`headerLayout`): * @@ -21,10 +21,10 @@ * Viewer M2 review (M2R-2, A11Y-8): how wide the controls are depends on the document (two-digit * counts, three severities) and on whether the search field is open, so the layout alone could * not keep Refine… inside the panel. After every change `fitRow` measures the row and, while it - * overflows, folds in this order: the revision chip (About stays in the ... menu), the "not - * observed" toggle (into the ... menu, with its count and units), then the title (kept for screen - * readers) and, only while the search field is open at mid or narrow widths, the severity toggles - * (back when it closes). The roving tab stop is re-derived after each fold. A layout engine is + * overflows, folds in this order: the revision chip and, since viewer M3, the Review button (About + * and Review stay in the ... menu, and `r` starts the walk), the "not observed" toggle (into the + * ... menu, with its count and units), then the title (kept for screen readers) and, only while + * the search field is open at mid or narrow widths, the severity toggles (back when it closes). The roving tab stop is re-derived after each fold. A layout engine is * needed to measure; jsdom folds nothing. * * The status bar (about 22 px) carries the step and connection counts, the coverage status with the @@ -73,8 +73,10 @@ export interface ChromeCallbacks { onZoom(dir: number): void; onToggleLegend(next: boolean): void; onToggleFlow(next: boolean): void; - /** `collapsed`: the minimap's new state (VIEW-12: the keyboard's way to it). */ - onToggleMinimap(collapsed: boolean): void; + /** Viewer M3: show or hide the phase index (it replaced the minimap). */ + onTogglePhaseIndex(shown: boolean): void; + /** Viewer M3: open the phase overview (Shift+0), or close it. */ + onOverview(): void; onToggleRail(): void; onFitWhole(): void; onZoomToSelection(): void; @@ -82,6 +84,8 @@ export interface ChromeCallbacks { onShortcuts(): void; /** The ... menu is opening: repaint, so its checkboxes and disabled items are current. */ onMenuOpen(): void; + /** Viewer M3: the Review button or the ... menu's item: start the review walk, or end it. */ + onReview(): void; } export interface ChromeState { @@ -93,9 +97,12 @@ export interface ChromeState { visibleCounts: { low: number; medium: number; high: number }; flowOn: boolean; legendOpen: boolean; - minimapCollapsed: boolean; - /** Viewer M2 live fix: why the minimap is not drawn now, or null when it is (the ... menu says it). */ - minimapUnavailable: string | null; + /** Viewer M3: the reader has not hidden the phase index. */ + phaseIndexShown: boolean; + /** Why the phase index is not drawn (fewer than two phases), or null when it is (the ... menu says it). */ + phaseIndexUnavailable: string | null; + /** Viewer M3: the phase overview is open. */ + overviewOpen: boolean; railOpen: boolean; /** The status bar's freshness item, or null before a document arrives. */ freshness: FreshnessStatus | null; @@ -106,6 +113,8 @@ export interface ChromeState { exceptionsOn: boolean; /** Viewer M2: the rail is docked beside the canvas or a bottom sheet under it (the menu names it). */ railMode: 'docked' | 'sheet'; + /** Viewer M3: the review walk is running (Review is pressed). */ + walking: boolean; } /** Viewer M2: inferred or unresolved claims, counted by what they are. */ @@ -137,8 +146,9 @@ export function coverageText(document: WorkflowDocument): string { } /** - * How far `fitRow` may fold the row: the revision chip, then "not observed", then the title; and, - * only while the search field is open, the severity toggles (they come back when it closes). + * How far `fitRow` may fold the row: the revision chip and the Review button (viewer M3), then "not + * observed", then the title; and, only while the search field is open, the severity toggles (they + * come back when it closes). */ export const HEADER_FIT_MAX = 4; @@ -172,6 +182,8 @@ export class Chrome { /** The word after the severity toggles, "findings": the unit their numbers count. */ private sevUnit: HTMLElement; private exceptionsBtn: HTMLButtonElement; + /** Viewer M3: starts or ends the review walk; folds with the revision chip. */ + private reviewBtn: HTMLButtonElement; private counts: HTMLElement; private coverage: HTMLButtonElement; private fresh: HTMLElement; @@ -279,6 +291,18 @@ export class Chrome { on(this.exceptionsBtn, 'click', () => cb.onToggleExceptions(this.exceptionsBtn.getAttribute('aria-pressed') !== 'true')); this.toolbar.appendChild(this.exceptionsBtn); + // Viewer M3: the review walk. A toggle: pressed while the walk runs. Like the revision chip it + // is left out below 620 px and folds first when the row is short (the ... menu always has it, + // and `r` starts it anywhere). + this.reviewBtn = el('button', 'mlv-btn mlv-header__review') as HTMLButtonElement; + this.reviewBtn.type = 'button'; + this.reviewBtn.setAttribute('aria-pressed', 'false'); + this.reviewBtn.appendChild(uiIcon('review', 14)); + add(this.reviewBtn, el('span', 'mlv-header__reviewlabel', 'Review')); + this.reviewBtn.hidden = true; + on(this.reviewBtn, 'click', () => cb.onReview()); + this.toolbar.appendChild(this.reviewBtn); + this.more = new MoreMenu((id, byKeyboard) => this.pick(id, byKeyboard), () => cb.onMenuOpen()); this.toolbar.appendChild(this.more.button); @@ -371,13 +395,16 @@ export class Chrome { * Viewer M2 review (M2R-1): the `hidden` attribute on every control the current shape leaves * out, so the roving tab stop, the ... menu and a test agree with what the stylesheet draws. The * revision chip: no document, below 620 px, or folded. The search icon: only at `mid` with the - * field closed. "Not observed": nothing to count, or folded. + * field closed. "Not observed": nothing to count, or folded. Viewer M3: Review goes with the + * revision chip (below 620 px, or folded), so the 541 px row keeps the M2 controls; the ... menu + * always has it. */ private syncControls(): void { this.provenance.hidden = !this.hasDocument || this.layout === 'narrow' || this.fitLevel >= 1; const searchFolded = (this.layout === 'mid' || this.layout === 'narrow') && this.header.getAttribute('data-search') !== 'open'; this.searchBtn.hidden = !(this.layout === 'mid' && searchFolded); this.exceptionsBtn.hidden = !this.hasExceptions || this.fitLevel >= 2; + this.reviewBtn.hidden = !this.hasDocument || this.layout === 'narrow' || this.fitLevel >= 1; for (const [sev, b] of this.sevButtons) b.hidden = !this.sevShown.has(sev) || this.fitLevel >= 4; this.sevUnit.hidden = this.sevShown.size === 0 || this.fitLevel >= 4; this.header.setAttribute('data-fit', String(this.fitLevel)); @@ -422,9 +449,11 @@ export class Chrome { exceptionsOn: s.exceptionsOn, legendOpen: s.legendOpen, flowOn: s.flowOn, - minimapShown: !s.minimapCollapsed, - minimapUnavailable: s.minimapUnavailable, + phaseIndexShown: s.phaseIndexShown, + phaseIndexUnavailable: s.phaseIndexUnavailable, + overviewOpen: s.overviewOpen, railOpen: s.railOpen, + walking: s.walking, railMode: s.railMode, hasSelection: s.hasSelection, canExport: !!s.graph, @@ -438,11 +467,13 @@ export class Chrome { else if (id === 'exceptions') cb.onToggleExceptions(!(this.state && this.state.exceptionsOn)); else if (id === 'legend') cb.onToggleLegend(!(this.state && this.state.legendOpen)); else if (id === 'flow') cb.onToggleFlow(!(this.state && this.state.flowOn)); - else if (id === 'minimap') cb.onToggleMinimap(!(this.state && this.state.minimapCollapsed)); + else if (id === 'phaseindex') cb.onTogglePhaseIndex(!(this.state && this.state.phaseIndexShown)); + else if (id === 'overview') cb.onOverview(); else if (id === 'rail') cb.onToggleRail(); else if (id === 'fit') cb.onFitWhole(); else if (id === 'zoomsel') cb.onZoomToSelection(); else if (id === 'shortcuts') cb.onShortcuts(); + else if (id === 'review') cb.onReview(); else cb.onExport(id); } @@ -493,6 +524,14 @@ export class Chrome { const shown = SEVERITY_ORDER.filter((sev) => this.sevShown.has(sev)); this.sevUnit.textContent = shown.length === 1 && s.visibleCounts[shown[0]] === 1 ? 'finding' : 'findings'; + this.reviewBtn.setAttribute('aria-pressed', s.walking ? 'true' : 'false'); + this.reviewBtn.title = s.walking + ? 'End the review walk (Escape). It remembers its place for this revision.' + : 'Review the claims one by one (R): each is selected here and its cited lines are opened and highlighted in the editor beside. Focus stays here.'; + // One name; `aria-pressed` carries the state (M3 review, A11Y-M3-7): "Review the claims, toggle + // button, pressed" while the walk runs. + this.reviewBtn.setAttribute('aria-label', 'Review the claims'); + this.syncSearch(); this.renderStatus(s); this.fitRow(); diff --git a/webview/src/ui/commands.ts b/webview/src/ui/commands.ts index e767ff52..b3219d96 100644 --- a/webview/src/ui/commands.ts +++ b/webview/src/ui/commands.ts @@ -12,8 +12,8 @@ import type { Issue, RailTab, Sel, Severity } from '../types.js'; /** The operations the keyboard is allowed to reach. Implemented by App. */ export interface CommandPort { focusSearch(): void; - /** Escape's cascade, innermost first. Returns once something was dismissed. */ - dismissTopmost(): void; + /** Escape's cascade, innermost first. Returns once something was dismissed; false when nothing was. */ + dismissTopmost(): boolean; visibleIssues(): Issue[]; selectedIssueId(): string | null; focusIssue(id: string): void; @@ -21,7 +21,7 @@ export interface CommandPort { fit(): void; toggleFocusMode(): void; zoomToSelection(): void; - /** Collapse every group and fit (VIEW-10). */ + /** Viewer M3: open or close the phase overview (Shift+0). */ overview(): void; toggleLegend(): void; toggleFlow(): void; @@ -38,6 +38,14 @@ export interface CommandPort { focusRailTabs(): void; toggleShortcuts(): void; cycleConnections(backwards: boolean): boolean; + /** Viewer M3: the review walk (see `KeyCommands`). */ + walking(): boolean; + review(): void; + walkStep(delta: number): boolean; + walkQuote(delta: number): boolean; + walkNotObserved(backwards: boolean): boolean; + /** Viewer M3: `n` / `p` while the walk runs: the next or previous finding in the walk's order. */ + walkFindings(backwards: boolean): boolean; } /** Every rail tab, in strip order: About, Findings, Selection, Outline (viewer M2). */ @@ -64,6 +72,8 @@ export function canvasCommands(port: CommandPort): KeyCommands { focusSearch: () => port.focusSearch(), escape: () => port.dismissTopmost(), cycleIssue: (backwards) => { + // Viewer M3: while the review walk runs, n / p move the walk to the next or previous finding. + if (port.walking()) return port.walkFindings(backwards); const issues = port.visibleIssues(); if (!issues.length) return false; const current = port.selectedIssueId(); @@ -91,5 +101,10 @@ export function canvasCommands(port: CommandPort): KeyCommands { focusRailTabs: () => port.focusRailTabs(), toggleShortcuts: () => port.toggleShortcuts(), cycleConnections: (backwards) => port.cycleConnections(backwards), + walking: () => port.walking(), + review: () => port.review(), + walkStep: (delta) => port.walkStep(delta), + walkQuote: (delta) => port.walkQuote(delta), + walkNotObserved: (backwards) => port.walkNotObserved(backwards), }; } diff --git a/webview/src/ui/hostnotice.ts b/webview/src/ui/hostnotice.ts index 8ee935a4..141c6cfa 100644 --- a/webview/src/ui/hostnotice.ts +++ b/webview/src/ui/hostnotice.ts @@ -19,6 +19,8 @@ const WARN_CODES = new Set(['stale', 'root-hint']); export interface HostNoticeCallbacks { /** The root hint's actions; the host decides which folder. */ onWorkspaceHint(action: 'add' | 'open'): void; + /** Viewer M3: the stale notice's "Review affected claims" starts the walk on Changed files. */ + onReviewAffected(): void; } export class HostNotice { @@ -27,6 +29,12 @@ export class HostNotice { private text: HTMLElement; private actions: HTMLElement; private cb: HostNoticeCallbacks; + /** Viewer M3: "Review affected claims", offered under a stale notice while claims cite those files. */ + private review: HTMLButtonElement; + private reviewCount = 0; + private walking = false; + private message = ''; + private codes: readonly string[] = []; constructor(cb: HostNoticeCallbacks) { this.cb = cb; @@ -37,6 +45,22 @@ export class HostNotice { this.icon.setAttribute('aria-hidden', 'true'); this.text = add(this.root, el('p', 'mlv-hostnotice__text')); this.actions = add(this.root, el('div', 'mlv-hostnotice__actions')); + this.review = button('mlv-btn mlv-hostnotice__review', 'Review affected claims'); + this.review.setAttribute('data-notice-action', 'review'); + on(this.review, 'click', () => this.cb.onReviewAffected()); + } + + /** + * Viewer M3: how many claims cite a changed or missing file (the walk's Changed files count), and + * whether the walk is running. The action is offered under the host's `stale` notice when that + * count is above zero and the walk is not already running (it would be a second tab stop before + * the diagram, and the walk bar has the filter). + */ + setReview(count: number, walking: boolean): void { + if (count === this.reviewCount && walking === this.walking) return; + this.reviewCount = count; + this.walking = walking; + this.syncActions(); } /** Show the host's text, or hide the notice when it is empty. */ @@ -49,8 +73,21 @@ export class HostNotice { this.text.textContent = message; clear(this.icon); if (kind !== 'info') this.icon.appendChild(uiIcon('warning', 14)); + this.message = message; + this.codes = list; + this.syncActions(); + } + + private syncActions(): void { + const message = this.message; + const list = this.codes; clear(this.actions); this.actions.hidden = true; + if (message && list.indexOf('stale') >= 0 && this.reviewCount > 0 && !this.walking) { + this.review.title = 'Walk the ' + this.reviewCount + (this.reviewCount === 1 ? ' claim' : ' claims') + ' whose quotes cite a file that changed or went missing; nothing stale is opened'; + this.actions.appendChild(this.review); + this.actions.hidden = false; + } if (message && list.indexOf('root-hint') >= 0) { const addBtn = button('mlv-btn mlv-btn--primary', 'Add folder to workspace'); addBtn.setAttribute('data-workspace-hint', 'add'); diff --git a/webview/src/ui/keymap.ts b/webview/src/ui/keymap.ts index e67accd9..780b49ad 100644 --- a/webview/src/ui/keymap.ts +++ b/webview/src/ui/keymap.ts @@ -12,14 +12,19 @@ export interface KeyCommands { focusSearch(): void; - escape(): void; + /** The Escape cascade; false when nothing was left to dismiss, so the key is not consumed. */ + escape(): boolean; cycleIssue(backwards: boolean): boolean; zoom(direction: number): void; /** `0`: the readable view (viewer M2), the first paint again. */ fit(): void; toggleFocusMode(): void; zoomToSelection(): void; - /** `Shift+0`: collapse every group and fit — the one-screen picture. */ + /** + * `Shift+0` (viewer M3): open the phase overview, every phase as a block of its step titles, or + * close it. It used to fold every group and fit the whole diagram; the whole diagram is the ... + * menu's "Fit the whole diagram", and a group folds with its own chevron (or Space). + */ overview(): void; /** `l`: show or hide the legend. */ toggleLegend(): void; @@ -46,6 +51,16 @@ export interface KeyCommands { toggleShortcuts(): void; /** `e` / `Shift+E`: walk the selection's connections. */ cycleConnections(backwards: boolean): boolean; + /** Viewer M3: the review walk is running (j, k, ↓, ↑, [ and ] answer only then). */ + walking(): boolean; + /** Viewer M3, `r`: start the review walk where it was left in this revision, or end it. */ + review(): void; + /** Viewer M3, while walking: j / ↓ (+1) and k / ↑ (-1) step through the claims. */ + walkStep(delta: number): boolean; + /** Viewer M3, while walking: `]` (+1) and `[` (-1) step through the claim's quotes. */ + walkQuote(delta: number): boolean; + /** Viewer M3, `u` / `Shift+U`: start the walk on the claims not observed, or step through them. */ + walkNotObserved(backwards: boolean): boolean; } export interface KeyBinding { @@ -59,16 +74,22 @@ export const KEYMAP: KeyBinding[] = [ // bar. `Mod+F` is printed Cmd+F (⌘F) on macOS and Ctrl+F elsewhere (ui/platform.ts). Viewer M2 // live fix: Ctrl/Cmd+K is gone; the workbench reads Cmd+K (macOS) and Ctrl+K as a chord prefix. { keys: ['Mod+F', '/'], action: 'focusSearch', description: 'Search steps, findings, IDs, or cited text' }, - { keys: ['n', 'p'], action: 'cycleIssue', description: 'Next / previous finding (document order)' }, + { keys: ['n', 'p'], action: 'cycleIssue', description: 'Next / previous finding (document order); in the review walk, in the walk\'s order' }, // Viewer M1: a click selects and shows the claim; opening the source is Enter (or a double-click). - { keys: ['Enter'], action: 'open', description: 'Open the cited source beside the diagram; focus stays here' }, + { keys: ['Enter'], action: 'open', description: 'Open the cited source beside the diagram; focus stays here (in the review walk: open the current quote again)' }, { keys: ['Alt+Enter'], action: 'openFocus', description: 'Open the cited source and move focus to the editor' }, { keys: ['Space'], action: 'collapse', description: 'Collapse or expand the selected group' }, - { keys: ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'], action: 'move', description: 'Move the selection' }, + { keys: ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'], action: 'move', description: 'Move the selection (from a connection, to its end that lies that way; in the review walk, ↓ and ↑ step through the claims)' }, + // Viewer M3: the review walk. Plain keys only (the M2 key rule): VS Code leaves them to a focused + // webview. `[` and `]` were the scope keys until viewer M2 removed scoping. + { keys: ['r'], action: 'review', description: 'Review walk: go claim by claim, each opened and highlighted in the editor beside (starts on the claims not observed; r again or Escape ends it)' }, + { keys: ['j', 'k'], action: 'walkStep', description: 'In the review walk: next / previous claim' }, + { keys: ['[', ']'], action: 'walkQuote', description: 'In the review walk: previous / next quote of the claim' }, + { keys: ['u', 'Shift+U'], action: 'walkNotObserved', description: 'Review walk on the claims not observed: start it, then next / previous one' }, // Viewer M2: key 0 is the readable first view; the whole document is the ... menu's "Fit the - // whole diagram" (and Shift+0, which also folds the groups). + // whole diagram". Viewer M3: Shift+0 is the phase overview (it used to fold every group and fit). { keys: ['0'], action: 'fit', description: 'Readable view: the whole diagram if it fits at reading size, otherwise phase 1' }, - { keys: ['Shift+0'], action: 'overview', description: 'Overview: collapse every group and fit the whole diagram' }, + { keys: ['Shift+0'], action: 'overview', description: 'Phase overview: every phase as a block of its step titles; arrows move between phases, Enter goes to one, Escape comes back' }, { keys: ['+', '='], action: 'zoomIn', description: 'Zoom in' }, { keys: ['-', '_'], action: 'zoomOut', description: 'Zoom out' }, { keys: ['z'], action: 'zoomToSelection', description: 'Zoom to the selection' }, @@ -89,7 +110,7 @@ export const KEYMAP: KeyBinding[] = [ { keys: ['?'], action: 'shortcuts', description: 'Show this shortcut sheet' }, // The rungs, in the order `dismissTopmost` runs them (CONTRACTS 11.13). The // sheet is the only place the cascade is described to the user (MLV-R1-F2-06). - { keys: ['Escape'], action: 'escape', description: 'Close the menu, this sheet, the Refine popover or the legend, collapse the bottom panel, exit focus mode, clear the selection, leave the canvas' }, + { keys: ['Escape'], action: 'escape', description: 'Close the menu, this sheet, the Refine popover, the phase list or the legend, leave the phase overview, end the review walk, collapse the bottom panel, exit focus mode, clear the selection, leave the canvas' }, { keys: ['Tab', 'Shift+Tab'], action: 'browser', description: 'Move focus out of the diagram (never intercepted)' }, ]; @@ -134,10 +155,37 @@ export function handleCanvasKey(ev: KeyboardEvent, cmd: KeyCommands): boolean { cmd.toggleShortcuts(); return consume(); } + // Viewer M3: an Escape that dismissed nothing (the focus is already off the canvas and nothing is + // open or selected) is left unconsumed, so VS Code can use it, for example to hide a notification. + // An Escape the cascade used is consumed, and the panel's bootstrap then keeps VS Code from also + // acting on it. if (key === 'Escape') { - cmd.escape(); + if (!cmd.escape()) return false; + return consume(); + } + // Viewer M3: the review walk. `r` starts or ends it; `u` / Shift+U start it on the claims not + // observed or step through them (branch on shiftKey: Caps Lock sends 'U' for a plain u). While it + // runs, j / k and ↓ / ↑ step and [ / ] change the quote; outside it those keys keep their meaning + // (the arrows move spatially) or are left unconsumed. + if (key === 'r' || key === 'R') { + cmd.review(); + return consume(); + } + if (key === 'u' || key === 'U') { + if (!cmd.walkNotObserved(ev.shiftKey)) return false; return consume(); } + if (cmd.walking()) { + const step = key === 'j' || key === 'J' || key === 'ArrowDown' ? 1 : key === 'k' || key === 'K' || key === 'ArrowUp' ? -1 : 0; + if (step) { + if (!cmd.walkStep(step)) return false; + return consume(); + } + if (key === '[' || key === ']') { + if (!cmd.walkQuote(key === ']' ? 1 : -1)) return false; + return consume(); + } + } if (key === 'n' || key === 'N' || key === 'p' || key === 'P') { if (!cmd.cycleIssue(key === 'p' || key === 'P')) return false; return consume(); @@ -151,7 +199,7 @@ export function handleCanvasKey(ev: KeyboardEvent, cmd: KeyCommands): boolean { return consume(); } // Shift+0 arrives as ')' on a US layout and as '0' with `shiftKey` elsewhere; - // both mean Overview, and the plain `0` below must not swallow either. + // both mean the phase overview, and the plain `0` below must not swallow either. if (key === ')' || (key === '0' && ev.shiftKey)) { cmd.overview(); return consume(); diff --git a/webview/src/ui/legend.ts b/webview/src/ui/legend.ts index 811fcfd5..4c00638b 100644 --- a/webview/src/ui/legend.ts +++ b/webview/src/ui/legend.ts @@ -49,7 +49,9 @@ const EDGE_ROWS: LegendRow[] = [ ]; const PHASE_ROWS: LegendRow[] = [ - { group: 'phase', key: 'order', label: 'phase colour', detail: 'Each phase gets a colour by its place in the document, shown on the lane\'s left rule, the swatch before its number and name, and the card\'s left edge. It means nothing else. Problems are the only other colour on the diagram.' }, + { group: 'phase', key: 'order', label: 'phase colour', detail: 'Each phase gets a colour by its place in the document, shown on the lane\'s left rule, the swatch before its number and name, the card\'s left edge, and its number in the phase index and the phase overview. It means nothing else. Problems are the only other colour on the diagram.' }, + // Viewer M3: the labelled phase index (it replaced the minimap) and the phase overview. + { group: 'phase', key: 'index', label: 'phase index', detail: 'The list in the lower right corner of the diagram: each phase\'s number, name, the findings that touch it and its step count, with the phases in view marked. A row goes to that phase. Beside the code it is one line, such as "4/6 Data loading", that opens the list. Shift+0 shows every phase as a block of its step titles (the phase overview).' }, ]; const FRESHNESS_ROWS: LegendRow[] = [ @@ -153,7 +155,7 @@ function swatchFor(row: LegendRow): Node { if (row.key === 'kinds') return el('span', 'mlv-chip', 'data'); return edgeSwatch('observed', row.key === 'back'); } - if (row.group === 'phase') return phaseSwatch(); + if (row.group === 'phase') return row.key === 'index' ? el('span', 'mlv-chip', '4/6') : phaseSwatch(); if (row.key === 'stale') { const chip = el('span', 'mlv-chip mlv-chip--stale'); chip.appendChild(uiIcon('warning', 11)); @@ -164,8 +166,8 @@ function swatchFor(row: LegendRow): Node { } /** - * The panel itself: anchored to the minimap corner of the canvas, collapsible, - * and remembered per viewer through `ViewState.legendOpen`. + * The panel itself: anchored to the lower right corner of the canvas (over the phase index while + * it is open), collapsible, and remembered per viewer through `ViewState.legendOpen`. */ export class Legend { readonly root: HTMLElement; diff --git a/webview/src/ui/moremenu.ts b/webview/src/ui/moremenu.ts index 2520b9e0..0a2fa901 100644 --- a/webview/src/ui/moremenu.ts +++ b/webview/src/ui/moremenu.ts @@ -1,6 +1,6 @@ /** - * The header's ... menu (viewer M2): the view toggles, the whole-diagram fit, the exports and the - * shortcut sheet, plus whatever the header has no room for: search and the revision below 620 px, + * The header's ... menu (viewer M2): the review walk and the phase overview (viewer M3), the view toggles, the whole-diagram + * fit, the exports and the shortcut sheet, plus whatever the header has no room for: search and the revision below 620 px, * and, in a row too narrow for its controls, the revision and the "not observed" toggle. * * The standard menu-button pattern, as the export menu it replaces used it (VW-03): the trigger is @@ -21,9 +21,11 @@ export type MoreItemId = | 'search' | 'about' | 'exceptions' + | 'review' + | 'overview' | 'legend' | 'flow' - | 'minimap' + | 'phaseindex' | 'rail' | 'fit' | 'zoomsel' @@ -49,9 +51,15 @@ const ITEMS: ItemSpec[] = [ { id: 'search', label: 'Search steps and findings', icon: 'search', keys: 'Mod+F', folded: true }, { id: 'about', label: 'About this revision', icon: 'info', folded: true }, { id: 'exceptions', label: 'Not observed', icon: 'notobserved', check: true, folded: true }, + // Viewer M3: the review walk, always here (the header's Review button folds first when the row + // is short); "End the review" while it runs. + { id: 'review', label: 'Review the claims', icon: 'review', keys: 'R', group: true }, + // Viewer M3: every phase as a block of its step titles; Shift+0 does the same. + { id: 'overview', label: 'Phase overview', icon: 'phases', keys: 'Shift+0' }, { id: 'legend', label: 'Legend', icon: 'legend', keys: 'L', check: true, group: true }, { id: 'flow', label: 'Connection flow animation', icon: 'flow', keys: 'A', check: true }, - { id: 'minimap', label: 'Overview map', icon: 'minimap', check: true }, + // Viewer M3: the labelled phase index replaced the overview map (the minimap). + { id: 'phaseindex', label: 'Phase index', icon: 'phaseindex', check: true }, // Viewer M2 live fix: `b` on the canvas; Ctrl/Cmd+B also toggled the workbench's side bar. { id: 'rail', label: 'Side panel', icon: 'rail', keys: 'B', check: true }, { id: 'fit', label: 'Fit the whole diagram', icon: 'fit', group: true }, @@ -78,13 +86,18 @@ export interface MoreMenuState { exceptionsOn: boolean; legendOpen: boolean; flowOn: boolean; - minimapShown: boolean; + /** Viewer M3: the reader has not hidden the phase index. */ + phaseIndexShown: boolean; /** - * Viewer M2 live fix: why the minimap is not drawn now (too few cards, no room beside the code, - * a short canvas), or null. The item is then disabled, unchecked, and says why. + * Why the phase index is not drawn (fewer than two phases), or null. The item is then disabled, + * unchecked, and says why (the viewer M2 live fix's rule for the map it replaced). */ - minimapUnavailable: string | null; + phaseIndexUnavailable: string | null; + /** Viewer M3: the phase overview is open (the item reads "Close the phase overview"). */ + overviewOpen: boolean; railOpen: boolean; + /** Viewer M3: the review walk is running (the item reads "End the review"). */ + walking: boolean; /** Viewer M2: the rail item is "Side panel" while docked and "Bottom panel" as a sheet. */ railMode: 'docked' | 'sheet'; hasSelection: boolean; @@ -116,7 +129,7 @@ export class MoreMenu { const uid = 'mlv-more' + ++menuSeq; this.button = iconButton('mlv-btn mlv-btn--icon mlv-btn--more', 'More actions'); - this.button.title = 'More: legend, flow, fit, export, shortcuts'; + this.button.title = 'More: review, phase overview, legend, fit, export, shortcuts'; this.button.appendChild(uiIcon('more', 16)); this.button.setAttribute('aria-haspopup', 'menu'); this.button.setAttribute('aria-expanded', 'false'); @@ -163,7 +176,7 @@ export class MoreMenu { if (spec.folded) item.setAttribute('data-folded', '1'); item.appendChild(uiIcon(spec.icon, 14)); add(item, el('span', 'mlv-moremenu__label', spec.label)); - if (spec.id === 'minimap') add(item, el('span', 'mlv-moremenu__note')).hidden = true; + if (spec.id === 'phaseindex') add(item, el('span', 'mlv-moremenu__note')).hidden = true; if (spec.keys) add(item, el('kbd', 'mlv-moremenu__keys', keyLabel(spec.keys))).setAttribute('aria-hidden', 'true'); on(item, 'click', (ev: MouseEvent) => { ev.preventDefault(); @@ -226,24 +239,42 @@ export class MoreMenu { if (label) label.textContent = s.exceptionsLabel; item.title = s.exceptionsOn ? 'The observed claims are faded; choose to show them again' : 'Fade the observed claims so these stand out'; } + if (id === 'review') { + const label = item.querySelector('.mlv-moremenu__label'); + if (label) label.textContent = s.walking ? 'End the review' : 'Review the claims'; + item.title = s.walking + ? 'End the review walk (Escape); it remembers its place for this revision' + : 'Walk the claims one by one, each opened and highlighted in the editor beside; focus stays here'; + item.disabled = !s.canExport; + } if (id === 'rail') { const label = item.querySelector('.mlv-moremenu__label'); if (label) label.textContent = s.railMode === 'sheet' ? 'Bottom panel' : 'Side panel'; item.title = s.railMode === 'sheet' ? 'Open or collapse the bottom panel' : 'Show or hide the side panel'; } + if (id === 'overview') { + const label = item.querySelector('.mlv-moremenu__label'); + if (label) label.textContent = s.overviewOpen ? 'Close the phase overview' : 'Phase overview'; + item.title = s.overviewOpen + ? 'Back to the diagram where you were (Escape)' + : 'Every phase as a block of its step titles, with the connections between phases; choose one to go there'; + item.disabled = !s.canExport; + } if (spec.check) { - const on_ = id === 'legend' ? s.legendOpen : id === 'flow' ? s.flowOn : id === 'minimap' ? s.minimapShown && !s.minimapUnavailable : id === 'exceptions' ? s.exceptionsOn : s.railOpen; + const on_ = id === 'legend' ? s.legendOpen : id === 'flow' ? s.flowOn : id === 'phaseindex' ? s.phaseIndexShown && !s.phaseIndexUnavailable : id === 'exceptions' ? s.exceptionsOn : s.railOpen; item.setAttribute('aria-checked', on_ ? 'true' : 'false'); } - if (id === 'minimap') { - // Viewer M2 live fix: never checked while the map is not drawn; the reason is on the item. + if (id === 'phaseindex') { + // Never checked while the index is not drawn; the reason is on the item. const note = item.querySelector('.mlv-moremenu__note'); if (note) { - note.textContent = s.minimapUnavailable || ''; - note.hidden = !s.minimapUnavailable; + note.textContent = s.phaseIndexUnavailable || ''; + note.hidden = !s.phaseIndexUnavailable; } - item.disabled = !!s.minimapUnavailable; - item.title = s.minimapUnavailable ? 'Overview map: ' + s.minimapUnavailable.charAt(0).toLowerCase() + s.minimapUnavailable.slice(1) : 'Show or hide the overview map'; + item.disabled = !!s.phaseIndexUnavailable; + item.title = s.phaseIndexUnavailable + ? 'Phase index: ' + s.phaseIndexUnavailable.charAt(0).toLowerCase() + s.phaseIndexUnavailable.slice(1) + : 'Show or hide the phase index: each phase with its findings and steps, the phases in view marked'; } if (id === 'zoomsel') item.disabled = !s.hasSelection; if (EXPORTS.indexOf(id) >= 0) item.disabled = !s.canExport; diff --git a/webview/src/ui/overview.ts b/webview/src/ui/overview.ts new file mode 100644 index 00000000..52ead572 --- /dev/null +++ b/webview/src/ui/overview.ts @@ -0,0 +1,399 @@ +/** + * Viewer M3, roadmap step 13: the phase overview's DOM (Shift+0). The geometry is + * `render/phaseoverview.ts`; this file only draws it, over the canvas and outside the world layer + * that pans and zooms, so nothing in the routed picture (or its golden) moves. + * + * Keyboard: each phase block is one focusable element of a roving group. ↓ / → and ↑ / ← move + * between blocks, Home and End jump to the first and last, Enter or Space goes to the phase, and + * Shift+0 goes back to where the reader was. Escape goes on to the canvas's Escape cascade, which + * closes a legend or phase list opened over the overview first and then leaves the overview (M3 + * review, F5). Tab is never held. Every other key goes on to the canvas, whose handler closes the + * overview first and then acts (app/keys.ts). + * + * Names: a block is a button named "Phase 2 of 6: Data preparation. 9 steps (1 inferred). 2 + * findings touch this phase. 3 connections to phase 3; 4 connections ahead to phase 5. Enter goes + * to this phase." and described by its list of titles, which ends with "… 7 more steps" when it is + * trimmed. + */ + +import { add, clear, el, on, svg } from '../dom.js'; +import { uiIcon } from '../icons.js'; +import { countsTotal, severityCluster, severityGlyph, SEVERITY_WORD } from '../markers.js'; +import { stampPhase } from '../render/phase.js'; +import { phaseFindingsSpoken, phaseFindingsText } from '../render/nodes.js'; +import { blockStepsText, isNextLink, phaseOverviewLayout } from '../render/phaseoverview.js'; +import type { OverviewInput, OverviewLayout, OverviewPhase } from '../render/phaseoverview.js'; + +export interface OverviewCallbacks { + /** Go to a phase (a click, or Enter / Space on its block). */ + go(phaseId: string, byKeyboard: boolean): void; + /** Back to the diagram as it was (Escape, Shift+0, the Back button). */ + back(): void; +} + +let overviewSeq = 0; + +const plural = (n: number, one: string, many: string): string => n + ' ' + (n === 1 ? one : many); + +export class PhaseOverview { + readonly root: HTMLElement; + private content: HTMLElement; + private cb: OverviewCallbacks; + private uid: string; + private input: OverviewInput | null = null; + private layoutData: OverviewLayout | null = null; + private blockEls: HTMLElement[] = []; + private active = 0; + /** + * How much of the overlay's top the sticky header covers while the overview scrolls (0 when the + * header is too tall to stick and scrolls with the blocks). A block brought into view is placed + * below it. + */ + private covered = 0; + private disposers: (() => void)[] = []; + + constructor(cb: OverviewCallbacks) { + this.cb = cb; + this.uid = 'mlv-ov' + ++overviewSeq; + this.root = el('section', 'mlv-overview'); + this.root.setAttribute('aria-label', 'Phase overview'); + this.root.hidden = true; + this.content = add(this.root, el('div', 'mlv-overview__content')); + this.disposers.push(on(this.root, 'keydown', (ev: KeyboardEvent) => this.onKey(ev))); + // The overview scrolls by itself; the canvas's wheel zoom must not see it (it would move the + // hidden diagram under the overlay). + this.disposers.push(on(this.root, 'wheel', (ev: WheelEvent) => ev.stopPropagation())); + } + + get open(): boolean { + return !this.root.hidden; + } + + /** The layout last drawn (tests and the harness read it). */ + get layout(): OverviewLayout | null { + return this.layoutData; + } + + /** The block that has (or would get) the focus, by position in drawn order. */ + get activeIndex(): number { + return this.active; + } + + /** Draw it for `input` at the canvas size and show it, with `activePhase` (drawn position) current. */ + show(input: OverviewInput, w: number, h: number, activePhase: number): void { + this.root.hidden = false; + this.active = Math.max(0, Math.min(input.phases.length - 1, activePhase)); + this.draw(input, w, h); + this.focusBlock(this.active); + } + + hide(): void { + this.root.hidden = true; + } + + /** Draw again for a new size or new data (a resize, a severity toggle), keeping the current block. */ + redraw(input: OverviewInput, w: number, h: number): void { + if (!this.open) return; + const doc = this.root.ownerDocument; + const hadFocus = !!doc && !!doc.activeElement && this.root.contains(doc.activeElement); + this.active = Math.max(0, Math.min(input.phases.length - 1, this.active)); + this.draw(input, w, h); + if (hadFocus) this.focusBlock(this.active); + } + + focusBlock(i: number): void { + const block = this.blockEls[i]; + if (!block) { + try { + this.root.focus(); + } catch (_e) { + /* detached */ + } + return; + } + this.active = i; + for (const [j, b] of this.blockEls.entries()) b.tabIndex = j === i ? 0 : -1; + try { + block.focus({ preventScroll: true }); + } catch (_e) { + /* a host may have detached the overlay mid-gesture */ + } + scrollIntoViewIfNeeded(this.root, block, this.covered, i === 0); + } + + private draw(input: OverviewInput, w: number, h: number): void { + this.input = input; + const layout = phaseOverviewLayout(input, w, h); + this.layoutData = layout; + clear(this.content); + this.blockEls = []; + this.root.setAttribute('data-layout', layout.narrow ? 'narrow' : 'wide'); + this.root.setAttribute('data-scrolls', layout.scrolls ? 'true' : 'false'); + this.content.style.width = layout.width + 'px'; + this.content.style.height = layout.height + 'px'; + + // Header: what this is, the way back, the counts and (with room) the key. M3 live check, W4: it + // sticks to the top of the overlay while the overview scrolls, so Back, the counts and the key + // stay on screen when the focused block is low (at 901 and 541 px they scrolled away). It is + // the first box of the content, as tall as the space above the first block less the gap, so + // nothing below it moves; a header taller than half the overlay scrolls with the blocks. + const header = add(this.content, el('div', 'mlv-overview__header')); + const headerBox = layout.header.y + layout.header.h; + const sticky = layout.scrolls && headerBox * 2 <= layout.viewH; + this.covered = sticky ? headerBox : 0; + header.setAttribute('data-sticky', sticky ? 'true' : 'false'); + header.style.width = layout.width + 'px'; + header.style.height = headerBox + 'px'; + header.style.paddingTop = layout.header.y + 'px'; + header.style.paddingLeft = layout.header.x + 'px'; + header.style.paddingRight = Math.max(0, layout.width - layout.header.x - layout.header.w) + 'px'; + const top = add(header, el('div', 'mlv-overview__top')); + const title = add(top, el('h3', 'mlv-overview__title', 'Phase overview')); + title.id = this.uid + '-title'; + this.root.setAttribute('aria-labelledby', title.id); + const back = add(top, el('button', 'mlv-btn mlv-overview__back')) as HTMLButtonElement; + back.type = 'button'; + back.tabIndex = -1; + back.setAttribute('aria-label', 'Back to the diagram (Escape)'); + back.title = 'Back to the diagram where you were (Escape or Shift+0)'; + back.appendChild(uiIcon('close', 12)); + add(back, el('span', 'mlv-overview__backlabel', 'Back')); + add(back, el('kbd', 'mlv-overview__kbd', 'Esc')).setAttribute('aria-hidden', 'true'); + on(back, 'click', (ev: MouseEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + this.cb.back(); + }); + const summary = add(header, el('p', 'mlv-overview__summary', layout.header.summary)); + summary.id = this.uid + '-summary'; + this.root.setAttribute('aria-describedby', summary.id); + if (layout.header.key) { + // The ◌ is drawn (a dotted ring), not typed: system fonts on macOS have no visible glyph for it. + const key = add(header, el('p', 'mlv-overview__key')); + layout.header.key.split('◌').forEach((part, k) => { + if (k > 0) key.appendChild(inferredMark()); + key.appendChild(doc(header).createTextNode(part)); + }); + } + + // Arrows and brackets under the blocks' text, in one SVG over the whole content. + const links = svg('svg', { class: 'mlv-overview__links', width: layout.width, height: layout.height, viewBox: '0 0 ' + layout.width + ' ' + layout.height, 'aria-hidden': 'true', focusable: 'false' }); + const defs = svg('defs'); + const marker = svg('marker', { id: this.uid + '-arrow', viewBox: '0 0 8 8', refX: 7, refY: 4, markerWidth: 7, markerHeight: 7, orient: 'auto-start-reverse' }); + marker.appendChild(svg('path', { d: 'M0 0.6 7.4 4 0 7.4Z', class: 'mlv-overview__head' })); + defs.appendChild(marker); + links.appendChild(defs); + for (const a of layout.arrows) { + links.appendChild(svg('path', { class: 'mlv-overview__arrow', d: 'M' + a.x + ' ' + a.y1 + 'V' + a.y2, 'marker-end': 'url(#' + this.uid + '-arrow)' })); + } + for (const b of layout.brackets) { + const path = svg('path', { + class: 'mlv-overview__bracket', + d: 'M' + b.x0 + ' ' + b.y1 + 'H' + b.x + 'V' + b.y2 + 'H' + (b.x0 + 2), + 'marker-end': 'url(#' + this.uid + '-arrow)', + }); + if (b.back) path.setAttribute('data-back', 'true'); + links.appendChild(path); + } + this.content.appendChild(links); + for (const a of layout.arrows) { + const label = add(this.content, el('span', 'mlv-overview__arrowlabel', plural(a.count, 'connection', 'connections'))); + label.style.left = a.labelX + 'px'; + label.style.top = a.labelY + 'px'; + label.setAttribute('aria-hidden', 'true'); + } + for (const b of layout.brackets) { + const from = input.phases[b.from]; + const to = input.phases[b.to]; + const count = add(this.content, el('span', 'mlv-overview__count', String(b.count))); + count.style.left = b.labelX + 'px'; + count.style.top = b.labelY + 'px'; + if (b.back) count.setAttribute('data-back', 'true'); + count.setAttribute('aria-hidden', 'true'); + count.title = plural(b.count, 'connection', 'connections') + ' from phase ' + (from.index + 1) + (b.back ? ' back to' : ' ahead to') + ' phase ' + (to.index + 1); + } + + const list = add(this.content, el('ol', 'mlv-overview__blocks')); + list.setAttribute('aria-label', plural(input.phases.length, 'phase', 'phases')); + layout.blocks.forEach((block, i) => { + const phase = input.phases[block.phase]; + const li = add(list, el('li', 'mlv-overview__item')); + const box = add(li, el('div', 'mlv-ovblock')); + box.setAttribute('role', 'button'); + box.setAttribute('data-phase-id', phase.id); + box.setAttribute('data-block', String(i)); + box.setAttribute('data-head', block.twoLineHead ? 'two-lines' : 'one-line'); + stampPhase(box, phase.index); + box.tabIndex = i === this.active ? 0 : -1; + place(box, block); + box.setAttribute('aria-label', blockName(input, block.phase)); + const head = add(box, el('div', 'mlv-ovblock__head')); + head.style.height = block.headH + 'px'; + const name = add(head, el('div', 'mlv-ovblock__name')); + add(name, el('span', 'mlv-ovblock__num', String(phase.index + 1))); + const label = add(name, el('span', 'mlv-ovblock__label', phase.label)); + label.title = 'Phase ' + (phase.index + 1) + ': ' + phase.label; + const meta = add(head, el('div', 'mlv-ovblock__meta')); + add(meta, el('span', 'mlv-ovblock__steps', blockStepsText(phase))); + const total = countsTotal(phase.findings); + if (total > 0) { + const cluster = severityCluster(phase.findings, 12, phaseFindingsSpoken(phase.findings)); + if (cluster) meta.appendChild(cluster); + add(meta, el('span', 'mlv-ovblock__unit', phaseFindingsText(total))); + } + const steps = add(box, el('ul', 'mlv-ovblock__list')); + steps.id = this.uid + '-list-' + i; + box.setAttribute('aria-describedby', steps.id); + for (const item of block.items) { + const row = add(steps, el('li', 'mlv-ovitem')); + row.style.left = round1(item.x - block.x) + 'px'; + row.style.top = round1(item.y - block.y) + 'px'; + row.style.width = Math.max(0, item.w) + 'px'; + if (item.step < 0) { + row.classList.add('mlv-ovitem--more'); + row.textContent = '… ' + plural(item.more, 'more step', 'more steps'); + add(row, el('span', 'mlv-sr', '.')); + continue; + } + const step = phase.steps[item.step]; + row.setAttribute('data-node-ref', step.id); + if (item.depth) row.setAttribute('data-depth', String(item.depth)); + if (step.basis && step.basis !== 'observed') row.setAttribute('data-basis', step.basis); + const mark = add(row, el('span', 'mlv-ovitem__mark', step.basis === 'unresolved' ? '?' : '')); + if (step.basis === 'inferred') mark.appendChild(inferredMark()); + mark.setAttribute('aria-hidden', 'true'); + if (step.basis === 'inferred' || step.basis === 'unresolved') add(row, el('span', 'mlv-sr', step.basis + ': ')); + const text = add(row, el('span', 'mlv-ovitem__title', step.title)); + text.title = step.title + (step.basis && step.basis !== 'observed' ? ' (' + step.basis + ')' : ''); + if (step.tags.length) { + const tags = add(row, el('span', 'mlv-ovitem__tags')); + for (const tag of step.tags) { + const t = add(tags, el('span', 'mlv-ovitem__tag')); + t.setAttribute('data-sev', tag.severity); + t.title = tag.short + ' (' + tag.id + '), ' + SEVERITY_WORD[tag.severity] + ' severity'; + const glyph = severityGlyph(tag.severity, 10, ''); + glyph.setAttribute('aria-hidden', 'true'); + glyph.removeAttribute('role'); + t.appendChild(glyph); + add(t, el('span', 'mlv-ovitem__tagid', tag.short)); + } + // The tags are drawn for the eye; a screen reader hears the F labels once, in words. + tags.setAttribute('aria-hidden', 'true'); + add(row, el('span', 'mlv-sr', ', ' + (step.tags.length === 1 ? 'finding ' : 'findings ') + step.tags.map((t) => t.short).join(', '))); + } + // A separator after each title, so the block's description does not run them together. + if (step.tags.length || !/[.!?…]$/.test(step.title.trim())) add(row, el('span', 'mlv-sr', '. ')); + else add(row, el('span', 'mlv-sr', ' ')); + } + on(box, 'click', (ev: MouseEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + this.active = i; + this.cb.go(phase.id, ev.detail === 0); + }); + on(box, 'focus', () => { + if (this.active === i) return; + this.active = i; + for (const [j, b] of this.blockEls.entries()) b.tabIndex = j === i ? 0 : -1; + }); + this.blockEls.push(box); + }); + } + + private onKey(ev: KeyboardEvent): void { + if (ev.ctrlKey || ev.metaKey || ev.altKey) return; + const key = ev.key; + const n = this.blockEls.length; + let handled = true; + // Escape is not handled here: the canvas's cascade (ui/appkeys.ts) closes what is on top of the + // overview first (the legend, the phase list) and leaves the overview on the next press. + if (key === ')' || (key === '0' && ev.shiftKey)) this.cb.back(); + else if (key === 'ArrowDown' || key === 'ArrowRight') this.focusBlock(Math.min(n - 1, this.active + 1)); + else if (key === 'ArrowUp' || key === 'ArrowLeft') this.focusBlock(Math.max(0, this.active - 1)); + else if (key === 'Home') this.focusBlock(0); + else if (key === 'End') this.focusBlock(n - 1); + else if (key === 'Enter' || key === ' ') { + const phase = this.input && this.input.phases[this.active]; + if (phase) this.cb.go(phase.id, true); + } else handled = false; + if (!handled) return; + // Marked handled (the panel's bootstrap then keeps VS Code from acting on an Escape), and kept + // from the canvas's own keys under the overlay (the arrows would move the selection). + ev.preventDefault(); + ev.stopPropagation(); + } + + destroy(): void { + for (const dispose of this.disposers) { + try { + dispose(); + } catch (_e) { + /* already torn down */ + } + } + this.disposers = []; + } +} + +/** + * The block's accessible name: the phase, its counts with their units, and its connections to + * other phases, as sentences. The titles are its description (the list inside it). + */ +export function blockName(input: OverviewInput, at: number): string { + const phase: OverviewPhase = input.phases[at]; + const last = input.phases.length ? input.phases[input.phases.length - 1].index + 1 : 0; + const parts = ['Phase ' + (phase.index + 1) + ' of ' + Math.max(last, input.phases.length) + ': ' + phase.label + '.', blockStepsText(phase) + '.']; + const total = countsTotal(phase.findings); + if (total > 0) parts.push(phaseFindingsText(total) + '.'); + const out: string[] = []; + for (const link of input.links) { + if (link.from !== at) continue; + const to = input.phases[link.to]; + out.push(plural(link.count, 'connection', 'connections') + (isNextLink(link) ? ' to' : link.to < link.from ? ' back to' : ' ahead to') + ' phase ' + (to.index + 1)); + } + if (out.length) parts.push(out.join('; ') + '.'); + parts.push('Enter goes to this phase.'); + return parts.join(' '); +} + +/** The inferred mark, ◌, as a dotted ring the stylesheet draws (`.mlv-ovmark`). */ +function inferredMark(): HTMLElement { + const mark = el('span', 'mlv-ovmark'); + mark.setAttribute('aria-hidden', 'true'); + mark.setAttribute('data-mark', '◌'); + return mark; +} + +function doc(node: Node): Document { + return node.ownerDocument || document; +} + +function place(element: HTMLElement, r: { x: number; y: number; w: number; h: number }): void { + element.style.left = r.x + 'px'; + element.style.top = r.y + 'px'; + element.style.width = r.w + 'px'; + element.style.height = r.h + 'px'; +} + +/** + * Scroll the overlay so the focused block is wholly in view below the sticky header (`covered` + * pixels of the top), or shows its top when it is taller than the room. The first block scrolls + * the overlay to its top, so the header shows whole even when it does not stick (it used to stop + * 12 px above the block, with the header out of view). + */ +function scrollIntoViewIfNeeded(scroller: HTMLElement, block: HTMLElement, covered: number, first: boolean): void { + const top = block.offsetTop; + const bottom = top + block.offsetHeight; + const view = scroller.clientHeight; + if (!(view > 0)) return; + let next = scroller.scrollTop; + if (top - 12 < next + covered) next = top - 12 - covered; + else if (bottom + 12 > next + view) next = Math.min(top - 12 - covered, bottom + 12 - view); + if (first && bottom + 12 <= view) next = 0; + next = Math.max(0, next); + if (next !== scroller.scrollTop) scroller.scrollTop = next; +} + +function round1(v: number): number { + return Math.round(v * 10) / 10; +} diff --git a/webview/src/ui/rail.ts b/webview/src/ui/rail.ts index 1e3ce0f3..3f25d8e9 100644 --- a/webview/src/ui/rail.ts +++ b/webview/src/ui/rail.ts @@ -16,6 +16,7 @@ import { renderIssuePanel } from './issuelist.js'; import { renderOutlineTree } from './outline.js'; import type { RelationMode } from './outline.js'; import { renderSelectionPane } from './selection.js'; +import type { WalkMark } from './selection.js'; import { renderAboutPane } from './about.js'; import type { Issue, Loc, MLEdge, MLNode, RailTab, RelatedLoc, StaleReason, WorkflowDocument } from '../types.js'; import type { GraphIndex } from '../layout/model.js'; @@ -81,6 +82,8 @@ export interface RailState { document: WorkflowDocument | null; /** Viewer M2: the Selection pane's columns (two in a sheet at least 620 px wide). */ columns: 1 | 2; + /** Viewer M3: the review walk's quote mark for the Selection pane, while it shows the walk's claim. */ + walk?: WalkMark | null; } /** The tabs, in order. The Selection tab keeps the id `inspector` (saved view states use it). */ @@ -112,6 +115,12 @@ export class Rail { private current: RailTab = 'about'; private mode: 'docked' | 'sheet' = 'docked'; private open = true; + /** + * The claim the Selection pane was last built for (kind, id and revision). A new claim starts the + * pane at its top, so its title shows: the pane is one scroller kept across claims, and it used to + * keep the offset of the claim before (M3 live check, W1). + */ + private selectionKey = ''; constructor(cb: RailCallbacks) { this.cb = cb; @@ -494,6 +503,7 @@ export class Rail { columns: s.columns, document: s.document, staleReason: s.staleReason, + walk: s.walk || null, }, { onOpen: (loc, focusEditor) => this.cb.onOpen(loc, focusEditor), onShowNode: (id) => this.cb.onShowNode(id), @@ -503,6 +513,14 @@ export class Rail { onRefine: () => this.cb.onRefine(), onShowLimitations: () => this.cb.onShowLimitations(), }); + // The same claim built again (a host answer, a freshness change, a resize) keeps the reader's + // place; another claim starts at the top. The walk then brings its quote into view. + const claim = s.selectedIssue ? 'issue:' + s.selectedIssue.id : s.selectedEdge ? 'edge:' + s.selectedEdge.id : s.selectedNode ? 'node:' + s.selectedNode.id : ''; + const key = claim + '@' + (s.document && s.document.revision ? s.document.revision.id : ''); + if (key !== this.selectionKey) { + this.selectionKey = key; + panel.scrollTop = 0; + } } private renderOutline(s: RailState): void { diff --git a/webview/src/ui/searchcontroller.ts b/webview/src/ui/searchcontroller.ts index 89fb198a..dd156ee6 100644 --- a/webview/src/ui/searchcontroller.ts +++ b/webview/src/ui/searchcontroller.ts @@ -85,6 +85,8 @@ export class SearchController { handleKey(ev: KeyboardEvent): void { if (ev.key === 'Escape') { + // Viewer M3: marked handled, so the panel's bootstrap keeps VS Code from also acting on it. + ev.preventDefault(); this.clear(); this.host.onQueryChanged(''); this.host.blurToCanvas(); diff --git a/webview/src/ui/selection.ts b/webview/src/ui/selection.ts index c03f238d..07fbb502 100644 --- a/webview/src/ui/selection.ts +++ b/webview/src/ui/selection.ts @@ -34,6 +34,8 @@ import { edgeKindText } from '../render/edges.js'; import { issueStaleReasons, staleChipText, suggestionBlock, wireOpenControl } from './issuelist.js'; import { allElsewhere, staleQuotes, STALE_TEXT } from '../freshness.js'; import { uiIcon } from '../icons.js'; +import { walkQuoteText } from '../walk.js'; +import type { WalkOpenStatus } from '../walk.js'; import type { GraphIndex } from '../layout/model.js'; import type { Issue, Loc, MLEdge, MLNode, RelatedLoc, Sel, StaleReason, WorkflowDocument } from '../types.js'; @@ -55,6 +57,17 @@ export interface SelectionPaneState { /** The displayed revision: its published hashes decide "unchanged" against "not checked". */ document: WorkflowDocument | null; staleReason?(file: string): StaleReason | undefined; + /** + * Viewer M3: while the review walk shows this claim, which of its quotes the walk asked the + * editor for, and what became of it (`applyWalkMark`). + */ + walk?: WalkMark | null; +} + +/** Viewer M3: the review walk's quote in the pane and what the editor beside shows for it. */ +export interface WalkMark { + quote: number; + status: WalkOpenStatus; } export interface SelectionPaneCallbacks { @@ -96,6 +109,114 @@ export function renderSelectionPane(panel: HTMLElement, s: SelectionPaneState, c } else { add(root, el('p', 'mlv-empty-note', 'Select a step, a connection or a finding to read its claim beside its evidence.')); } + applyWalkMark(panel, s.walk || null); +} + +/** + * Viewer M3: mark the review walk's quote in the pane under `container`: a left rule on that quote + * (`.mlv-quote.is-walk`, `data-walk-status`) and one line under it saying what the editor beside + * shows: opening, highlighted, or why it was not opened (the host's reason). Any older mark goes. + * Updated in place when the host answers, so the pane keeps its scroll and focus. `reveal` brings + * the marked quote into view (`revealWalkQuote`), at once: no smooth scroll, so nothing moves under + * reduced motion. `'claim'` (the walk moved to another claim) keeps the claim's title in view and + * shows the quote too only when both fit; `'quote'` (`[` or `]` moved to another quote) shows the + * quote, keeping the title when both fit. + */ +export function applyWalkMark(container: HTMLElement | null, mark: WalkMark | null, reveal: WalkReveal | false = false): void { + if (!container) return; + for (const old of Array.from(container.querySelectorAll('.mlv-quote.is-walk'))) { + old.classList.remove('is-walk'); + old.removeAttribute('data-walk-status'); + const line = old.querySelector('.mlv-quote__walk'); + if (line) line.remove(); + } + if (!mark) return; + const quotes = container.querySelectorAll('.mlv-sel .mlv-insp__source-evidence > .mlv-quote'); + const li = quotes[mark.quote] as HTMLElement | undefined; + if (!li) return; + li.classList.add('is-walk'); + li.setAttribute('data-walk-status', mark.status.state); + const text = walkQuoteText(mark.status); + if (text) { + const line = el('p', 'mlv-quote__walk'); + if (mark.status.state === 'blocked' || mark.status.state === 'failed') line.appendChild(uiIcon('warning', 12)); + add(line, el('span', '', text)); + const head = li.querySelector('.mlv-quote__head'); + if (head && head.nextSibling) li.insertBefore(line, head.nextSibling); + else li.appendChild(line); + } + if (reveal) revealWalkQuote(li, reveal === 'claim'); +} + +/** How the walk brings its quote into view: after a step to another claim, or after `[` or `]`. */ +export type WalkReveal = 'claim' | 'quote'; + +/** Room kept between a revealed quote (or the title) and the pane's edge. */ +const REVEAL_MARGIN = 8; + +/** + * Where the pane's scroll should be so the walk's quote shows, moving the least from `current` + * (M3 live check, W1). Positions are in the pane's content, in pixels; `view` is the pane's + * height. What must show is the quote's file line and the walk's line under it (what the editor + * beside shows for it): `quoteTop` to `quoteBottom`, cut to the pane's height. When the claim's + * title fits in the pane together with that, the title stays in view too; when they do not fit + * together, the quote wins. The quoted lines below may run past the fold: the editor beside shows + * them highlighted. `titleFirst` (a step to another claim): when they do not fit together, the + * title wins instead, as the reader meets the claim before its quotes, and the editor beside + * already shows the quoted lines highlighted. + */ +export function walkRevealTop(current: number, view: number, quoteTop: number, quoteBottom: number, titleTop: number | null, titleFirst = false): number { + if (!(view > 0)) return current; + const bottom = Math.min(quoteBottom, quoteTop + view - 2 * REVEAL_MARGIN); + const lowest = bottom + REVEAL_MARGIN - view; + let highest = quoteTop - REVEAL_MARGIN; + if (titleTop !== null && titleTop - REVEAL_MARGIN >= lowest) highest = Math.min(highest, titleTop - REVEAL_MARGIN); + else if (titleTop !== null && titleFirst) return Math.max(0, Math.round(Math.min(current, titleTop - REVEAL_MARGIN))); + if (highest < lowest) highest = lowest; + return Math.max(0, Math.round(Math.min(Math.max(current, lowest), highest))); +} + +/** How long after a reveal a change of the pane's height (a bottom panel still opening) redoes it. */ +const REVEAL_SETTLE_MS = 500; + +/** + * Scroll the tab panel that holds the walk's quote `li` so its head and walk line show + * (`walkRevealTop`), at once. A step can open the bottom panel as it goes; under reduced motion the + * stylesheet turns that into a 0.01 ms transition, which the browser starts on the next frame, so + * the pane still has its collapsed height when the step measures it (headless Chrome: 16 px + * instead of 315). When the pane's height changes soon after, the reveal runs again from where the + * pane was. + */ +function revealWalkQuote(li: HTMLElement, titleFirst: boolean): void { + const scroller = li.closest('[role="tabpanel"]') as HTMLElement | null; + if (!scroller) return; + const origin = scroller.scrollTop; + const view = revealFrom(scroller, li, origin, titleFirst); + const win = scroller.ownerDocument ? (scroller.ownerDocument.defaultView as (Window & { ResizeObserver?: typeof ResizeObserver }) | null) : null; + const RO = win ? win.ResizeObserver : undefined; + if (!win || !RO) return; + const observer = new RO(() => { + if (scroller.clientHeight === view) return; + observer.disconnect(); + if (li.isConnected && li.classList.contains('is-walk')) revealFrom(scroller, li, origin, titleFirst); + }); + observer.observe(scroller); + win.setTimeout(() => observer.disconnect(), REVEAL_SETTLE_MS); +} + +/** One reveal of `li` in `scroller`, counted from the scroll offset `from`; the pane height it used. */ +function revealFrom(scroller: HTMLElement, li: HTMLElement, from: number, titleFirst: boolean): number { + const view = scroller.clientHeight; + if (!(view > 0)) return view; + const box = scroller.getBoundingClientRect(); + const at = (r: { top: number }) => r.top - box.top + scroller.scrollTop; + const quote = li.getBoundingClientRect(); + const line = li.querySelector('.mlv-quote__walk') || li.querySelector('.mlv-quote__head'); + const end = line ? at(line.getBoundingClientRect()) + line.getBoundingClientRect().height : at(quote) + quote.height; + const title = scroller.querySelector('.mlv-sel .mlv-insp__title'); + const next = walkRevealTop(from, view, at(quote), end, title ? at(title.getBoundingClientRect()) : null, titleFirst); + if (next !== scroller.scrollTop) scroller.scrollTop = next; + return view; } /** The claim column and the evidence column; one container when the pane has one column. */ diff --git a/webview/src/ui/shell.ts b/webview/src/ui/shell.ts index ccf241c1..9f239813 100644 --- a/webview/src/ui/shell.ts +++ b/webview/src/ui/shell.ts @@ -2,7 +2,7 @@ * The application shell: the DOM skeleton and the canvas gestures. * * Structure only — no graph knowledge. `buildShell` returns the slots the app - * fills (chrome, rail, tooltip, minimap, toasts, states) and `wireCanvasGestures` + * fills (chrome, rail, tooltip, toasts, states) and `wireCanvasGestures` * attaches pan, wheel-zoom and background-click, returning their disposers. */ @@ -15,8 +15,8 @@ import type { ViewportController } from '../render/canvas.js'; export interface Shell { body: HTMLElement; /** - * The `
` landmark around the diagram (VIEW-12). The canvas, the minimap - * and the Pipeline Answer Card live inside it; the rail is a sibling `