@sverka/sarif-viewer-tui 0.2.28 → 0.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.mjs CHANGED
@@ -30,7 +30,7 @@ async function main() {
30
30
  process.exit(1);
31
31
  }
32
32
  const findings = resolveFindings(options);
33
- const { renderSarifTui } = await import("./render-BR4Ucxcf.mjs");
33
+ const { renderSarifTui } = await import("./render-CMYjrBTk.mjs");
34
34
  await renderSarifTui({ findings });
35
35
  }
36
36
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/viewer.tsx","../src/render.ts","../src/filter-model.ts","../src/input.ts"],"mappings":";;;;;UAMiB;;WAEN;;WAEA;;WAEA,oBAAoB;;;WAGpB,UAAU;;;KAIT;;KAIA;;;;qBCJC;WACF,mBAAmB;EAC5B,QAAQ;EACR;EACA;EACA,MAAM;EACN;EACA;UAEQ;mBACS;UACT;EAEI,YAAA,mBAAmB;EAI/B,YAAa;EAOb;EAEA,eAAe;EAIf;EAKA;EAIA,4BAA4B;EAM5B,YAAY,eAAe,KAAK;;;wBAwFlB,YAAY,OAAO;EAAW,mBAAmB;qBAAY,IAAA;;;;;;;;;;;;;;;wBChI7D,eAAe,SAAS,kBAAkB;;;;qBCd7C,2BAA2B;;wBAUxB,aAAa,UAAU;;;wBAiBvB,iBACd,mBAAmB,WACnB,QAAQ,wBACE;;;wBAMI,eACd,mBAAmB,WACnB,yBACU;;;wBAMI,aACd,mBAAmB,WACnB,MAAM,oBACI;;;;;;;;;;;;;;wBC1CI,gBAAgB,SAAS,kBAAkB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/viewer.tsx","../src/render.ts","../src/filter-model.ts","../src/input.ts"],"mappings":";;;;;UAMiB;;WAEN;;WAEA;;WAEA,oBAAoB;;;WAGpB,UAAU;;;KAIT;;KAIA;;;;qBCHC;WACF,mBAAmB;EAC5B,QAAQ;EACR;EACA;EACA,MAAM;EACN;EACA;UAEQ;mBACS;UACT;EAEI,YAAA,mBAAmB;EAI/B,YAAa;EAOb;EAEA,eAAe;EAIf;EAKA;EAIA,4BAA4B;EAM5B,YAAY,eAAe,KAAK;;;wBAwFlB,YAAY,OAAO;EAAW,mBAAmB;qBAAY,IAAA;;;;;;;;;;;;;;;wBCjI7D,eAAe,SAAS,kBAAkB;;;;qBCd7C,2BAA2B;;wBAUxB,aAAa,UAAU;;;wBAiBvB,iBACd,mBAAmB,WACnB,QAAQ,wBACE;;;wBAMI,eACd,mBAAmB,WACnB,yBACU;;;wBAMI,aACd,mBAAmB,WACnB,MAAM,oBACI;;;;;;;;;;;;;;wBC1CI,gBAAgB,SAAS,kBAAkB"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as filterBySeverity, c as sortFindings, i as SEVERITY_FILTERS, n as SarifTuiApp, o as searchFindings, r as TuiStore, s as severityRank, t as renderSarifTui } from "./render-D5JdWdKp.mjs";
1
+ import { a as filterBySeverity, c as sortFindings, i as SEVERITY_FILTERS, n as SarifTuiApp, o as searchFindings, r as TuiStore, s as severityRank, t as renderSarifTui } from "./render-eTA48SFK.mjs";
2
2
  import { t as resolveFindings } from "./input-BZDp8iY2.mjs";
3
3
  import { DEFAULT_NORMALIZE_CONTEXT } from "@sverka/verification";
4
4
  export { DEFAULT_NORMALIZE_CONTEXT, SEVERITY_FILTERS, SarifTuiApp, TuiStore, filterBySeverity, renderSarifTui, resolveFindings, searchFindings, severityRank, sortFindings };
@@ -0,0 +1 @@
1
+ export { t as renderSarifTui } from "./render-eTA48SFK.mjs";
@@ -1,9 +1,9 @@
1
1
  import { t as resolveFindings } from "./input-BZDp8iY2.mjs";
2
+ import process from "node:process";
2
3
  import { createElement, useEffect, useRef, useSyncExternalStore } from "react";
