genspec-tui

command module
v0.36.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

genspec-tui

An interactive terminal front-end for codescan:

you may browse a Go source tree on the left, watch the Swagger spec it produces on the right, and see the scanner's diagnostics underneath — all regenerated on every save.

In either panel you may activate the "track mode": spec items point back to the source that created them, source line point to the spec item it generate and a diagnostic points to the source line that emitted it.

Its reason to exist is the loop: change an annotation, hit save, see the spec change. Beyond that it links the two sides together, so you can ask "which Go code produced this node?" and "what did this field turn into?" and get an answer by position rather than by guessing at names.

Intended audience: codescan/go-swagger maintainers and contributors - but any experienced spec author could benefit from it.

Install and run

genspec-tui is a separate Go module inside the codescan repo, so bubbletea and its dependency tree never reach the lean library.

go install github.com/go-openapi/codescan/cmd/genspec-tui@latest

# scan the module in the current directory
genspec-tui

# or point it somewhere, and narrow the scope
genspec-tui -workdir ../my-api ./internal/models/... ./internal/api/...

When working from a checkout, the repo's go.work wires the module to the local library:

go run ./cmd/genspec-tui -workdir ./testdata ./goparsing/petstore/...

The packages are positional, as they are for genspec and for every other Go command; naming none scans ./.... The older -packages flag still works when no package is named, so existing scripts and editor tasks keep running, but it is no longer the spelling to reach for.

Flags

Every codescan option is a flag here, declared once in cmd/internal/cliopts and shared with genspec and genspec-wasi — so a flag means the same thing whichever command you reach for, and -h lists the current set rather than this README going stale. They fall in four groups: scan (which code), go (what it is built as), load (how it is read) and emit (what the document says). -loader (go, own or auto) selects the toolchain-free loader.

A fifth group is this command's own, and observes the run rather than configuring it — see What a scan cost:

Flag Default Meaning
-profile false profile every scan: a CPU profile and per-phase allocation profiles, reported under m and written for go tool pprof
-profile-dir a fresh temp dir where -profile writes its .pprof files
-mem-profile-rate 0 with -profile, runtime.MemProfileRate: 0 samples every 512 KiB, 1 records every allocation exactly
A configuration file for the defaults

Flags may be preset in a .codescan.yaml, found by searching upwards from where the command was started — the same file, with the same sections, that genspec reads. Anything typed on the command line wins over it.

emit:
  scan-models: true
  prune-unused-models: true
profile:
  profile: true

The options naming a path are not among them — -workdir and -profile-dir — because a file found by searching upwards belongs to the tree being scanned, and that tree must not choose where the session reads or writes. They are typed:

genspec-tui -workdir ./api -profile -profile-dir ./profiles

-c / -config pins a file, -no-config rejects one. A section this command does not recognize — document:, which is genspec's — is skipped rather than rejected, so one file can serve both; a key that no flag matches inside a known section is an error, since a misspelling would otherwise read as a setting that quietly never applied. When a file is read, the status line says so and what it decided.

The file decides what the session starts with, not what it is stuck with: every boolean is toggled live with o, and the spec re-renders on close, which makes the popup the fastest way to see what an option such as EmitRefSiblings actually changes. The rows are grouped (discovery & scope · $ref & composition · naming · docs & comments · types & extensions · package loading), and a knob that only bites in combination says so — PruneUnusedModels shows (needs ScanModels) until that one is on, and EmitXGoType shows (moot: SkipExtensions) while extensions are suppressed.

The value-typed options are flags and file keys rather than popup rows, since a checkbox list cannot express them. The one option with no route in at all is InputSpec (overlay mode).

Layout

┌───────────────────────┬──────────────────────────────────────┐
│ source tree           │ spec · JSON                          │
│  or the file viewer   │  the generated document              │
├───────────────────────┴──────────────────────────────────────┤
│ diagnostics                                                  │
├──────────────────────────────────────────────────────────────┤
│ status / help                                                │
└──────────────────────────────────────────────────────────────┘

The left pane shows either the source tree or, once you open a file, the file viewer. The viewer is read-only and navigable by default; i turns it into an editor and Esc steps back out. Saving writes to disk, the watcher notices, and the spec re-renders.

