react-doctor 0.9.2-dev.201106e → 0.9.2-dev.b31fd85

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/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1d99d852-8548-5af9-a8d9-d49d41e704f6")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3ced4c14-18cb-5b0c-811c-3448b5c2f139")}catch(e){}}();
3
3
  import { createRequire } from "node:module";
4
4
  import * as NodeChildProcess from "node:child_process";
5
5
  import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
@@ -78326,6 +78326,7 @@ const BASELINE_FILES_TEMP_DIR_PREFIX = "react-doctor-baseline-";
78326
78326
  const SCAN_RESULT_CACHE_FILENAME = "scan-cache.json";
78327
78327
  const RUN_GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
78328
78328
  const GH_DEFAULT_BRANCH_PROBE_TIMEOUT_MS = 5e3;
78329
+ const TUI_REPORT_COMPACT_MAX_ROWS = 29;
78329
78330
  const TUI_REPORT_DETAIL_WIDTH_FRACTION = .6;
78330
78331
  const CI_TRUST_COMPANIES = "PayPal, Rippling, and Alibaba";
78331
78332
  const WELCOME_EXPLANATION_HOLD_MS = 1e3;
@@ -78343,6 +78344,7 @@ const METRIC = {
78343
78344
  projectConfigSelected: "project.config_selected",
78344
78345
  scanCompleted: "scan.completed",
78345
78346
  scanDuration: "scan.duration",
78347
+ scanFeedbackDelay: "scan.feedback_delay",
78346
78348
  scanPhaseDuration: "scan.phase_duration",
78347
78349
  scanFiles: "scan.files",
78348
78350
  scanScore: "scan.score",
@@ -78374,6 +78376,10 @@ const METRIC = {
78374
78376
  lspScanCompleted: "lsp.scan.completed",
78375
78377
  lspScanDuration: "lsp.scan.duration",
78376
78378
  lspScanDiagnostics: "lsp.scan.diagnostics",
78379
+ tuiActionMenuOpened: "tui.action_menu_opened",
78380
+ tuiCompactReportShown: "tui.compact_report_shown",
78381
+ tuiFindingNavigated: "tui.finding_navigated",
78382
+ tuiStackedReportCapped: "tui.stacked_report_capped",
78377
78383
  aiTrainingWarningShown: "ai.training.warning_shown",
78378
78384
  jsonOutUsed: "json.out_used"
78379
78385
  };
@@ -78418,7 +78424,7 @@ const makeNoopConsole = () => ({
78418
78424
  });
78419
78425
  //#endregion
78420
78426
  //#region src/cli/utils/version.ts
78421
- const VERSION = "0.9.2-dev.201106e";
78427
+ const VERSION = "0.9.2-dev.b31fd85";
78422
78428
  //#endregion
78423
78429
  //#region src/cli/utils/json-mode.ts
78424
78430
  let context = null;
@@ -78776,13 +78782,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
78776
78782
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
78777
78783
  * standard `SENTRY_RELEASE` override.
78778
78784
  */
78779
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.2-dev.201106e`;
78785
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.2-dev.b31fd85`;
78780
78786
  /**
78781
78787
  * Deployment environment shown in Sentry's environment filter. Defaults to
78782
78788
  * `production` for tagged releases and `development` for dev/unbuilt versions,
78783
78789
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
78784
78790
  */
78785
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.2-dev.201106e") ? "development" : "production");
78791
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.2-dev.b31fd85") ? "development" : "production");
78786
78792
  /**
78787
78793
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
78788
78794
  * (set to `0` to disable tracing) and falls back to
@@ -96100,6 +96106,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
96100
96106
  const isQuiet = isScoreOnly || isJsonMode;
96101
96107
  const requestedDirectory = Path.resolve(directory);
96102
96108
  const startTime = performance$1.now();
96109
+ let scanStartupSpinner = null;
96103
96110
  if (isJsonMode) {
96104
96111
  enableJsonMode({
96105
96112
  compact: Boolean(flags.jsonCompact),
@@ -96233,7 +96240,20 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
96233
96240
  return;
96234
96241
  }
96235
96242
  const projectDirectories = await selectProjects(resolvedDirectory, flags.project, skipPrompts, userConfig?.projects);
96236
- const changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
96243
+ const projectSelectionCompletedTime = performance$1.now();
96244
+ let changedFilesDiffInfo = flags.changedFilesFrom ? buildChangedFilesDiffInfo(readChangedFilesFrom(Path.resolve(flags.changedFilesFrom))) : null;
96245
+ if (changedFilesDiffInfo !== null && scanTarget.didRedirectViaRootDir) {
96246
+ const relativeProjectDirectory = resolveProjectRelativeDirectory(requestedDirectory, resolvedDirectory);
96247
+ if (relativeProjectDirectory) {
96248
+ const projectPrefix = `${relativeProjectDirectory}/`;
96249
+ changedFilesDiffInfo = {
96250
+ ...changedFilesDiffInfo,
96251
+ changedFiles: changedFilesDiffInfo.changedFiles.flatMap((filePath) => {
96252
+ return filePath.startsWith(projectPrefix) ? [filePath.slice(projectPrefix.length)] : [];
96253
+ })
96254
+ };
96255
+ }
96256
+ }
96237
96257
  const requestedScope = resolveScope(flags, userConfig);
96238
96258
  const includeUntracked = flags.includeUntracked ?? false;
96239
96259
  const scopeRequest = requestedScope.scope === void 0 && changedFilesDiffInfo !== null ? {
@@ -96241,15 +96261,20 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
96241
96261
  scope: "changed"
96242
96262
  } : requestedScope;
96243
96263
  validateIncludeUntrackedScope(includeUntracked, scopeRequest.scope);
96264
+ scanStartupSpinner = !isQuiet ? spinner("Scanning...").start() : null;
96265
+ if (scanStartupSpinner !== null) recordDistribution(METRIC.scanFeedbackDelay, performance$1.now() - projectSelectionCompletedTime, { unit: "millisecond" });
96244
96266
  const wantsDiffMode = scopeRequest.scope !== void 0 && scopeRequest.scope !== "full";
96245
96267
  const shouldDetectDiff = changedFilesDiffInfo === null && (wantsDiffMode || scopeRequest.scope === void 0 && !skipPrompts && !isQuiet);
96246
96268
  const diffInfo = changedFilesDiffInfo ?? (shouldDetectDiff ? await getDiffInfo(resolvedDirectory, scopeRequest.base, includeUntracked) : null);
96269
+ scanStartupSpinner?.stop();
96270
+ scanStartupSpinner = null;
96247
96271
  const scope = await finalizeScope({
96248
96272
  requested: scopeRequest,
96249
96273
  diffInfo,
96250
96274
  skipPrompts,
96251
96275
  isQuiet
96252
96276
  });
96277
+ scanStartupSpinner = !isQuiet ? spinner("Scanning...").start() : null;
96253
96278
  const isDiffMode = scope !== "full";
96254
96279
  const comparisonBaseRef = isDiffMode && diffInfo && !diffInfo.isCurrentChanges ? diffInfo.baseSha ? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseSha) : diffInfo.diffBaseRef ?? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseBranch) : null;
96255
96280
  const baselineRef = scope === "changed" ? comparisonBaseRef : null;
@@ -96261,6 +96286,8 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
96261
96286
  files: [...diffInfo.changedFiles],
96262
96287
  includeUntracked
96263
96288
  }) : null;
96289
+ scanStartupSpinner?.stop();
96290
+ scanStartupSpinner = null;
96264
96291
  if (scope === "lines" && changedLineRanges === null && !isQuiet) {
96265
96292
  cliLogger.warn("Could not determine changed lines (no base ref or git diff failed); reporting all issues in changed files.");
96266
96293
  cliLogger.break();
@@ -96404,6 +96431,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
96404
96431
  }
96405
96432
  }
96406
96433
  } catch (error) {
96434
+ scanStartupSpinner?.stop();
96407
96435
  const isUserError = isExpectedUserError(error);
96408
96436
  const sentryEventId = isUserError ? void 0 : await reportErrorToSentry(error);
96409
96437
  if (isDebugFlagEnabled()) await flushSentry();
@@ -97338,6 +97366,7 @@ const COMMAND_FLAG_SPECS = new Map([
97338
97366
  "--no-color",
97339
97367
  "--no-dead-code",
97340
97368
  "--no-score",
97369
+ "--no-supply-chain",
97341
97370
  "--yes"
97342
97371
  ]),
97343
97372
  longOptionsWithRequiredValues: new Set(["--blocking", "--project"]),
@@ -97523,15 +97552,17 @@ rules.command("category <category> <severity>").description("Set the severity fo
97523
97552
  rules.command("ignore-tag <tag>").description("Skip a whole rule family by tag before linting (e.g. design)").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesIgnoreTagAction(tag, command.optsWithGlobals()));
97524
97553
  rules.command("unignore-tag <tag>").description("Stop ignoring a tag previously skipped via ignore-tag").option("-c, --cwd <cwd>", "working directory", process.cwd()).action((tag, _options, command) => rulesUnignoreTagAction(tag, command.optsWithGlobals()));
97525
97554
  program.command("experimental-lsp", { hidden: false }).description("[experimental] run the React Doctor language server over stdio (for editors)").allowUnknownOption().action(() => {});
97526
- program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-score", "skip the score API, the share URL, and crash reporting").option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action(async (directory = ".", _localOptions, command) => {
97555
+ program.command("experimental-tui [directory]", { hidden: true }).description("[experimental] interactive, scrollable scan report").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").option("--no-dead-code", "skip dead-code analysis").option("--no-supply-chain", "skip the dependency supply-chain scan").option("--no-score", "skip the score API, the share URL, and crash reporting").option("-p, --project <names>", "scan specific workspace projects (comma-separated, or *)").option("-y, --yes", "skip the project prompt and scan every discovered project").action(async (directory = ".", _localOptions, command) => {
97527
97556
  const options = command.optsWithGlobals();
97528
97557
  const deadCode = options.deadCode === false ? false : void 0;
97529
97558
  const noScore = options.score === false ? true : void 0;
97559
+ const supplyChain = options.supplyChain === false ? false : void 0;
97530
97560
  const nodeMajorVersion = Number(process.versions.node.split(".")[0]);
97531
97561
  if (process.stdout.isTTY !== true || process.stdin.isTTY !== true || isNonInteractiveEnvironment() || nodeMajorVersion < 22) {
97532
97562
  await inspectAction(directory, {
97533
97563
  deadCode,
97534
97564
  score: options.score === false ? false : void 0,
97565
+ supplyChain,
97535
97566
  project: options.project,
97536
97567
  yes: options.yes,
97537
97568
  blocking: options.blocking
@@ -97539,12 +97570,13 @@ program.command("experimental-tui [directory]", { hidden: true }).description("[
97539
97570
  return;
97540
97571
  }
97541
97572
  recordCount(METRIC.cliInvoked, 1, { command: "experimental-tui" });
97542
- const { runScanApp } = await import("./run-scan-app-DZ5jGWFf.js");
97573
+ const { runScanApp } = await import("./run-scan-app-C9MJnVwv.js");
97543
97574
  const { shouldFail } = await runScanApp({
97544
97575
  directory,
97545
97576
  options: {
97546
97577
  deadCode,
97547
- noScore
97578
+ noScore,
97579
+ supplyChain
97548
97580
  },
97549
97581
  projectFlag: options.project,
97550
97582
  skipPrompts: options.yes ?? false,
@@ -97578,7 +97610,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
97578
97610
  handleError(error, { sentryEventId });
97579
97611
  });
97580
97612
  //#endregion
97581
- export { toForwardSlashes as A, highlighter as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, isCiEnvironment as F, sync as G, mergeReactDoctorConfigs as H, DIAGNOSTIC_CATEGORY_BUCKETS as I, void_ as K, Progress as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_DETAIL_WIDTH_FRACTION as P, Reporter as R, hasLintHardFailure as S, formatFixRecipeLine as T, resolveScanTarget as U, mapWithConcurrency as V, runPromise as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o, inspect as p, succeed$3 as q, discoverWorkspacePackages as r, setUpGitHubActions as s, exitGracefully as t, copyToClipboard as u, computeProjectedScore as v, buildSortedRuleGroups as w, buildNoScoreMessage as x, formatElapsedTime as y, getCategoryImpact as z };
97613
+ export { toForwardSlashes as A, getCategoryImpact as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, TUI_REPORT_DETAIL_WIDTH_FRACTION as F, runPromise as G, mapWithConcurrency as H, isCiEnvironment as I, succeed$3 as J, sync as K, DIAGNOSTIC_CATEGORY_BUCKETS as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_COMPACT_MAX_ROWS as P, Progress as R, hasLintHardFailure as S, formatFixRecipeLine as T, mergeReactDoctorConfigs as U, highlighter as V, resolveScanTarget as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o, inspect as p, void_ as q, discoverWorkspacePackages as r, setUpGitHubActions as s, exitGracefully as t, copyToClipboard as u, computeProjectedScore as v, buildSortedRuleGroups as w, buildNoScoreMessage as x, formatElapsedTime as y, Reporter as z };
97582
97614
 
97583
97615
  //# sourceMappingURL=cli.js.map
97584
- //# debugId=1d99d852-8548-5af9-a8d9-d49d41e704f6
97616
+ //# debugId=3ced4c14-18cb-5b0c-811c-3448b5c2f139
package/dist/lsp.js CHANGED
@@ -76182,6 +76182,7 @@ const METRIC = {
76182
76182
  projectConfigSelected: "project.config_selected",
76183
76183
  scanCompleted: "scan.completed",
76184
76184
  scanDuration: "scan.duration",
76185
+ scanFeedbackDelay: "scan.feedback_delay",
76185
76186
  scanPhaseDuration: "scan.phase_duration",
76186
76187
  scanFiles: "scan.files",
76187
76188
  scanScore: "scan.score",
@@ -76213,6 +76214,10 @@ const METRIC = {
76213
76214
  lspScanCompleted: "lsp.scan.completed",
76214
76215
  lspScanDuration: "lsp.scan.duration",
76215
76216
  lspScanDiagnostics: "lsp.scan.diagnostics",
76217
+ tuiActionMenuOpened: "tui.action_menu_opened",
76218
+ tuiCompactReportShown: "tui.compact_report_shown",
76219
+ tuiFindingNavigated: "tui.finding_navigated",
76220
+ tuiStackedReportCapped: "tui.stacked_report_capped",
76216
76221
  aiTrainingWarningShown: "ai.training.warning_shown",
76217
76222
  jsonOutUsed: "json.out_used"
76218
76223
  };
@@ -76514,5 +76519,5 @@ const startLanguageServer = () => {
76514
76519
  };
76515
76520
  //#endregion
76516
76521
  export { startLanguageServer };
76517
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c691162a-94f2-58c6-9397-fab6dbaa4d44")}catch(e){}}();
76518
- //# debugId=c691162a-94f2-58c6-9397-fab6dbaa4d44
76522
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="26eb6ceb-bf30-5c1a-b80b-534117e1557d")}catch(e){}}();
76523
+ //# debugId=26eb6ceb-bf30-5c1a-b80b-534117e1557d
@@ -1,6 +1,6 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="d3f7c231-0bd2-5808-8ce4-be477335e722")}catch(e){}}();
3
- import { A as toForwardSlashes, B as highlighter, C as buildRulePriorityMap, D as resolveBlockingLevel, E as formatLearnMoreLine, F as isCiEnvironment, G as sync, H as mergeReactDoctorConfigs, I as DIAGNOSTIC_CATEGORY_BUCKETS, K as void_, L as Progress, M as isUnicodeSupported, N as METRIC, O as isReactDoctorWorkflowInstalled, P as TUI_REPORT_DETAIL_WIDTH_FRACTION, R as Reporter, S as hasLintHardFailure, T as formatFixRecipeLine, U as resolveScanTarget, V as mapWithConcurrency, W as runPromise, _ as canAnimateOnboarding, a as resolveCliInspectOptions, b as buildCodeFrame, c as detectLaunchableAgents, d as launchCliAgent, f as isShareOptedOut, g as doctorFace, h as easeOutCubic, i as selectProjects, j as findNearestPackageDirectory, k as recordCount, l as CLI_AGENT_BINARIES, m as printFooter, n as shouldFailScanGate, o as countUniqueScannedFiles, p as inspect, q as succeed, r as discoverWorkspacePackages, s as setUpGitHubActions, t as exitGracefully, u as copyToClipboard, v as computeProjectedScore, w as buildSortedRuleGroups, x as buildNoScoreMessage, y as formatElapsedTime, z as getCategoryImpact } from "./cli.js";
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="90f58187-6a1d-56d1-a411-800f7bc51d28")}catch(e){}}();
3
+ import { A as toForwardSlashes, B as getCategoryImpact, C as buildRulePriorityMap, D as resolveBlockingLevel, E as formatLearnMoreLine, F as TUI_REPORT_DETAIL_WIDTH_FRACTION, G as runPromise, H as mapWithConcurrency, I as isCiEnvironment, J as succeed, K as sync, L as DIAGNOSTIC_CATEGORY_BUCKETS, M as isUnicodeSupported, N as METRIC, O as isReactDoctorWorkflowInstalled, P as TUI_REPORT_COMPACT_MAX_ROWS, R as Progress, S as hasLintHardFailure, T as formatFixRecipeLine, U as mergeReactDoctorConfigs, V as highlighter, W as resolveScanTarget, _ as canAnimateOnboarding, a as resolveCliInspectOptions, b as buildCodeFrame, c as detectLaunchableAgents, d as launchCliAgent, f as isShareOptedOut, g as doctorFace, h as easeOutCubic, i as selectProjects, j as findNearestPackageDirectory, k as recordCount, l as CLI_AGENT_BINARIES, m as printFooter, n as shouldFailScanGate, o as countUniqueScannedFiles, p as inspect, q as void_, r as discoverWorkspacePackages, s as setUpGitHubActions, t as exitGracefully, u as copyToClipboard, v as computeProjectedScore, w as buildSortedRuleGroups, x as buildNoScoreMessage, y as formatElapsedTime, z as Reporter } from "./cli.js";
4
4
  import path from "node:path";
5
5
  import { performance } from "node:perf_hooks";
6
6
  import { getSkillAgentConfig } from "agent-install";
@@ -428,14 +428,11 @@ const buildIssuePrompt = ({ row, projectName }) => {
428
428
  };
429
429
  //#endregion
430
430
  //#region src/cli/ink/components/diagnostic-action-menu.tsx
431
- const MODAL_BACKGROUND = "black";
432
- const MODAL_SELECTED_BACKGROUND = "white";
433
431
  const DiagnosticActionMenu = ({ title, itemLabels, focusedIndex, maxWidth }) => {
434
432
  return /* @__PURE__ */ jsxs(Box, {
435
433
  width: Math.max(24, Math.min(44, maxWidth)),
436
434
  borderStyle: "round",
437
- borderColor: "gray",
438
- backgroundColor: MODAL_BACKGROUND,
435
+ borderColor: "cyan",
439
436
  paddingX: 2,
440
437
  paddingY: 1,
441
438
  flexDirection: "column",
@@ -455,14 +452,12 @@ const DiagnosticActionMenu = ({ title, itemLabels, focusedIndex, maxWidth }) =>
455
452
  flexDirection: "column",
456
453
  children: itemLabels.map((label, index) => {
457
454
  const isFocused = index === focusedIndex;
458
- return /* @__PURE__ */ jsx(Box, {
459
- backgroundColor: isFocused ? MODAL_SELECTED_BACKGROUND : void 0,
460
- children: /* @__PURE__ */ jsxs(Text, {
461
- wrap: "truncate-end",
462
- bold: isFocused,
463
- color: isFocused ? "black" : "gray",
464
- children: [isFocused ? "› " : " ", label]
465
- })
455
+ return /* @__PURE__ */ jsxs(Text, {
456
+ wrap: "truncate-end",
457
+ bold: isFocused,
458
+ color: isFocused ? "cyan" : void 0,
459
+ dimColor: !isFocused,
460
+ children: [isFocused ? "› " : " ", label]
466
461
  }, label);
467
462
  })
468
463
  }),
@@ -514,110 +509,97 @@ const DiagnosticDetail = ({ row, rootDirectory }) => {
514
509
  const impact = getCategoryImpact(row.category);
515
510
  return /* @__PURE__ */ jsxs(Box, {
516
511
  flexDirection: "column",
517
- children: [
518
- /* @__PURE__ */ jsxs(Text, {
519
- wrap: "truncate-end",
520
- children: [
521
- /* @__PURE__ */ jsxs(Text, {
522
- color: variant.color,
523
- children: [
524
- " ",
525
- variant.icon,
526
- " "
527
- ]
528
- }),
529
- /* @__PURE__ */ jsx(Text, {
530
- color: variant.color,
531
- bold: true,
532
- children: row.title
533
- }),
534
- row.siteCount > 1 ? /* @__PURE__ */ jsxs(Text, {
535
- dimColor: true,
536
- children: [" ×", row.siteCount]
537
- }) : null
538
- ]
539
- }),
540
- /* @__PURE__ */ jsxs(Box, {
541
- flexDirection: "column",
542
- paddingLeft: 4,
543
- children: [
544
- /* @__PURE__ */ jsxs(Text, {
545
- dimColor: true,
546
- wrap: "truncate-end",
547
- children: [
548
- row.category,
549
- " · ",
550
- variant.label
551
- ]
552
- }),
553
- impact ? /* @__PURE__ */ jsx(Text, {
554
- dimColor: true,
555
- wrap: "wrap",
556
- children: impact
557
- }) : null,
558
- /* @__PURE__ */ jsx(Text, {
512
+ children: [/* @__PURE__ */ jsxs(Text, {
513
+ wrap: "truncate-end",
514
+ children: [
515
+ /* @__PURE__ */ jsxs(Text, {
516
+ color: variant.color,
517
+ children: [variant.icon, " "]
518
+ }),
519
+ /* @__PURE__ */ jsx(Text, {
520
+ color: variant.color,
521
+ bold: true,
522
+ children: row.title
523
+ }),
524
+ row.siteCount > 1 ? /* @__PURE__ */ jsxs(Text, {
525
+ dimColor: true,
526
+ children: [" ×", row.siteCount]
527
+ }) : null
528
+ ]
529
+ }), /* @__PURE__ */ jsxs(Box, {
530
+ flexDirection: "column",
531
+ paddingLeft: 2,
532
+ children: [
533
+ /* @__PURE__ */ jsxs(Text, {
534
+ dimColor: true,
535
+ wrap: "truncate-end",
536
+ children: [
537
+ row.category,
538
+ " · ",
539
+ variant.label,
540
+ " · ",
541
+ row.location
542
+ ]
543
+ }),
544
+ impact ? /* @__PURE__ */ jsx(Text, {
545
+ dimColor: true,
546
+ wrap: "wrap",
547
+ children: impact
548
+ }) : null,
549
+ /* @__PURE__ */ jsx(Text, {
550
+ wrap: "wrap",
551
+ children: representative.message
552
+ }),
553
+ codeFrame ? /* @__PURE__ */ jsx(Box, {
554
+ marginTop: 1,
555
+ children: /* @__PURE__ */ jsx(Text, { children: codeFrame })
556
+ }) : null,
557
+ representative.help ? /* @__PURE__ */ jsx(Box, {
558
+ marginTop: 1,
559
+ children: /* @__PURE__ */ jsxs(Text, {
559
560
  wrap: "wrap",
560
- children: representative.message
561
- }),
562
- /* @__PURE__ */ jsx(Text, {
563
- dimColor: true,
561
+ children: [/* @__PURE__ */ jsx(Text, {
562
+ color: "cyan",
563
+ children: "Fix "
564
+ }), representative.help]
565
+ })
566
+ }) : null,
567
+ row.learnMore ? /* @__PURE__ */ jsx(Box, {
568
+ marginTop: 1,
569
+ children: /* @__PURE__ */ jsx(Text, {
570
+ color: "blue",
564
571
  wrap: "truncate-end",
565
- children: row.location
572
+ children: row.learnMore
566
573
  })
567
- ]
568
- }),
569
- codeFrame ? /* @__PURE__ */ jsx(Box, {
570
- marginTop: 1,
571
- marginLeft: 4,
572
- borderStyle: "round",
573
- borderColor: "gray",
574
- paddingX: 1,
575
- alignSelf: "flex-start",
576
- children: /* @__PURE__ */ jsx(Text, { children: codeFrame })
577
- }) : null,
578
- representative.help ? /* @__PURE__ */ jsx(Box, {
579
- marginTop: 1,
580
- paddingLeft: 4,
581
- children: /* @__PURE__ */ jsxs(Text, {
582
- dimColor: true,
583
- wrap: "wrap",
584
- children: ["→ ", representative.help]
585
- })
586
- }) : null,
587
- row.learnMore ? /* @__PURE__ */ jsx(Box, {
588
- marginTop: 1,
589
- paddingLeft: 4,
590
- children: /* @__PURE__ */ jsx(Text, {
591
- color: "cyan",
592
- wrap: "truncate-end",
593
- children: row.learnMore
594
- })
595
- }) : null
596
- ]
574
+ }) : null
575
+ ]
576
+ })]
597
577
  });
598
578
  };
599
579
  //#endregion
600
580
  //#region src/cli/ink/components/diagnostic-item.tsx
601
581
  const DiagnosticItem = ({ row, isSelected, isRead }) => {
602
582
  const variant = severityVariant(row.severity);
583
+ const shouldHighlightSeverity = isSelected || !isRead;
584
+ let marker = "• ";
585
+ if (isRead) marker = " ";
586
+ if (isSelected) marker = "› ";
587
+ let markerColor = shouldHighlightSeverity ? variant.color : void 0;
588
+ if (isSelected) markerColor = "cyan";
603
589
  return /* @__PURE__ */ jsxs(Text, {
604
590
  wrap: "truncate-end",
605
- dimColor: isRead,
591
+ dimColor: isRead && !isSelected,
606
592
  children: [
607
593
  /* @__PURE__ */ jsx(Text, {
608
- color: isSelected ? variant.color : void 0,
609
- children: isSelected ? "›" : " "
610
- }),
611
- /* @__PURE__ */ jsx(Text, {
612
- color: isRead ? void 0 : variant.color,
613
- children: isRead ? " " : " •"
594
+ color: markerColor,
595
+ children: marker
614
596
  }),
615
597
  /* @__PURE__ */ jsx(Text, {
616
- color: isRead ? void 0 : variant.color,
617
- children: ` ${variant.icon} `
598
+ color: shouldHighlightSeverity ? variant.color : void 0,
599
+ children: `${variant.icon} `
618
600
  }),
619
601
  /* @__PURE__ */ jsx(Text, {
620
- color: isRead ? void 0 : variant.color,
602
+ color: shouldHighlightSeverity ? variant.color : void 0,
621
603
  bold: isSelected,
622
604
  children: row.title
623
605
  }),
@@ -633,9 +615,8 @@ const DiagnosticItem = ({ row, isSelected, isRead }) => {
633
615
  const StatusBar = ({ total, errorCount, warningCount, position, groupCount, unreadCount, projectCount, keyHints = /* @__PURE__ */ jsx(Text, {
634
616
  dimColor: true,
635
617
  children: "↑/↓ to move"
636
- }), exitHint = "q to quit" }) => /* @__PURE__ */ jsxs(Text, {
637
- wrap: "truncate-end",
638
- children: [
618
+ }), exitHint = "q to quit", compact = false }) => {
619
+ const counts = /* @__PURE__ */ jsxs(Fragment, { children: [
639
620
  /* @__PURE__ */ jsxs(Text, {
640
621
  bold: true,
641
622
  children: [
@@ -646,7 +627,7 @@ const StatusBar = ({ total, errorCount, warningCount, position, groupCount, unre
646
627
  }),
647
628
  /* @__PURE__ */ jsx(Text, {
648
629
  dimColor: true,
649
- children: " › "
630
+ children: " "
650
631
  }),
651
632
  /* @__PURE__ */ jsxs(Text, {
652
633
  color: "red",
@@ -654,46 +635,80 @@ const StatusBar = ({ total, errorCount, warningCount, position, groupCount, unre
654
635
  }),
655
636
  /* @__PURE__ */ jsx(Text, {
656
637
  dimColor: true,
657
- children: ", "
638
+ children: " "
658
639
  }),
659
640
  /* @__PURE__ */ jsxs(Text, {
660
641
  color: "yellow",
661
- dimColor: true,
662
642
  children: [warningCount, " warnings"]
663
- }),
664
- unreadCount !== void 0 ? /* @__PURE__ */ jsxs(Text, {
665
- color: unreadCount > 0 ? "cyan" : void 0,
666
- dimColor: unreadCount === 0,
667
- children: [
668
- " · ",
669
- unreadCount,
670
- " unread"
671
- ]
672
- }) : null,
673
- projectCount !== void 0 ? /* @__PURE__ */ jsxs(Text, {
674
- dimColor: true,
643
+ })
644
+ ] });
645
+ if (compact) return /* @__PURE__ */ jsxs(Text, {
646
+ wrap: "truncate-end",
647
+ children: [
648
+ counts,
649
+ /* @__PURE__ */ jsxs(Text, {
650
+ dimColor: true,
651
+ children: [
652
+ " · ",
653
+ position,
654
+ "/",
655
+ groupCount,
656
+ " · "
657
+ ]
658
+ }),
659
+ keyHints,
660
+ /* @__PURE__ */ jsxs(Text, {
661
+ dimColor: true,
662
+ children: [" · ", exitHint]
663
+ })
664
+ ]
665
+ });
666
+ return /* @__PURE__ */ jsxs(Box, {
667
+ flexDirection: "column",
668
+ children: [/* @__PURE__ */ jsxs(Text, {
669
+ wrap: "truncate-end",
675
670
  children: [
676
- " · ",
677
- projectCount,
678
- " ",
679
- projectCount === 1 ? "project" : "projects"
671
+ counts,
672
+ /* @__PURE__ */ jsxs(Text, {
673
+ dimColor: true,
674
+ children: [
675
+ " · ",
676
+ position,
677
+ "/",
678
+ groupCount
679
+ ]
680
+ }),
681
+ projectCount !== void 0 ? /* @__PURE__ */ jsxs(Text, {
682
+ dimColor: true,
683
+ children: [
684
+ " · ",
685
+ projectCount,
686
+ " ",
687
+ projectCount === 1 ? "project" : "projects"
688
+ ]
689
+ }) : null
680
690
  ]
681
- }) : null,
682
- /* @__PURE__ */ jsxs(Text, {
683
- dimColor: true,
691
+ }), /* @__PURE__ */ jsxs(Text, {
692
+ wrap: "truncate-end",
684
693
  children: [
685
- " ",
686
- position,
687
- "/",
688
- groupCount,
689
- " · ",
694
+ unreadCount !== void 0 ? /* @__PURE__ */ jsxs(Text, {
695
+ color: unreadCount > 0 ? "cyan" : void 0,
696
+ dimColor: unreadCount === 0,
697
+ children: [
698
+ unreadCount,
699
+ " unread",
700
+ " · "
701
+ ]
702
+ }) : null,
690
703
  keyHints,
691
- " · ",
692
- exitHint
704
+ /* @__PURE__ */ jsxs(Text, {
705
+ dimColor: true,
706
+ children: [" · ", exitHint]
707
+ })
693
708
  ]
694
- })
695
- ]
696
- });
709
+ })]
710
+ });
711
+ };
697
712
  //#endregion
698
713
  //#region src/cli/ink/components/diagnostic-list.tsx
699
714
  const ADD_TO_CI_KEY = "a";
@@ -715,6 +730,7 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
715
730
  const [isMenuOpen, setIsMenuOpen] = useState(false);
716
731
  const [menuIndex, setMenuIndex] = useState(0);
717
732
  const isSplit = layout === "split";
733
+ const isCompact = layout === "compact";
718
734
  const { rows: terminalRows } = useStdoutDimensions();
719
735
  const { selectedIndex, visibleStart, visibleEnd } = useScrollViewport({
720
736
  itemCount: entries.length,
@@ -726,6 +742,8 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
726
742
  const selectedEntry = entries[selectedIndex];
727
743
  const selected = selectedEntry?.kind === "item" ? selectedEntry.row : null;
728
744
  const selectedRuleKey = selected?.ruleKey ?? null;
745
+ const initialSelectedRuleKey = useRef(selectedRuleKey);
746
+ const didRecordFindingNavigation = useRef(false);
729
747
  const [readRuleKeys, setReadRuleKeys] = useState(() => /* @__PURE__ */ new Set());
730
748
  const [copiedRuleKey, setCopiedRuleKey] = useState(null);
731
749
  const menuLabels = useMemo(() => ["Copy prompt", ...launchableAgents.map((agentId) => getSkillAgentConfig(agentId).displayName)], [launchableAgents]);
@@ -733,6 +751,11 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
733
751
  if (!selectedRuleKey) return;
734
752
  setReadRuleKeys((previous) => previous.has(selectedRuleKey) ? previous : new Set(previous).add(selectedRuleKey));
735
753
  }, [selectedRuleKey]);
754
+ useEffect(() => {
755
+ if (!selectedRuleKey || selectedRuleKey === initialSelectedRuleKey.current || didRecordFindingNavigation.current) return;
756
+ didRecordFindingNavigation.current = true;
757
+ recordCount(METRIC.tuiFindingNavigated);
758
+ }, [selectedRuleKey]);
736
759
  const copySelectedPrompt = () => {
737
760
  if (!selected) return;
738
761
  const prompt = buildIssuePrompt({
@@ -772,6 +795,7 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
772
795
  return onExit();
773
796
  }
774
797
  if (key.return && selected) {
798
+ recordCount(METRIC.tuiActionMenuOpened);
775
799
  setMenuIndex(0);
776
800
  setIsMenuOpen(true);
777
801
  }
@@ -782,6 +806,18 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
782
806
  if (key.downArrow || input === "j") return setMenuIndex((index) => Math.min(menuLabels.length - 1, index + 1));
783
807
  if (key.return) return runMenuItem();
784
808
  }, { isActive: isMenuOpen });
809
+ if (isMenuOpen && selected) return /* @__PURE__ */ jsx(Box, {
810
+ width,
811
+ height: Math.max(6, terminalRows - 2),
812
+ justifyContent: "center",
813
+ alignItems: "center",
814
+ children: /* @__PURE__ */ jsx(DiagnosticActionMenu, {
815
+ title: selected.title,
816
+ itemLabels: menuLabels,
817
+ focusedIndex: menuIndex,
818
+ maxWidth: width
819
+ })
820
+ });
785
821
  const errorRows = rows.filter((row) => row.severity === "error");
786
822
  const warningRows = rows.filter((row) => row.severity === "warning");
787
823
  const itemPosition = entries.slice(0, selectedIndex + 1).filter((entry) => entry.kind === "item").length;
@@ -802,20 +838,7 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
802
838
  children: "✓ Copied fix prompt"
803
839
  })
804
840
  }) : null] });
805
- const keyHints = useMemo(() => isMenuOpen ? /* @__PURE__ */ jsxs(Fragment, { children: [
806
- /* @__PURE__ */ jsx(Text, {
807
- dimColor: true,
808
- children: "↑/↓ select · "
809
- }),
810
- /* @__PURE__ */ jsx(Text, {
811
- color: "cyan",
812
- children: "enter"
813
- }),
814
- /* @__PURE__ */ jsx(Text, {
815
- dimColor: true,
816
- children: " run · esc close"
817
- })
818
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
841
+ const keyHints = /* @__PURE__ */ jsxs(Fragment, { children: [
819
842
  /* @__PURE__ */ jsx(Text, {
820
843
  dimColor: true,
821
844
  children: "↑/↓ move · "
@@ -842,9 +865,9 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
842
865
  children: " add CI"
843
866
  })
844
867
  ] }) : null
845
- ] }), [isMenuOpen, showCiCallout]);
868
+ ] });
846
869
  const statusBar = /* @__PURE__ */ jsx(Box, {
847
- marginTop: 1,
870
+ marginTop: isCompact ? 0 : 1,
848
871
  children: /* @__PURE__ */ jsx(StatusBar, {
849
872
  total: sumSites(rows),
850
873
  errorCount: sumSites(errorRows),
@@ -853,62 +876,54 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
853
876
  groupCount: rows.length,
854
877
  unreadCount,
855
878
  projectCount,
856
- keyHints,
857
- exitHint
879
+ keyHints: isCompact && copiedRuleKey === selectedRuleKey ? /* @__PURE__ */ jsx(Text, {
880
+ color: "green",
881
+ children: "✓ Copied fix prompt"
882
+ }) : keyHints,
883
+ exitHint,
884
+ compact: isCompact
858
885
  })
859
886
  });
