Two surfaces, each with its own journeys: the desktop app and the
website (site/, netscli.com — landing page, docs and changelog).
The flows an acceptance pass should walk, step by step, against a running build and a served site. Written from the shipped app; each step below was observed rather than intended. Where a step depends on privileges or the local network, the condition is stated so a failure can be attributed correctly.
The CLI, TUI and MCP surfaces have no walkthrough: they are exercised by
docs/ and by their own tests, and neither has states a person navigates.
Scope: apps/netscli-gui.
See what is on this network, and confirm whether a given host or port is reachable — without opening a terminal.
Everything else in the app supports that: the other tools are the follow-up questions you ask once discovery has told you an address exists.
-
Launch. The window shows a Discover tab, pre-filled with the subnet of the default interface. Discover is the entry point because the first useful question on an unfamiliar network is "what is on it", and a scan needs a host you do not have yet.
-
Run discovery. Press the run button, which reads "Discover" here because it carries the tool’s own name. The chevron beside it offers "Clear ARP table, then discover", on discover, sweep and the ARP tab only. Progress replaces the empty table while the sweep runs.
-
Read the results. Each host is a row: IP, hostname, MAC, vendor, RTT, and Found by, reading either
probe replyorneighbour table. The second means the host answered nothing and is only remembered by the OS, which can outlive the device — so it is the column to distrust when something looks stale. Selecting the row says the same in words.The column exists because the app used to call every discovered host a “Responded host”, including the ones that had answered nothing.
-
Select and inspect. Click a row and the detail pane below shows that row's fields and the raw result. The arrow keys move the selection; Ctrl+A selects all.
-
Ask a follow-up. A port scan or inspect tab opened from a host appears at the end of the strip, pre-filled with that host, and starts running at once — the click named the host, which is the only thing the tool needed. Each tool is a tab, and tabs are independent — a long sweep in one does not block another.
-
Read the equivalent command. The command strip under the results shows the
netscli …invocation matching the current form, so anything done in the UI can be reproduced in a shell or a script. -
Take the results away. Export JSON or CSV and the file appears on disk — with interaction toasts off, which is the default, that is the only signal it worked. Copying selected rows and saving a result bundle are the other two exits, and are just as quiet when they succeed. Failure is not optional — it goes to the tab's error strip whatever the toast preferences say, because a failed export writes nothing, and a silent nothing is indistinguishable from a success.
Each tool opens in a tab, and the strip is the only place they can be
reordered or closed. Walked at 1100x800 with three tabs open, which the strip
labels Discover: 192.168.1.0/24, Scan: 127.0.0.1 #2 and
Scan: 127.0.0.1 #3 — three because the disabled states below only appear
once a tab has neighbours on both sides.
Those numbers are worth reading before the steps: a tab whose identifier is
shared with another gets # and its position in the strip, not its place
among the duplicates. So the first duplicate here is #2, there is no #1,
and reordering renames tabs. See Known gaps.
-
Drag a tab to reorder it. Press on a tab and move sideways; past about 5px of travel the press becomes a drag. The strip reorders live — crossing a neighbour's midpoint swaps them while the button is still down, so the order under the cursor is the order you get. Observed: from
[Discover, Scan, Scan], dragging the first tab past the second gives[Scan, Discover, Scan]while still held, and releasing keeps it.The strip's own drag-to-scroll still applies to the space around the tabs, and the wheel scrolls either way. Grabbing a tab reorders it instead, because otherwise there is no gesture left for the thing a tab drag is expected to do.
-
A cancelled drag puts the tab back. A drag the system interrupts (
pointercancel) returns to the order before the press, not to wherever the gesture died — by then the tab has usually moved several times. Observed: dragged to[Discover, Scan, Scan], cancelled, back to[Scan, Discover, Scan]. -
Ctrl+Shift+Left/Right moves the focused tab one place, so reordering is not pointer-only. Focus travels with the tab, so a repeated press walks the same tab down the strip rather than swapping a pair back and forth. It stops at the ends rather than wrapping. Plain Left/Right moves the selection instead and does wrap: from the last tab, Right selects the first.
Observed with native key events, from
[Discover, Scan #2, Scan #3]with Discover focused: one press gives[Scan, Discover, Scan]with focus on index 1, a second gives[Scan, Scan, Discover]with focus on index 2. -
Right-click a tab for the close menu: Close, Close others, Close to the right, Close to the left, then Close all after a separator. Items that would do nothing are disabled rather than hidden, so the menu keeps the same shape as you move along the strip. Observed on the first of three tabs, "Close to the left" is disabled; on the last, "Close to the right" is. With a single tab open, others / right / left are all disabled while Close and Close all stay enabled.
Each of these is a real state the app can be in, and each is worth exercising deliberately.
| State | What should happen |
|---|---|
| Empty — a tab created but never run | Form and Run control visible; the table shows no rows and does not pretend to. |
| Running | Progress with counts; Stop is enabled; other tabs stay usable. |
| Empty result — the run succeeded and found nothing | Says "This run completed and found nothing." Distinct from "not yet run", and from a filter hiding every row, which says that instead. A /30 with nothing on it is a legitimate answer, not a failure. |
| Operation failure | The tab's error strip shows the reason. It is unconditional and not preference-gated — a failed run leaves the table empty, so without it the cause is invisible. |
| Privilege failure | Named explicitly, e.g. "Failed to clear ARP table: … requires elevation." The run that depended on it is suppressed, not continued, because a discover after a failed ARP clear looks identical to one after a successful clear. |
| Capability unavailable — e.g. packet capture without the driver | The tool reports why rather than failing opaquely. |
| Completion, tab in background | A toast carrying an "Open tab" action, only with operation toasts enabled — which is not the default. At defaults this state is silent, and that is correct. |
| Completion, tab in foreground | No toast. The result arriving in the table is the signal; repeating it teaches people to ignore toasts. |
| Preferences at defaults | Toasts off; concurrency 256; opens on Discover. A profile left at 1 probe by the pre-0.3.1 defect is repaired once, on upgrade. |
| Tab dragged, button still down | The strip has already reordered and the dragged tab is faded in its new slot. Nothing is committed until release, but nothing is hidden either — what you see is what you will get. |
| Drag cancelled | The order returns to what it was before the press. |
| No tabs open — the last tab closed, or Close all | The workspace shows "No Tabs Open" with "Choose Scan" and "Choose Tool" actions. Closing everything is a legitimate state, not a blank window. |
Recorded so an acceptance pass does not report them as new:
- The end-to-end suite (
npm run test:tauri-render) runs only locally, never on hosted CI, because it builds and drives the real app. - Selenium's native
.click()does not register as a React click against the attached WebView2 session; a DOM.click()does. A menu item that appears dead under the harness may be working for a user, and vice versa — verify interaction findings both ways before believing either. - The MSI has never been tested on a clean machine without WebView2 preinstalled.
- Reordering renames tabs, and the numbers can end up non-contiguous.
Duplicate tabs are numbered by their position in the strip rather than by
which duplicate they are (
tabDisplay.ts,#${index + 1}), so the number is not a name — it moves when anything around it moves. Observed: from[Discover: 192.168.1.0/24, Scan: 127.0.0.1 #2, Scan: 127.0.0.1 #3], moving the Discover tab one place right gives[Scan: 127.0.0.1 #1, Discover: 192.168.1.0/24, Scan: 127.0.0.1 #3]— the tab that was#2is now#1, and the two duplicates read#1and#3with no#2between them. A drag is the most likely way to hit this, which makes it awkward for the section above.
Scope: site/ — the landing page, the docs, and the changelog, served as a
static build. Written by walking a served production build at 1440px and
375px; the structure below is what is there, not what was intended.
This section did not exist while the site shipped, which is why nothing had ever walked it. Every journey here is one a person actually performs; a step that cannot be completed is a finding, not a curiosity.
Decide whether NetsCLI is worth installing, then install it.
Everything else on the site serves that or follows from it: the docs explain what you just installed, and the changelog says whether to update.
Search engines and language models are a second audience for the same content, which is why the copy has to answer a question rather than describe a feature. They are not a separate journey — a page that reads well for a person and states plainly what the tool does serves both.
- Land. The hero states what NetsCLI is in one line, with the current release, star count and download total beside it. Each of those three is hidden until GitHub confirms it, so an unauthenticated rate limit shows nothing rather than something wrong.
- Scan the surfaces section (
#surfaces) — desktop app, terminal UI, CLI, MCP server — and recognise which one is yours. - Reach the FAQ (
#faq), 13 questions in 4 groups: what it is, install and updates, interfaces and integrations, network workflows, limits and dependencies. These carry the comparison questions people actually search for ("alternative to Angry IP Scanner", "replace nmap"). - Leave for the source if that is the decision — the GitHub link is in the top nav and in the hero.
- Reach
#install, from the nav or the hero CTA. - Pick a platform — Windows, macOS, Linux tabs. The tab set is the first choice because a command for the wrong OS is worse than none.
- Pick a form — desktop app or CLI + terminal UI. These are separate products with separate package names, and the page must not blur them.
- Copy a command, or download an installer directly. Every command has
a copy button; the download links resolve through
/releases/latest/download/…so they never name a version that has moved on. - Verify, if you care — checksums and signing are stated with a link to the verification steps.
- Enter the docs, from the nav or a search engine landing on a deep page. Either is a first page, so every page has to stand alone.
- Orient: left sidebar for the section list, right rail for the contents of this page, breadcrumb for where you are.
- Search (
Ctrl K, or the button on narrow screens) when the nav does not have the word you are thinking of. - Follow an anchor to a heading, and share that link.
- Move between pages with the sidebar, keeping your place.
- Open the changelog, from the nav or from a version number.
- Read the newest release first; each entry says what changed and why it matters, with install, compatibility or security notes called out.
- Tell released from unreleased. An entry with no published release behind it is labelled and is not linked, because a link would 404.
- Hit a 404 and get a page that says so and offers a way back, rather than a dead end.
Each journey above has a narrow variant, and they are where this site has historically broken:
- The section list collapses behind a menu control; the page contents collapse into a dropdown under the hero.
- Search becomes a full-screen dialog rather than an inline field.
- The install tabs and command blocks must not overflow horizontally; a command you cannot read is a command you cannot copy.
| State | What should happen |
|---|---|
| First paint, no JavaScript | Every page's content is in the HTML, including the changelog entries. Nothing says "Loading". |
| GitHub unreachable or rate-limited | Hero metrics stay hidden; the changelog still lists every release from the repo's own file, unlinked and marked not-yet-released. Nothing shows a stale or invented number. |
| A version in the changelog with no release | Labelled "Not yet released", and deliberately not linked. |
| Search index unavailable | The dialog says so in its own voice, rather than appearing empty or hanging. |
| Theme: dark, light, or following the system | All three are selectable and all three are legible; the control shows which is active. |
| Deep-linked to an anchor | The target heading is visible and not hidden behind sticky chrome. |
| Narrow viewport | No horizontal overflow on any page. Every command block scrolls within itself rather than widening the page. |
| Navigation chrome is dropped; the article survives. |
Recorded so an acceptance pass reports what is new rather than what is already known. All were measured on a served production build.
- Page padding is asymmetric. At 1440px a docs page leaves 29px to the left of the sidebar and 92px to the right of the contents rail.
- The two narrow-screen header controls do not match. Search is
rgba(17,22,29,0.9)with argba(140,149,166,0.24)border; the menu toggle isrgba(255,255,255,0.035)with argba(255,255,255,0.1)border. Same size, same radius, different surface. - The install section is dense — 45 copy controls on one page — and the relative prominence of package manager, script and direct download has not been decided deliberately.
- Typography scale is unreviewed and reads large at desktop widths.
- The coverage matrix states capabilities as Yes/No where several rows are not applicable rather than absent, and it treats the CLI, TUI and MCP as fully separate when they share one binary and one core.
- Mobile search layout: the clear control and the cancel affordance compete, and the results panel does not extend to the bottom of the viewport.
- Anchor links jump the page rather than moving to the heading quietly.
- The sidebar's active marker shifts when a different item is hovered.
- Breadcrumb separators sit low relative to their text.
- The theme control's focus ring is drawn incorrectly, and its dropdown is unstyled.
No design-direction.md covers this surface: that document scopes itself to
the desktop app. Until one exists, "does this look right" has no written
answer here, and that is the root of most of the list above.