Clicking a pane focuses it, and the mouse wheel scrolls whichever pane is under the pointer — Tab is never required.

Both dividers move: ctrl+←/ctrl+→ for the one between the left pane and the spec, ctrl+↑/ctrl+↓ for the one above the diagnostics. Each key travels in its own arrow's direction, and they work from every mode including the editor — wanting more room for what you are typing in is exactly when you reach for a resize.

The dividers are held as proportions, not cell counts, so resizing the terminal keeps the layout you chose rather than letting one pane absorb the difference. They stop short of either edge: a pane driven to nothing could not be dragged back, because the keys that would restore it are advertised in a status line it no longer has room for.

The binding surface is context-dependent — f follows from three different panes, Enter opens a file in the tree but follows a $ref in the spec — so the header carries a standing h: help banner, and h (or ?) opens the full keymap grouped by pane. The table below mirrors that overlay.

A rescan keeps you where you were: the cursor is restored to the same node, not the same line number, so a definition appearing above what you are reading does not slide you somewhere else. If that node is gone — you deleted the type — the cursor falls back to its nearest surviving ancestor.

Keys

Anywhere
Key Action
h / ? the key-bindings overlay (also advertised in the header)
Tab / shift+Tab cycle focus forward / backward
ctrl+← / ctrl+→ move the divider between the left pane and the spec
ctrl+↑ / ctrl+↓ move the divider above the diagnostics strip
click focus the pane under the pointer — or, on a swagger: directive, show what it means
wheel scroll the pane under the pointer
c copy the focused pane's raw content to the clipboard
r rescan now
v / V validate the generated spec / switch the diagnostics pane between scan and validation
F5 reload the open file from disk (asks before discarding unsaved edits)
o scanner options popup (space toggles, Esc/o applies and rescans)
m what the last scan cost — wall clock and memory, split between scanning and rendering; the profiles too under -profile
ctrl+q / ctrl+c quit
Spec pane
Key Action
/ j k move the cursor
PgUp / PgDn move it a page (the view never leaves the cursor behind)
Home / End first / last line
ctrl+j / ctrl+y render as JSON / YAML — keeps you on the same node, not the same line. The YAML is converted the first time it is asked for, since it costs more than the JSON it is made from
/ search; n / N step through matches
f toggle follow mode (spec drives, the source pane mirrors)
F3 / shift+F3 next / previous reference to the node under the cursor
Enter follow the $ref under the cursor to its definition
Esc clear the search and the reference cycle

The spec pane has a line cursor, and everything above acts on the node under it. Searching parks the cursor on the match, so / then F3 or Enter composes.

Source tree
Key Action
/ j k move the selection
PgUp / PgDn, Home / End move a page at a time, or jump to the ends
/ collapse / expand a directory
Enter open a file (or expand/collapse a directory)
g locate the selected file's first node in the spec
File viewer (read-only)
Key Action
/ j k move the navigation line
PgUp / PgDn, Home / End move a page at a time, or jump to the ends
f toggle follow mode (source drives, the spec mirrors)
K what the swagger: annotation on this line means
i / Enter start editing
Esc back to the tree

The viewer shadows only these keys; every other binding (/, o, r, g, ctrl+j / ctrl+y, Tab, c) still works while a file is open.

File editor
Key Action
ctrl+f jump from the cursor's line to the spec node it produced
ctrl+s save (triggers a rescan)
F5 reload from disk, discarding the edits (asks first)
Esc back to the read-only viewer

ctrl+f rather than f because the editor owns plain f for typing.

Annotation reference (K)

With the viewer's navigation line on a comment carrying a swagger: directive, K shows what that annotation does: its syntax, a one-line summary, and what may be written in its body.

K rather than a letter of its own because it is vim's "look up what is under the cursor", and what LSP clients bind hover to. It reads the buffer, so it works on an annotation you are still typing — which is when it is wanted.

Clicking the directive does the same. The pointer has to be on the swagger:… token itself, not merely on its line: clicking a pane to focus it is the most ordinary thing you do here, and it must not throw a popup up because the pointer came to rest inside a comment. The click asks "what is that" and leaves the navigation line where it was.