860
- const overlay = isMenuOpen && selected ? /* @__PURE__ */ jsx(Box, {
861
- position: "absolute",
862
- top: 0,
863
- left: 0,
864
- width,
865
- height: Math.max(6, terminalRows - 2),
866
- justifyContent: "center",
867
- alignItems: "center",
868
- children: /* @__PURE__ */ jsx(DiagnosticActionMenu, {
869
- title: selected.title,
870
- itemLabels: menuLabels,
871
- focusedIndex: menuIndex,
872
- maxWidth: width
873
- })
874
- }) : null;
875
887
  if (isSplit) return /* @__PURE__ */ jsxs(Box, {
876
888
  flexDirection: "column",
877
889
  width,
878
- position: "relative",
879
- children: [
880
- /* @__PURE__ */ jsxs(Box, {
881
- flexDirection: "row",
882
- children: [/* @__PURE__ */ jsxs(Box, {
883
- flexDirection: "column",
884
- width: listColumnWidth,
885
- marginRight: 1,
886
- children: [header, /* @__PURE__ */ jsx(Box, {
887
- marginTop: 1,
888
- children: listColumn
889
- })]
890
- }), /* @__PURE__ */ jsx(Box, {
891
- flexDirection: "column",
892
- width: detailColumnWidth,
893
- borderStyle: "single",
894
- borderColor: "gray",
895
- borderTop: false,
896
- borderRight: false,
897
- borderBottom: false,
898
- paddingLeft: 1,
899
- height: detailHeight,
900
- overflowY: "hidden",
901
- children: detailContent
890
+ children: [/* @__PURE__ */ jsxs(Box, {
891
+ flexDirection: "row",
892
+ children: [/* @__PURE__ */ jsxs(Box, {
893
+ flexDirection: "column",
894
+ width: listColumnWidth,
895
+ marginRight: 1,
896
+ children: [header, /* @__PURE__ */ jsx(Box, {
897
+ marginTop: 1,
898
+ children: listColumn
902
899
  })]
903
- }),
904
- statusBar,
905
- overlay
900
+ }), /* @__PURE__ */ jsx(Box, {
901
+ flexDirection: "column",
902
+ width: detailColumnWidth,
903
+ borderStyle: "single",
904
+ borderColor: "gray",
905
+ borderTop: false,
906
+ borderRight: false,
907
+ borderBottom: false,
908
+ paddingLeft: 1,
909
+ height: detailHeight,
910
+ overflowY: "hidden",
911
+ children: detailContent
912
+ })]
913
+ }), statusBar]
914
+ });
915
+ if (isCompact) return /* @__PURE__ */ jsxs(Box, {
916
+ flexDirection: "column",
917
+ width,
918
+ children: [
919
+ header,
920
+ listColumn,
921
+ statusBar
906
922
  ]
907
923
  });
908
924
  return /* @__PURE__ */ jsxs(Box, {
909
925
  flexDirection: "column",
910
926
  width,
911
- position: "relative",
912
927
  children: [
913
928
  header,
914
929
  /* @__PURE__ */ jsx(Box, {
@@ -925,8 +940,7 @@ const DiagnosticList = ({ header, rows, width, listColumnWidth, detailColumnWidt
925
940
  overflowY: "hidden",
926
941
  children: detailContent
927
942
  }),
928
- statusBar,
929
- overlay
943
+ statusBar
930
944
  ]
931
945
  });
932
946
  };
@@ -991,7 +1005,7 @@ const scoreColorName = (score) => {
991
1005
  //#endregion
992
1006
  //#region src/cli/ink/components/score-header.tsx
993
1007
  const BRANDING = "https://react.doctor";
994
- const ScoreHeader = ({ score, projectedScore, projectName, issueCount, noScoreMessage, width }) => {
1008
+ const ScoreHeader = ({ score, projectedScore, projectName, issueCount, noScoreMessage, width, compact = false }) => {
995
1009
  const { columns } = useStdoutDimensions();
996
1010
  const availableWidth = width ?? columns;
997
1011
  const { stdout } = useStdout();
@@ -1001,6 +1015,42 @@ const ScoreHeader = ({ score, projectedScore, projectName, issueCount, noScoreMe
1001
1015
  projectedScore,
1002
1016
  animate: animate && score !== null
1003
1017
  });
1018
+ if (compact) return score ? /* @__PURE__ */ jsxs(Text, {
1019
+ wrap: "truncate-end",
1020
+ children: [
1021
+ /* @__PURE__ */ jsx(Text, {
1022
+ color: scoreColorName(score.score),
1023
+ bold: true,
1024
+ children: displayScore
1025
+ }),
1026
+ /* @__PURE__ */ jsxs(Text, {
1027
+ dimColor: true,
1028
+ children: [
1029
+ " / ",
1030
+ 100,
1031
+ " "
1032
+ ]
1033
+ }),
1034
+ /* @__PURE__ */ jsx(Text, {
1035
+ color: scoreColorName(score.score),
1036
+ children: score.label
1037
+ }),
1038
+ /* @__PURE__ */ jsxs(Text, {
1039
+ dimColor: true,
1040
+ children: [" · ", projectName]
1041
+ })
1042
+ ]
1043
+ }) : /* @__PURE__ */ jsxs(Text, {
1044
+ wrap: "truncate-end",
1045
+ children: [
1046
+ "React Doctor",
1047
+ " ",
1048
+ /* @__PURE__ */ jsxs(Text, {
1049
+ dimColor: true,
1050
+ children: ["· ", noScoreMessage ?? `${issueCount} issues · ${projectName}`]
1051
+ })
1052
+ ]
1053
+ });
1004
1054
  if (!score) return /* @__PURE__ */ jsxs(Box, {
1005
1055
  flexDirection: "column",
1006
1056
  paddingLeft: 2,
@@ -1136,8 +1186,8 @@ const ScoreHeader = ({ score, projectedScore, projectName, issueCount, noScoreMe
1136
1186
  };
1137
1187
  //#endregion
1138
1188
  //#region src/cli/ink/components/report.tsx
1139
- const STACKED_FIXED_ROWS = 10;
1140
- const SPLIT_CHROME_ROWS = 9;
1189
+ const STACKED_FIXED_ROWS = 11;
1190
+ const SPLIT_CHROME_ROWS = 10;
1141
1191
  const EMPTY_LAUNCHABLE_AGENTS = [];
1142
1192
  const Report = ({ report, onExit, launchableAgents = EMPTY_LAUNCHABLE_AGENTS, onHandoff, canAddToCi, onAddToCi, projectCount, priorityScores, exitHint = "q to quit" }) => {
1143
1193
  const { rows: terminalRows, columns } = useStdoutDimensions();
@@ -1151,17 +1201,38 @@ const Report = ({ report, onExit, launchableAgents = EMPTY_LAUNCHABLE_AGENTS, on
1151
1201
  }, { isActive: diagnosticRows.length === 0 });
1152
1202
  const width = Math.max(24, columns - 2);
1153
1203
  const isWide = columns >= 120 && terminalRows >= 22;
1154
- const detailHeight = isWide ? Math.max(0, terminalRows - 2) : Math.max(0, Math.min(15, terminalRows - STACKED_FIXED_ROWS - 3));
1155
- const listHeight = Math.max(3, terminalRows - (isWide ? SPLIT_CHROME_ROWS : STACKED_FIXED_ROWS + detailHeight));
1204
+ const isCompact = !isWide && terminalRows <= TUI_REPORT_COMPACT_MAX_ROWS;
1205
+ const didRecordCompactReport = useRef(false);
1206
+ const didRecordStackedReportCap = useRef(false);
1207
+ useEffect(() => {
1208
+ if (!isCompact || didRecordCompactReport.current) return;
1209
+ didRecordCompactReport.current = true;
1210
+ recordCount(METRIC.tuiCompactReportShown);
1211
+ }, [isCompact]);
1212
+ const detailHeight = isWide ? Math.max(0, terminalRows - 3) : Math.max(0, Math.min(15, terminalRows - STACKED_FIXED_ROWS - 3));
1213
+ const availableListHeight = Math.max(3, terminalRows - (isWide ? SPLIT_CHROME_ROWS : STACKED_FIXED_ROWS + detailHeight));
1214
+ const isStackedReportCapped = !isCompact && !isWide && availableListHeight > 16;
1215
+ useEffect(() => {
1216
+ if (!isStackedReportCapped || didRecordStackedReportCap.current) return;
1217
+ didRecordStackedReportCap.current = true;
1218
+ recordCount(METRIC.tuiStackedReportCapped);
1219
+ }, [isStackedReportCapped]);
1220
+ let listHeight = availableListHeight;
1221
+ if (isCompact) listHeight = Math.max(3, terminalRows - 1 - 1);
1222
+ else if (!isWide) listHeight = Math.min(16, availableListHeight);
1156
1223
  const detailColumnWidth = Math.max(20, Math.floor(width * TUI_REPORT_DETAIL_WIDTH_FRACTION));
1157
1224
  const listColumnWidth = Math.max(20, width - detailColumnWidth - 3);
1225
+ let layout = "stacked";
1226
+ if (isCompact) layout = "compact";
1227
+ else if (isWide) layout = "split";
1158
1228
  const scoreHeader = /* @__PURE__ */ jsx(ScoreHeader, {
1159
1229
  score: report.score,
1160
1230
  projectedScore: report.projectedScore,
1161
1231
  projectName: report.projectName,
1162
1232
  issueCount: report.diagnostics.length,
1163
1233
  noScoreMessage: report.noScoreMessage,
1164
- width: isWide ? listColumnWidth : width
1234
+ width: isWide ? listColumnWidth : width,
1235
+ compact: isCompact
1165
1236
  });
1166
1237
  if (diagnosticRows.length === 0) {
1167
1238
  const lintFailureReason = report.lintFailureReason;
@@ -1194,7 +1265,7 @@ const Report = ({ report, onExit, launchableAgents = EMPTY_LAUNCHABLE_AGENTS, on
1194
1265
  detailColumnWidth,
1195
1266
  listHeight,
1196
1267
  detailHeight,
1197
- layout: isWide ? "split" : "stacked",
1268
+ layout,
1198
1269
  rootDirectory: report.rootDirectory,
1199
1270
  projectName: report.projectName,
1200
1271
  launchableAgents,
@@ -1228,7 +1299,7 @@ const Scanning = ({ progressText, liveCount, recent }) => {
1228
1299
  ]
1229
1300
  }) : null
1230
1301
  ]
1231
- }), recent.map((diagnostic) => {
1302
+ }), recent.map((diagnostic, diagnosticIndex) => {
1232
1303
  const variant = severityVariant(diagnostic.severity === "error" ? "error" : "warning");
1233
1304
  const location = formatDiagnosticSite(diagnostic);
1234
1305
  return /* @__PURE__ */ jsxs(Text, {
@@ -1245,7 +1316,7 @@ const Scanning = ({ progressText, liveCount, recent }) => {
1245
1316
  children: [" ", location]
1246
1317
  })
1247
1318
  ]
1248
- }, `${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.plugin}:${diagnostic.rule}`);
1319
+ }, `${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.plugin}:${diagnostic.rule}:${diagnosticIndex}`);
1249
1320
  })]
1250
1321
  });
1251
1322
  };
@@ -1657,5 +1728,5 @@ const runScanApp = async (input) => {
1657
1728
  //#endregion
1658
1729
  export { runScanApp };
1659
1730
 
1660
- //# sourceMappingURL=run-scan-app-DZ5jGWFf.js.map
1661
- //# debugId=d3f7c231-0bd2-5808-8ce4-be477335e722
1731
+ //# sourceMappingURL=run-scan-app-C9MJnVwv.js.map
1732
+ //# debugId=90f58187-6a1d-56d1-a411-800f7bc51d28
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.9.2-dev.201106e",
3
+ "version": "0.9.2-dev.b31fd85",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -71,7 +71,7 @@
71
71
  "vscode-uri": "^3.1.0",
72
72
  "yaml": "^2.9.0",
73
73
  "deslop-js": "0.9.2",
74
- "oxlint-plugin-react-doctor": "0.9.2-dev.201106e"
74
+ "oxlint-plugin-react-doctor": "0.9.2-dev.b31fd85"
75
75
  },
76
76
  "devDependencies": {
77
77
  "@types/babel__code-frame": "^7.27.0",
@@ -81,8 +81,8 @@
81
81
  "commander": "^14.0.3",
82
82
  "ink-testing-library": "^4.0.0",
83
83
  "ora": "^9.4.0",
84
- "@react-doctor/api": "0.9.2",
85
84
  "@react-doctor/core": "0.9.2",
85
+ "@react-doctor/api": "0.9.2",
86
86
  "@react-doctor/language-server": "0.9.2"
87
87
  },
88
88
  "engines": {