3
4
  import { Box, Text, render, useApp, useInput, useStdout } from "ink";
4
5
  import { filterBySeverity, handleSearchInput, isQuitInput, searchFindings } from "@sverka/verification";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
- import process from "node:process";
7
7
  import { openSync } from "node:fs";
8
8
  import { ReadStream } from "node:tty";
9
9
  //#region src/filter-model.ts
@@ -182,8 +182,9 @@ function SarifTuiApp(props) {
182
182
  useInput((input, key) => {
183
183
  store.handleInput(input, key);
184
184
  }, { isActive: true });
185
- const termCols = stdout.columns || 80;
186
- const termRows = stdout.rows || 24;
185
+ const tty = stdout;
186
+ const termCols = tty.columns ?? process.stdout.columns ?? 80;
187
+ const termRows = tty.rows ?? process.stdout.rows ?? 24;
187
188
  const maxWidth = Math.max(10, termCols - 2);
188
189
  const visible = store.visibleFindings();
189
190
  const selected = Math.min(store.selected, Math.max(0, visible.length - 1));
@@ -288,4 +289,4 @@ function printFindings(findings) {
288
289
  //#endregion
289
290
  export { filterBySeverity$1 as a, sortFindings as c, SEVERITY_FILTERS as i, SarifTuiApp as n, searchFindings$1 as o, TuiStore as r, severityRank as s, renderSarifTui as t };
290
291
 
291
- //# sourceMappingURL=render-D5JdWdKp.mjs.map
292
+ //# sourceMappingURL=render-eTA48SFK.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-eTA48SFK.mjs","names":["filterBySeverity","sharedFilterBySeverity","searchFindings","sharedSearchFindings","filterBySeverity","searchFindings"],"sources":["../src/filter-model.ts","../src/viewer.tsx","../src/render.ts"],"sourcesContent":["// @sverka/sarif-viewer-tui — pure filter/search/sort helpers. Spec 46.\n\nimport {\n filterBySeverity as sharedFilterBySeverity,\n searchFindings as sharedSearchFindings,\n} from \"@sverka/verification\";\nimport type { Finding, Severity } from \"@sverka/verification\";\nimport type { SortMode, ViewerFilter } from \"./types.js\";\n\n/** Ordered list of severity filters cycled by the `f` key. */\nexport const SEVERITY_FILTERS: readonly ViewerFilter[] = [\n \"all\",\n \"critical\",\n \"high\",\n \"medium\",\n \"low\",\n \"info\",\n];\n\n/** Severity rank for sorting: critical > high > medium > low > info. */\nexport function severityRank(severity: Severity): number {\n switch (severity) {\n case \"critical\":\n return 4;\n case \"high\":\n return 3;\n case \"medium\":\n return 2;\n case \"low\":\n return 1;\n case \"info\":\n return 0;\n }\n}\n\n/** Filter findings by severity level. `all` returns everything. Pure.\n * Delegates to the shared `@sverka/verification` implementation. */\nexport function filterBySeverity(\n findings: readonly Finding[],\n filter: ViewerFilter,\n): readonly Finding[] {\n return sharedFilterBySeverity(findings, filter);\n}\n\n/** Substring match (case-insensitive) across message, checkId, file, rule. Pure.\n * Delegates to the shared `@sverka/verification` implementation. */\nexport function searchFindings(\n findings: readonly Finding[],\n query: string,\n): readonly Finding[] {\n return sharedSearchFindings(findings, query);\n}\n\n/** Sort findings by mode. Sort is stable (preserves original order for equal\n * keys). `none` returns the input unchanged. Pure. */\nexport function sortFindings(\n findings: readonly Finding[],\n mode: SortMode,\n): readonly Finding[] {\n if (mode === \"none\") return findings;\n const indexed = findings.map((f, i) => ({ f, i }));\n switch (mode) {\n case \"severity\":\n indexed.sort(\n (a, b) =>\n severityRank(b.f.severity) - severityRank(a.f.severity) || a.i - b.i,\n );\n break;\n case \"file\":\n indexed.sort(\n (a, b) =>\n a.f.file.localeCompare(b.f.file, \"en\") ||\n a.f.startLine - b.f.startLine ||\n a.i - b.i,\n );\n break;\n case \"rule\":\n indexed.sort(\n (a, b) => a.f.rule.localeCompare(b.f.rule, \"en\") || a.i - b.i,\n );\n break;\n }\n return indexed.map(({ f }) => f);\n}\n","// @sverka/sarif-viewer-tui — Ink TUI component. Spec 46.\n\nimport process from \"node:process\";\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\nimport { Box, Text, useApp, useInput, useStdout } from \"ink\";\nimport type { Key } from \"ink\";\nimport {\n handleSearchInput,\n isQuitInput,\n type Finding,\n} from \"@sverka/verification\";\nimport type { SortMode, ViewerFilter } from \"./types.js\";\nimport {\n SEVERITY_FILTERS,\n filterBySeverity,\n searchFindings,\n sortFindings,\n} from \"./filter-model.js\";\n\n/** Mutable store backing the SarifTuiApp. */\nexport class TuiStore {\n readonly findings: readonly Finding[];\n filter: ViewerFilter = \"all\";\n search = \"\";\n searching = false;\n sort: SortMode = \"none\";\n selected = 0;\n details = false;\n\n private version = 0;\n private readonly listeners = new Set<() => void>();\n private onQuit: (() => void) | null = null;\n\n constructor(findings: readonly Finding[]) {\n this.findings = findings;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n getVersion = (): number => this.version;\n\n setQuitHandler(fn: () => void): void {\n this.onQuit = fn;\n }\n\n notify(): void {\n this.version += 1;\n for (const l of this.listeners) l();\n }\n\n quit(): void {\n this.onQuit?.();\n }\n\n visibleFindings(): readonly Finding[] {\n const filtered = filterBySeverity(this.findings, this.filter);\n const searched = searchFindings(filtered, this.search);\n return sortFindings(searched, this.sort);\n }\n\n handleInput(input: string, key: Key): void {\n if (handleSearchInput(input, key, this)) {\n this.notify();\n return;\n }\n\n if (isQuitInput(input, key)) {\n this.quit();\n return;\n }\n if (input === \"f\") {\n const i = SEVERITY_FILTERS.indexOf(this.filter);\n this.filter =\n SEVERITY_FILTERS[(i + 1) % SEVERITY_FILTERS.length] ?? \"all\";\n this.selected = Math.min(\n this.selected,\n Math.max(0, this.visibleFindings().length - 1),\n );\n } else if (input === \"s\") {\n this.sort = nextSortMode(this.sort);\n this.selected = Math.min(\n this.selected,\n Math.max(0, this.visibleFindings().length - 1),\n );\n } else if (input === \"d\") {\n this.details = !this.details;\n } else if (input === \"j\" || key.downArrow) {\n const max = Math.max(0, this.visibleFindings().length - 1);\n this.selected = Math.min(max, this.selected + 1);\n } else if (input === \"k\" || key.upArrow) {\n this.selected = Math.max(0, this.selected - 1);\n } else {\n return;\n }\n this.notify();\n }\n}\n\nconst SORT_CYCLE: readonly SortMode[] = [\"none\", \"severity\", \"file\", \"rule\"];\n\nfunction nextSortMode(mode: SortMode): SortMode {\n const i = SORT_CYCLE.indexOf(mode);\n return SORT_CYCLE[(i + 1) % SORT_CYCLE.length] ?? \"none\";\n}\n\n/** Map a severity to a color prop object. */\nfunction severityColor(severity: string): { color?: string } {\n if (severity === \"critical\" || severity === \"high\") return { color: \"red\" };\n if (severity === \"medium\") return { color: \"yellow\" };\n return {};\n}\n\n/** Render the search indicator based on store state. */\nfunction renderSearchIndicator(store: TuiStore): string {\n if (store.searching) return ` /${store.search}▏`;\n if (store.search) return ` /${store.search}`;\n return \"\";\n}\n\n/** Detail lines for the selected finding. */\nfunction detailsForFinding(finding: Finding): string[] {\n return [\n ` rule: ${finding.rule}`,\n ` file: ${finding.file}`,\n ` lines: ${finding.startLine}–${finding.endLine}`,\n ` message: ${finding.message}`,\n ...(finding.helpUrl ? [` help: ${finding.helpUrl}`] : []),\n ];\n}\n\n/** Compute the viewport window for the visible findings list. */\nfunction computeViewport(\n visible: readonly Finding[],\n selected: number,\n termRows: number,\n detailsCount: number,\n): { start: number; end: number; items: readonly Finding[] } {\n const reservedRows = 4 + detailsCount;\n const maxVisibleRows = Math.max(1, termRows - reservedRows);\n const start = Math.min(\n Math.max(0, selected - Math.floor(maxVisibleRows / 2)),\n Math.max(0, visible.length - maxVisibleRows),\n );\n const end = Math.min(visible.length, start + maxVisibleRows);\n return { start, end, items: visible.slice(start, end) };\n}\n\n/** The standalone SARIF viewer Ink component. */\nexport function SarifTuiApp(props: Readonly<{ findings: readonly Finding[] }>) {\n const storeRef = useRef<TuiStore | null>(null);\n if (storeRef.current === null) {\n storeRef.current = new TuiStore(props.findings);\n }\n const store = storeRef.current;\n useSyncExternalStore(store.subscribe, store.getVersion);\n const { exit } = useApp();\n const { stdout } = useStdout();\n\n useEffect(() => {\n store.setQuitHandler(exit);\n }, [store, exit]);\n\n // Reconcile store when findings prop changes (e.g. hot-reload or re-render\n // with different data). Updates the findings array and clamps selection.\n useEffect(() => {\n if (store.findings !== props.findings) {\n (store as { findings: readonly Finding[] }).findings = props.findings;\n store.selected = Math.min(\n store.selected,\n Math.max(0, store.visibleFindings().length - 1),\n );\n store.notify();\n }\n }, [props.findings, store]);\n\n useInput(\n (input, key) => {\n store.handleInput(input, key);\n },\n { isActive: true },\n );\n\n // ink 8 types the rendered stream as NodeJS.WritableStream — the\n // terminal dimensions are not part of that type. Read them from the\n // stream when it exposes them (a TTY), else from the process stream.\n const tty = stdout as Partial<Pick<NodeJS.WriteStream, \"rows\" | \"columns\">>;\n const termCols = tty.columns ?? process.stdout.columns ?? 80;\n const termRows = tty.rows ?? process.stdout.rows ?? 24;\n const maxWidth = Math.max(10, termCols - 2);\n const visible = store.visibleFindings();\n const selected = Math.min(store.selected, Math.max(0, visible.length - 1));\n const selectedFinding = visible[selected];\n\n const detailsLines =\n store.details && selectedFinding ? detailsForFinding(selectedFinding) : [];\n\n const { start: viewportStart, items: viewportItems } = computeViewport(\n visible,\n selected,\n termRows,\n detailsLines.length,\n );\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>SARIF viewer</Text>\n\n <Text>\n {\"Findings \"}\n {SEVERITY_FILTERS.map((f) => (\n <Text key={f} inverse={store.filter === f} bold={store.filter === f}>\n [{f}]\n </Text>\n ))}\n {` (${visible.length})`}\n {renderSearchIndicator(store)}\n </Text>\n\n {visible.length === 0 ? (\n <Text>No findings</Text>\n ) : (\n viewportItems.map((f, i) => {\n const actualIndex = viewportStart + i;\n return (\n <Text\n key={f.id}\n {...(actualIndex === selected ? {} : severityColor(f.severity))}\n inverse={actualIndex === selected}\n wrap=\"truncate\"\n >\n {` ${f.severity.padEnd(8)} ${f.checkId} ${f.file}:${f.startLine} ${f.message}`.slice(\n 0,\n maxWidth,\n )}\n </Text>\n );\n })\n )}\n\n {detailsLines.map((l) => (\n <Text key={`d-${l}`} color=\"gray\" wrap=\"truncate\">\n {l.slice(0, maxWidth)}\n </Text>\n ))}\n\n <Text>\n <Text color=\"gray\">\n q quit · j/k move · / search · f filter · s sort · d details\n </Text>\n </Text>\n </Box>\n );\n}\n","// @sverka/sarif-viewer-tui — render entry point. Spec 46.\n\nimport process from \"node:process\";\nimport { openSync } from \"node:fs\";\nimport { ReadStream } from \"node:tty\";\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { Finding } from \"@sverka/verification\";\nimport { resolveFindings } from \"./input.js\";\nimport { SarifTuiApp } from \"./viewer.js\";\nimport type { SarifTuiOptions } from \"./types.js\";\n\n/**\n * Resolve `options` into findings and render the SARIF TUI. Blocks until the\n * user quits (q / Ctrl-C). Resolves on exit.\n *\n * When stdout is not a TTY (piped output, CI, agents) or no interactive\n * input stream is available, Ink cannot run — the findings are printed as\n * plain text instead so the command still produces useful output and exits.\n *\n * Wraps resolveFindings in a try-catch so invalid input produces a clean\n * error message instead of an unhandled synchronous throw before the\n * promise exists.\n */\nexport function renderSarifTui(options: SarifTuiOptions): Promise<void> {\n let findings;\n try {\n findings = resolveFindings(options);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n process.stderr.write(`sarif-viewer-tui: ${msg}\\n`);\n return Promise.resolve();\n }\n\n if (!process.stdout.isTTY || process.env.CI) {\n // Non-interactive stdout or CI — even with a pseudo-TTY there is no\n // user to quit Ink, so a TUI would block forever. Print the findings.\n printFindings(findings);\n return Promise.resolve();\n }\n\n // Ink needs a raw-mode-capable stdin for keyboard input. When SARIF was\n // piped via stdin (e.g. `cat f.sarif | sverka view`), stdin is a pipe —\n // fall back to the controlling terminal so the TUI still works.\n const stdin = interactiveStdin();\n if (stdin === undefined) {\n printFindings(findings);\n return Promise.resolve();\n }\n\n const instance = render(createElement(SarifTuiApp, { findings }), {\n stdout: process.stdout,\n stdin,\n exitOnCtrlC: true,\n });\n return instance.waitUntilExit().then(() => undefined);\n}\n\n/** Return a raw-mode-capable input stream for Ink, or undefined. */\nfunction interactiveStdin(): ReadStream | undefined {\n if (process.stdin.isTTY) return process.stdin;\n // /dev/tty is POSIX-only; on Windows there is no equivalent Node API to\n // reopen console input, so piped stdin degrades to the text fallback.\n if (process.platform === \"win32\") return undefined;\n try {\n return new ReadStream(openSync(\"/dev/tty\", \"r\"));\n } catch {\n // No controlling terminal (CI, detached process) — non-interactive.\n return undefined;\n }\n}\n\n/** Plain-text fallback listing used when the TUI cannot run. */\nfunction printFindings(findings: readonly Finding[]): void {\n process.stdout.write(`SARIF findings (${findings.length}):\\n`);\n for (const f of findings) {\n process.stdout.write(\n ` ${f.severity.padEnd(8)} ${f.checkId} ${f.file}:${f.startLine} ${f.message}\\n`,\n );\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sverka/sarif-viewer-tui",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "description": "Sverka SARIF viewer TUI — standalone Ink-based terminal viewer for SARIF files",
5
5
  "license": "MIT",
6
6
  "sideEffects": false,
@@ -37,18 +37,18 @@
37
37
  "typecheck": "tsc --noEmit"
38
38
  },
39
39
  "peerDependencies": {
40
- "ink": ">=5 <8",
40
+ "ink": ">=5 <9",
41
41
  "react": ">=19.2.0 <20"
42
42
  },
43
43
  "dependencies": {
44
- "@sverka/verification": "^0.2.28"
44
+ "@sverka/verification": "^0.2.30"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/react": "^19",
48
- "ink": "7.1.1",
49
- "react": "19.3.0",
50
- "tsdown": "^0.23.0",
48
+ "ink": "^8.0.0",
49
+ "react": "19.3.1",
50
+ "tsdown": "^0.23.1",
51
51
  "typescript": "^5.8.0",
52
- "vitest": "^5.0.0"
52
+ "vitest": "^5.0.5"
53
53
  }
54
54
  }