The entries cover the twenty swagger: annotations. Individual body keywords (required, minLength, enum, …) are not documented one by one — there are too many for a popup to be the right place — so each annotation's entry says instead which family of keywords its body accepts.

What a scan cost (m)

m opens a card describing the run that just finished: how long it took, what it allocated, what it left live, and how much the process holds from the OS. Time and memory are both split between scanning (codescan) and rendering (serializing the document as JSON), because the run is fenced three times — before the scan, after it, and after rendering. Under -profile the same two phases are bracketed by the profiler, so what the sampler says and what the fences say describe the same halves of the same work.

A split line at the top recaps the whole thing as ratios — time 96% / 4% · memory 86% / 14%, and cpu too under -profile — because the question a reader usually arrives with is which phase is this?, and two figures in different units is a comparison they would otherwise do in their head. Time and memory can disagree sharply, which is why both are there.

It is a modal rather than another status-line field: the status line already carries the pane hints, the follow badge and the search prompt, and this is a figure most sessions never ask for.

Two things those figures are not, both stated on the card:

  • The window is process-wide. runtime.ReadMemStats accounts for the whole process, and while a scan runs the redraw loop, the spinner and the file watcher allocate on other goroutines.
  • A rescan holds two documents. The previous spec is only released once the new one lands, so on a rescan the retained figure reads high by about one document. That is arithmetic, not a leak.
Profiled runs (-profile)

A scalar cannot say who spent it, which is the ceiling on everything above. Start with -profile and each scan is also profiled: the card then reports where the CPU went and what allocated it, per phase, by function. The process-wide caveat stops mattering, because the noise arrives named — a redraw row you discount rather than a confound you cannot separate.

genspec-tui -profile -workdir ../my-api

# every allocation counted rather than sampled every 512 KiB — accurate, and slow
genspec-tui -profile -mem-profile-rate=1 -workdir ../my-api

The card grows scrolling when it outgrows the terminal (↑↓/jk, PgUp/PgDn, Home/End), and names the artifacts it wrote, with the commands that open them:

go tool pprof -http=: <dir>/cpu-scan.pprof
go tool pprof -http=: -base <dir>/mem-before.pprof <dir>/mem-after-scan.pprof
What the CPU table is charged to

A leaf frame answers "what was executing", which for this program is mostly the allocator and the collector — true, and nothing anyone can act on. So a sample is charged to the call of ours that led there, and what it called: the boundary where codescan hands the work to somebody else's code.

  38%    720ms   the runtime itself — collecting, allocating, scheduling
  26%    490ms   internal/packages.(*loadState).loadDir → go/types.(*Config).Check
  25%    480ms   internal/packages.(*loadState).loadDir → go/parser.ParseFile
   3%     60ms   packages/vfs.(*FS).ReadDir → os.(*unixDirent).Info