@@ -1,2 +0,0 @@
1
- import { t as renderSarifTui } from "./render-D5JdWdKp.mjs";
2
- export { renderSarifTui };
@@ -1 +0,0 @@
1
- {"version":3,"file":"render-D5JdWdKp.mjs","names":["filterBySeverity","sharedFilterBySeverity","searchFindings","sharedSearchFindings","filterBySeverity","searchFindings"],"sources":["../src/filter-model.ts","../src/viewer.tsx","../src/render.ts"],"sourcesContent":["// @sverka/sarif-viewer-tui — pure filter/search/sort helpers. Spec 46.\n\nimport {\n filterBySeverity as sharedFilterBySeverity,\n searchFindings as sharedSearchFindings,\n} from \"@sverka/verification\";\nimport type { Finding, Severity } from \"@sverka/verification\";\nimport type { SortMode, ViewerFilter } from \"./types.js\";\n\n/** Ordered list of severity filters cycled by the `f` key. */\nexport const SEVERITY_FILTERS: readonly ViewerFilter[] = [\n \"all\",\n \"critical\",\n \"high\",\n \"medium\",\n \"low\",\n \"info\",\n];\n\n/** Severity rank for sorting: critical > high > medium > low > info. */\nexport function severityRank(severity: Severity): number {\n switch (severity) {\n case \"critical\":\n return 4;\n case \"high\":\n return 3;\n case \"medium\":\n return 2;\n case \"low\":\n return 1;\n case \"info\":\n return 0;\n }\n}\n\n/** Filter findings by severity level. `all` returns everything. Pure.\n * Delegates to the shared `@sverka/verification` implementation. */\nexport function filterBySeverity(\n findings: readonly Finding[],\n filter: ViewerFilter,\n): readonly Finding[] {\n return sharedFilterBySeverity(findings, filter);\n}\n\n/** Substring match (case-insensitive) across message, checkId, file, rule. Pure.\n * Delegates to the shared `@sverka/verification` implementation. */\nexport function searchFindings(\n findings: readonly Finding[],\n query: string,\n): readonly Finding[] {\n return sharedSearchFindings(findings, query);\n}\n\n/** Sort findings by mode. Sort is stable (preserves original order for equal\n * keys). `none` returns the input unchanged. Pure. */\nexport function sortFindings(\n findings: readonly Finding[],\n mode: SortMode,\n): readonly Finding[] {\n if (mode === \"none\") return findings;\n const indexed = findings.map((f, i) => ({ f, i }));\n switch (mode) {\n case \"severity\":\n indexed.sort(\n (a, b) =>\n severityRank(b.f.severity) - severityRank(a.f.severity) || a.i - b.i,\n );\n break;\n case \"file\":\n indexed.sort(\n (a, b) =>\n a.f.file.localeCompare(b.f.file, \"en\") ||\n a.f.startLine - b.f.startLine ||\n a.i - b.i,\n );\n break;\n case \"rule\":\n indexed.sort(\n (a, b) => a.f.rule.localeCompare(b.f.rule, \"en\") || a.i - b.i,\n );\n break;\n }\n return indexed.map(({ f }) => f);\n}\n","// @sverka/sarif-viewer-tui — Ink TUI component. Spec 46.\n\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\nimport { Box, Text, useApp, useInput, useStdout } from \"ink\";\nimport type { Key } from \"ink\";\nimport {\n handleSearchInput,\n isQuitInput,\n type Finding,\n} from \"@sverka/verification\";\nimport type { SortMode, ViewerFilter } from \"./types.js\";\nimport {\n SEVERITY_FILTERS,\n filterBySeverity,\n searchFindings,\n sortFindings,\n} from \"./filter-model.js\";\n\n/** Mutable store backing the SarifTuiApp. */\nexport class TuiStore {\n readonly findings: readonly Finding[];\n filter: ViewerFilter = \"all\";\n search = \"\";\n searching = false;\n sort: SortMode = \"none\";\n selected = 0;\n details = false;\n\n private version = 0;\n private readonly listeners = new Set<() => void>();\n private onQuit: (() => void) | null = null;\n\n constructor(findings: readonly Finding[]) {\n this.findings = findings;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n getVersion = (): number => this.version;\n\n setQuitHandler(fn: () => void): void {\n this.onQuit = fn;\n }\n\n notify(): void {\n this.version += 1;\n for (const l of this.listeners) l();\n }\n\n quit(): void {\n this.onQuit?.();\n }\n\n visibleFindings(): readonly Finding[] {\n const filtered = filterBySeverity(this.findings, this.filter);\n const searched = searchFindings(filtered, this.search);\n return sortFindings(searched, this.sort);\n }\n\n handleInput(input: string, key: Key): void {\n if (handleSearchInput(input, key, this)) {\n this.notify();\n return;\n }\n\n if (isQuitInput(input, key)) {\n this.quit();\n return;\n }\n if (input === \"f\") {\n const i = SEVERITY_FILTERS.indexOf(this.filter);\n this.filter =\n SEVERITY_FILTERS[(i + 1) % SEVERITY_FILTERS.length] ?? \"all\";\n this.selected = Math.min(\n this.selected,\n Math.max(0, this.visibleFindings().length - 1),\n );\n } else if (input === \"s\") {\n this.sort = nextSortMode(this.sort);\n this.selected = Math.min(\n this.selected,\n Math.max(0, this.visibleFindings().length - 1),\n );\n } else if (input === \"d\") {\n this.details = !this.details;\n } else if (input === \"j\" || key.downArrow) {\n const max = Math.max(0, this.visibleFindings().length - 1);\n this.selected = Math.min(max, this.selected + 1);\n } else if (input === \"k\" || key.upArrow) {\n this.selected = Math.max(0, this.selected - 1);\n } else {\n return;\n }\n this.notify();\n }\n}\n\nconst SORT_CYCLE: readonly SortMode[] = [\"none\", \"severity\", \"file\", \"rule\"];\n\nfunction nextSortMode(mode: SortMode): SortMode {\n const i = SORT_CYCLE.indexOf(mode);\n return SORT_CYCLE[(i + 1) % SORT_CYCLE.length] ?? \"none\";\n}\n\n/** Map a severity to a color prop object. */\nfunction severityColor(severity: string): { color?: string } {\n if (severity === \"critical\" || severity === \"high\") return { color: \"red\" };\n if (severity === \"medium\") return { color: \"yellow\" };\n return {};\n}\n\n/** Render the search indicator based on store state. */\nfunction renderSearchIndicator(store: TuiStore): string {\n if (store.searching) return ` /${store.search}▏`;\n if (store.search) return ` /${store.search}`;\n return \"\";\n}\n\n/** Detail lines for the selected finding. */\nfunction detailsForFinding(finding: Finding): string[] {\n return [\n ` rule: ${finding.rule}`,\n ` file: ${finding.file}`,\n ` lines: ${finding.startLine}–${finding.endLine}`,\n ` message: ${finding.message}`,\n ...(finding.helpUrl ? [` help: ${finding.helpUrl}`] : []),\n ];\n}\n\n/** Compute the viewport window for the visible findings list. */\nfunction computeViewport(\n visible: readonly Finding[],\n selected: number,\n termRows: number,\n detailsCount: number,\n): { start: number; end: number; items: readonly Finding[] } {\n const reservedRows = 4 + detailsCount;\n const maxVisibleRows = Math.max(1, termRows - reservedRows);\n const start = Math.min(\n Math.max(0, selected - Math.floor(maxVisibleRows / 2)),\n Math.max(0, visible.length - maxVisibleRows),\n );\n const end = Math.min(visible.length, start + maxVisibleRows);\n return { start, end, items: visible.slice(start, end) };\n}\n\n/** The standalone SARIF viewer Ink component. */\nexport function SarifTuiApp(props: Readonly<{ findings: readonly Finding[] }>) {\n const storeRef = useRef<TuiStore | null>(null);\n if (storeRef.current === null) {\n storeRef.current = new TuiStore(props.findings);\n }\n const store = storeRef.current;\n useSyncExternalStore(store.subscribe, store.getVersion);\n const { exit } = useApp();\n const { stdout } = useStdout();\n\n useEffect(() => {\n store.setQuitHandler(exit);\n }, [store, exit]);\n\n // Reconcile store when findings prop changes (e.g. hot-reload or re-render\n // with different data). Updates the findings array and clamps selection.\n useEffect(() => {\n if (store.findings !== props.findings) {\n (store as { findings: readonly Finding[] }).findings = props.findings;\n store.selected = Math.min(\n store.selected,\n Math.max(0, store.visibleFindings().length - 1),\n );\n store.notify();\n }\n }, [props.findings, store]);\n\n useInput(\n (input, key) => {\n store.handleInput(input, key);\n },\n { isActive: true },\n );\n\n const termCols = stdout.columns || 80;\n const termRows = stdout.rows || 24;\n const maxWidth = Math.max(10, termCols - 2);\n const visible = store.visibleFindings();\n const selected = Math.min(store.selected, Math.max(0, visible.length - 1));\n const selectedFinding = visible[selected];\n\n const detailsLines =\n store.details && selectedFinding ? detailsForFinding(selectedFinding) : [];\n\n const { start: viewportStart, items: viewportItems } = computeViewport(\n visible,\n selected,\n termRows,\n detailsLines.length,\n );\n\n return (\n <Box flexDirection=\"column\">\n <Text bold>SARIF viewer</Text>\n\n <Text>\n {\"Findings \"}\n {SEVERITY_FILTERS.map((f) => (\n <Text key={f} inverse={store.filter === f} bold={store.filter === f}>\n [{f}]\n </Text>\n ))}\n {` (${visible.length})`}\n {renderSearchIndicator(store)}\n </Text>\n\n {visible.length === 0 ? (\n <Text>No findings</Text>\n ) : (\n viewportItems.map((f, i) => {\n const actualIndex = viewportStart + i;\n return (\n <Text\n key={f.id}\n {...(actualIndex === selected ? {} : severityColor(f.severity))}\n inverse={actualIndex === selected}\n wrap=\"truncate\"\n >\n {` ${f.severity.padEnd(8)} ${f.checkId} ${f.file}:${f.startLine} ${f.message}`.slice(\n 0,\n maxWidth,\n )}\n </Text>\n );\n })\n )}\n\n {detailsLines.map((l) => (\n <Text key={`d-${l}`} color=\"gray\" wrap=\"truncate\">\n {l.slice(0, maxWidth)}\n </Text>\n ))}\n\n <Text>\n <Text color=\"gray\">\n q quit · j/k move · / search · f filter · s sort · d details\n </Text>\n </Text>\n </Box>\n );\n}\n","// @sverka/sarif-viewer-tui — render entry point. Spec 46.\n\nimport process from \"node:process\";\nimport { openSync } from \"node:fs\";\nimport { ReadStream } from \"node:tty\";\nimport { render } from \"ink\";\nimport { createElement } from \"react\";\nimport type { Finding } from \"@sverka/verification\";\nimport { resolveFindings } from \"./input.js\";\nimport { SarifTuiApp } from \"./viewer.js\";\nimport type { SarifTuiOptions } from \"./types.js\";\n\n/**\n * Resolve `options` into findings and render the SARIF TUI. Blocks until the\n * user quits (q / Ctrl-C). Resolves on exit.\n *\n * When stdout is not a TTY (piped output, CI, agents) or no interactive\n * input stream is available, Ink cannot run — the findings are printed as\n * plain text instead so the command still produces useful output and exits.\n *\n * Wraps resolveFindings in a try-catch so invalid input produces a clean\n * error message instead of an unhandled synchronous throw before the\n * promise exists.\n */\nexport function renderSarifTui(options: SarifTuiOptions): Promise<void> {\n let findings;\n try {\n findings = resolveFindings(options);\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n process.stderr.write(`sarif-viewer-tui: ${msg}\\n`);\n return Promise.resolve();\n }\n\n if (!process.stdout.isTTY || process.env.CI) {\n // Non-interactive stdout or CI — even with a pseudo-TTY there is no\n // user to quit Ink, so a TUI would block forever. Print the findings.\n printFindings(findings);\n return Promise.resolve();\n }\n\n // Ink needs a raw-mode-capable stdin for keyboard input. When SARIF was\n // piped via stdin (e.g. `cat f.sarif | sverka view`), stdin is a pipe —\n // fall back to the controlling terminal so the TUI still works.\n const stdin = interactiveStdin();\n if (stdin === undefined) {\n printFindings(findings);\n return Promise.resolve();\n }\n\n const instance = render(createElement(SarifTuiApp, { findings }), {\n stdout: process.stdout,\n stdin,\n exitOnCtrlC: true,\n });\n return instance.waitUntilExit().then(() => undefined);\n}\n\n/** Return a raw-mode-capable input stream for Ink, or undefined. */\nfunction interactiveStdin(): ReadStream | undefined {\n if (process.stdin.isTTY) return process.stdin;\n // /dev/tty is POSIX-only; on Windows there is no equivalent Node API to\n // reopen console input, so piped stdin degrades to the text fallback.\n if (process.platform === \"win32\") return undefined;\n try {\n return new ReadStream(openSync(\"/dev/tty\", \"r\"));\n } catch {\n // No controlling terminal (CI, detached process) — non-interactive.\n return undefined;\n }\n}\n\n/** Plain-text fallback listing used when the TUI cannot run. */\nfunction printFindings(findings: readonly Finding[]): void {\n process.stdout.write(`SARIF findings (${findings.length}):\\n`);\n for (const f of findings) {\n process.stdout.write(\n ` ${f.severity.padEnd(8)} ${f.checkId} ${f.file}:${f.startLine} ${f.message}\\n`,\n );\n }\n}\n"],"mappings":"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"}