Read the pair as what the time went intowhere to go and change it. Three rules follow from it:

  • A row covers everything under the boundary, so these are not flat times. Every sample is still charged exactly once, so the shares partition the phase.
  • Garbage collection, the allocator and the scheduler have no call of ours above them, and get one row rather than eight naming the runtime's internals. It is not a bottleneck to look up: it is the price of the allocation table below, which is where something can be done about it.
  • Work on a goroutine we did not start (the loader's own) is charged to what that goroutine is doing, not to the errgroup wrapper every one of them roots in.

Function literals fold into the function they were written in: refine.func2.1 and refine.func3 are one piece of work seen at two of its entrances.

Symbols keep their last two path segments, because one is not enough to compare runs by: internal/packages is this project's own loader and go/packages is the one it stands in for, and both end in packages.

The allocation table is charged differently — to the innermost frame outside the runtime, the one that actually asked for the memory. types.(*Checker).recordTypeAndValue at 123 MB says the type-checker's own result set is the memory, which a boundary row would have hidden inside types.(*Config).Check.

Five artifacts are written: a CPU profile per phase (cpu-scan, cpu-render) and three heap snapshots — before, after scanning, after rendering — so -base reproduces either phase in the real tool. What the in-TUI tables give you is the answer without leaving the session; the flame graph is one command away.

Caveats worth knowing before reading a table:

  • Sampling. At the default rate the heap profiler records one allocation per 512 KiB, and the figures are scaled back up the way pprof does. The card says estimated from sampling, or every allocation counted under -mem-profile-rate=1.
  • Samples, not measurements. The CPU profiler runs at 100 Hz, so a scan under a second yields a few dozen samples and cannot rank functions; the card says so rather than dressing a handful of samples up as a ranking. Both phases are bracketed, but rendering is often shorter than the sampler's 10 ms interval and simply catches nothing — which the card states, because an absent section reads as an oversight while "nothing sampled" is a measurement.
  • The observer is excluded. Stopping one phase's CPU profile flushes it through a compressor and starting the next allocates its buffers, all of it between two fences. Those allocations are dropped from the tables rather than reported: in the short phase they would otherwise crowd out its real sites. The sampler catches the profiler writing the profile, too — those samples are dropped from the CPU total as well, so the shares describe the run rather than the observation of it.
  • Observing costs. A profiled run collects at each fence and carries the sampler's overhead, so the summary figures at the top of the card are worse than the same run unprofiled. The tables are the accurate account.
  • Not a runtime toggle. The heap sampling rate is a property of the process and is fixed at launch: two runs in one session are then comparable.
  • One at a time. The CPU profiler is a process-wide singleton, so if a second scan starts while one is in flight (changing an option mid-scan), it runs without a CPU profile and says so.
  • The last run only. Every scan writes the same five filenames, so a rescan overwrites its predecessor. Copy them elsewhere before saving a file if you want to compare two runs.

Reloading

F5 re-reads the open file from disk. Disk is the source of truth here, so this is how you pick up a change made outside the TUI — a git checkout, a gofmt, another editor — and equally how you throw away edits you no longer want.

It asks first, and only when there is something to lose: with a clean buffer it just reloads. Answer y to discard, n or Esc to keep editing. Enter declines too — the destructive answer is the one you have to type on purpose.

Reload keeps the line you were on and always lands in the read-only viewer, even when it interrupted an edit. The line is restored by number, which is the honest approximation: unlike a rescan, where the spec cursor is restored by node, nothing anchors a line of Go source to anything stable across an edit made behind the TUI's back.

Diagnostics pane — scan tab
Key Action
/ j k select a diagnostic
PgUp / PgDn, Home / End select a page at a time, or jump to the ends
Enter go to this diagnostic's source line and focus it
f toggle follow mode (the selection drives, the source and spec panes mirror)
Diagnostics pane — validation tab
Key Action
/ j k select a finding
PgUp / PgDn, Home / End select a page at a time, or jump to the ends
Enter go to this finding's node in the spec
f toggle follow mode (the selection drives, the spec pane mirrors)

Validating the generated spec (v)

v runs the document through go-openapi/validate and lists what it finds in a validation tab of the diagnostics pane; V switches between that and the scan's own findings.

The two tabs answer different questions. The scan tab says whether your annotations were understood. The validation tab says whether the document they produced is legal Swagger 2.0 — a scan can be perfectly clean and still emit something a consumer rejects.

They also track different things, which is why they are tabs rather than one list. A scan diagnostic carries a source position, so it drives the source pane (and the spec). A validation finding carries only a JSON pointer, so Enter and f there drive the spec pane and nothing else.

The tab exists only once you have pressed v, and a rescan retires it: the findings judged the document that scan has just replaced, and a list of complaints about a spec that no longer exists invites navigating to nodes that may have moved or gone. Press v again.

Each finding carries the JSON pointer the validator recorded as it walked, taken from the result rather than read back out of the message. Navigation is therefore exact: an ordinary path, an indexed one (/paths/~1pets/get/parameters/0/type lands on that parameter rather than on the list), an entry of a required array, and a fault reached through a $ref, which is reported against the shared definition holding it.

A finding about something the document does not have is reported on the value that should hold it — a response missing its description lands on the response. At the top of the document that value is the document itself, which RFC 6901 spells as the empty pointer, so Enter goes to the top of the spec and the row is labelled (the whole document).

Resolution still walks up to the nearest node actually rendered if a pointer ever addresses something this view does not show, so an imprecise landing would be an ancestor of what was reported — never a sibling. Nothing is known to need it since validate v0.26.3.

Cross-reference navigation

Two indexes, rebuilt on every render, meet at a JSON pointer:

  • the spec index maps each rendered line to the pointer of the node on it;
  • the source index maps pointers to Go source positions, from codescan's OnProvenance callback.
Follow mode (f)

f turns on a persistent link between panes. The pane you pressed it in is the driver and keeps focus; the others mirror it on every cursor move, centring and highlighting the linked line. The roles are styled differently so it is always clear which pane leads. A SPEC ▸ SOURCE badge names the direction and the resolved target.

Follow works in three directions: spec → source, source → spec, and diagnostic → source and spec. Esc, a second f, changing focus, or starting to edit all leave it.

The diagnostics pane drives two followers where the others drive one, because it is not itself either end of the link — it is a third place naming a source position, and what a finding says is usually about what that position produced. The two halves resolve independently: a diagnostic on a line that produced no spec node is the ordinary case (a parse error means nothing was built from it), so the source half still resolves and the badge reports both.

References (F3, Enter)

F3 steps through the places the node under the cursor is referenced, wrapping; shift+F3 goes back. Enter follows a $ref to its definition.

A cycle stays anchored to one definition while you keep pressing F3. Scroll away and the next F3 re-anchors on wherever you now are.

Syntax highlighting

Both panes are coloured, by the same renderer and the same palette.

The spec pane is coloured by key, string, number, keyword and punctuation. The classification is free: the lexer that builds the line↔pointer index already identifies every token, so highlighting is a third product of the same walk rather than a second parse.

The source viewer is coloured by go/scanner — the standard library's own tokenizer, so no highlighting library is involved on either side.

Comments there get three classes rather than one, because in a spec generator a comment is not uniformly commentary:

Looks like Reads as Why
// swagger:model order a spec key the annotation declares the thing; it is the input that produced the pane next to it
// required: true a keyword grammar the parser acts on — same class Go's own type/func get
// the id of the order dimmed prose freeform description

Only the keyword itself is lifted out, so // required: true reads as dim //, coloured required, dim : true — the way "required": true reads on the spec side. Recognition uses the parser's own keyword table, so aliases (minminimum, min lengthminLength) and letter case come for free. The parser acts on exactly what lights up.

Diagnostics at the site

The scanner's own findings are drawn on the token they name, underlined in the severity's colour — red for an error, amber for a warning, blue for a hint. The diagnostics pane below tells you what and where; this tells you which token, without leaving the line you are reading.

Marks come from the last scan and are re-derived on every rescan, so they never outlive the finding that produced them. Where codescan reports a position is where the mark goes: a keyword-level diagnostic lands on the keyword, while swagger:type: "array" is deprecated lands on the declaration, because the builder reports it there. The mark means "there is a finding about this", not "this is deprecated".

Keyword scope

Keyword highlighting is scoped to files that declare at least one annotation. name, in and example are ordinary English words, and lighting them up in a file the scanner never reads would claim something untrue. Within such a file the scope is the whole file, not the comment block: a field's constraints live in the field's doc comment while the swagger:model that gives them meaning sits on the enclosing type.

Precedence on a line is cursor, then search match, then syntax. The first two answer questions you asked, so they take the whole line instead of competing with colour for it.

The gutter

Both panes mark which lines actually lead somewhere, so you can see what is navigable without probing for it:

Marker In the spec pane In the source viewer
this node has a source position of its own, so following it lands exactly there this line produced a spec node
a followable $refEnter goes to its definition

Only exact anchors are marked. Nearly every line resolves to something through its nearest anchored ancestor, so marking those would dot the whole document and tell you nothing. External $refs are not marked either, because Enter cannot follow them.

The gutter column only appears when there is something to mark.

Control characters are shown, never obeyed

Everything on screen comes out of a repository you are reading rather than one you wrote: the contents of a file, the names of the files and directories around it, and the diagnostics and validation findings quoting all three. An escape sequence in any of those is not text to a terminal — it is a command, and it can repaint the screen, retitle the window or overwrite a line with something other than what is in the file.

So no scanned text reaches the terminal unescaped. A control character is drawn as its Unicode picture instead: ESC as , NUL as , DEL as , a carriage return as . The C1 range and bytes that are not valid UTF-8 have no picture and are shown as . A crafted line therefore reads as its own disguise:

// swagger:model ␛[2J␛[3JPet

Each stand-in occupies exactly one column, so line numbers, syntax colouring, diagnostic marks and click targets all still land where they should.

The spec pane needs none of this: JSON and YAML both encode a control character rather than emit it, so the rendered document is already safe by the time the pane sees it. That is asserted by a test rather than assumed.

Prose that the panes lay out themselves — a diagnostic message, a validation finding — keeps its own line breaks and tabs, since neither can command a terminal.

Limitations

These are known and deliberate; the TUI says so rather than guessing.

  • Not every node has source. codescan anchors code-detail nodes — type declarations, fields, values, route and meta blocks — and finer nodes resolve to their nearest anchored ancestor. A node with no anchored ancestor at all was not produced from code (an InputSpec overlay node, for instance); the follower holds position and says so instead of jumping somewhere plausible.
  • Positions are as of the last scan. With unsaved edits in the buffer, every anchor below the edit has shifted, so follow shows a STALE badge. Saving triggers a rescan and clears it.
  • $ref resolution is a site index, not a resolver. References are found by scanning the rendered document. Local #/… refs are followable; a ref into another file or a URL is reported as external rather than chased. Ref-to-ref chains and $ref nested in allOf are not unwound.
  • Keyword highlighting cannot know which declarations are scanned. It knows the file is annotated and the word is in the grammar's table; it does not know whether codescan visits that particular type. A keyword-shaped line in an unrelated comment of an annotated file still lights up. Knowing better needs the AST, and the AST needs a file that parses — which the buffer you are editing may not.
  • The editor normalises whitespace. bubbles/textarea rewrites tabs as four spaces when a file is loaded into it, and treats a lone CR as a line break, so files are converted to LF on the way in. Neither has an exported knob. The viewer, the highlighter and the cross-ref line numbers all agree with each other because they all read the same normalised text — but Ctrl-S writes the buffer, so saving re-indents a tab-indented file with spaces and rewrites CRLF endings as LF. Edit and save here only when you are content with that; the VIM/VS-Code integration is the real answer.
  • Only the read-only source viewer is highlighted. bubbles/textarea owns its own rendering and emits the buffer verbatim, so edit mode shows plain text; Esc returns to the coloured viewer, re-tokenizing what you typed.
  • shift+F3 is terminal-dependent. bubbletea v1's key type carries no Shift modifier, and the xterm family reports shift+F3 as F15. Terminals that send something else have no previous-reference key; F3 still wraps around.
  • The split keys are terminal-dependent too. ctrl+arrow reaches us as CSI 1;5<A-D>, which most modern emulators send and some (notably inside a default tmux or screen) do not. A terminal that sends a bare arrow instead simply has no resize keys — nothing misfires, because a bare arrow is already a navigation key.
  • Split sizes last for the session only. They are model state, so they survive rescans and terminal resizes but not a restart; persisting them needs a config file, which the TUI does not have.

Development

go test ./...                         # from cmd/genspec-tui
go test work ./...                    # from the repo root: every module at once
golangci-lint run --new-from-rev master

The TUI has no CI workflow of its own: go.work lists it, so the shared monorepo workflow lints and tests it alongside the library, across the {ubuntu, macos, windows} × {stable, oldstable} matrix.

The package layout under internal/ux:

Package Contents
ux the root bubbletea Model: key dispatch, layout, scan wiring, cross-ref navigation
ux/panels the four panes — Tree, FileView, Spec, Diagnostics
ux/index SpecIndex (line ↔ pointer), RefIndex ($ref sites), SourceIndex (pointer ↔ source position)
ux/key tea.KeyMsg → a small named-binding enum
ux/theme the shared lipgloss styles
ux/gadgets clipboard support

The scanner writes nothing to stdout or stderr: diagnostics arrive through codescan's OnDiagnostic callback, and main discards the standard logger, so nothing paints over the alt-screen.

Documentation

Overview

Command genspec-tui is an interactive terminal front-end for the codescan Swagger-spec generator.

It provides a source-tree browser (left), the generated spec (right, JSON or YAML), and diagnostics (bottom).

It regenerates the whole-scope spec on any file change.

The scan is configured from two places.

  • Boolean knobs are toggled live in the options overlay (o)
  • build tags, package and tag filters, naming - are command-line flags (a checkbox list cannot express them)

The knobs settings overlay re-runs the scan on close.

Defaults at startup may be consumed from a configuration file or environment variables.

Directories

Path Synopsis
internal
config
Package config holds the CLI configuration.
Package config holds the CLI configuration.
index
Package index provides structures to index spec nodes and a source tree.
Package index provides structures to index spec nodes and a source tree.
ux
Package ux is the bubbletea front-end for genspec-tui: a single root Model composing a header line, three panels (source tree, spec, diagnostics), and a status and help line.
Package ux is the bubbletea front-end for genspec-tui: a single root Model composing a header line, three panels (source tree, spec, diagnostics), and a status and help line.
ux/confirm
Package confirm is the yes or no modal: a question that must be answered before something irreversible happens.
Package confirm is the yes or no modal: a question that must be answered before something irreversible happens.
ux/diagnostics
Package diagnostics renders the scan half of the diagnostics pane: a severity tally, then one row per finding.
Package diagnostics renders the scan half of the diagnostics pane: a severity tally, then one row per finding.
ux/gadgets
Package gadgets holds small, self-contained TUI helpers.
Package gadgets holds small, self-contained TUI helpers.
ux/help
Package help is the keymap overlay: a scrollable modal listing every binding, grouped by the context it applies in.
Package help is the keymap overlay: a scrollable modal listing every binding, grouped by the context it applies in.
ux/humanize
Package humanize renders durations, sizes and counts the way the chrome shows them to a reader.
Package humanize renders durations, sizes and counts the way the chrome shows them to a reader.
ux/key
Package key normalizes tea.KeyMsg values into a small enum of named bindings, so the model dispatches on a plain string switch rather than a key-binding library.
Package key normalizes tea.KeyMsg values into a small enum of named bindings, so the model dispatches on a plain string switch rather than a key-binding library.
ux/options
Package options is the scanner-options overlay: a scrollable modal of boolean toggles bound directly to the codescan.Options the app scans with.
Package options is the scanner-options overlay: a scrollable modal of boolean toggles bound directly to the codescan.Options the app scans with.
ux/panels
Package panels holds the three scrollable sub-panels of the genspec-tui layout: the source tree (left), the generated spec (right) and the diagnostics (bottom).
Package panels holds the three scrollable sub-panels of the genspec-tui layout: the source tree (left), the generated spec (right) and the diagnostics (bottom).
ux/reference
Package reference is the annotation-reference popup: what the swagger: directive on this line means, and what may be written under it, without leaving the file to go and look it up.
Package reference is the annotation-reference popup: what the swagger: directive on this line means, and what may be written under it, without leaving the file to go and look it up.
ux/runstats
Package runstats shows what the last scan cost: the wall clock, and the memory the process moved across the run.
Package runstats shows what the last scan cost: the wall clock, and the memory the process moved across the run.
ux/safetext
Package safetext makes the control characters in scanned text visible, so nothing a repository contains can steer the terminal the TUI is drawn on.
Package safetext makes the control characters in scanned text visible, so nothing a repository contains can steer the terminal the TUI is drawn on.
ux/scan
Package scan runs codescan and reports the outcome as a bubbletea message.
Package scan runs codescan and reports the outcome as a bubbletea message.
ux/testutils
Package testutils holds helpers shared by the tests of several ux packages.
Package testutils holds helpers shared by the tests of several ux packages.
ux/theme
Package theme holds the lipgloss styles shared by the model and its panels: a rounded-border panel box (bright when focused, dim otherwise), a panel title, and the status line.
Package theme holds the lipgloss styles shared by the model and its panels: a rounded-border panel box (bright when focused, dim otherwise), a panel title, and the status line.
ux/validation
Package validation runs the produced spec through go-openapi/validate and normalises what comes back into findings the TUI can list and navigate to.
Package validation runs the produced spec through go-openapi/validate and normalises what comes back into findings the TUI can list and navigate to.
ux/watcher
Package watcher reports changes under a source tree, so the spec can be regenerated on save.
Package watcher reports changes under a source tree, so the spec can be regenerated on save.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL