Skip to content

Commit c153a7e

Browse files
committed
feat: improve markdown rendering styles
Part 1: Inline code spans now render with cyan text on dark teal background with space padding for a 'pill' look, replacing the previous plain yellow text. Part 2: h1/h2 headings now display a colored divider bar (━) underneath, clamped to 30 chars. h3+ headings are now bold cyan (previously non-bold). Part 3: Update Terminal.astro hero demo and index.mdx feature terminal to show current column layout (LEVEL, SHORT ID, COUNT, SEEN, FIXABILITY, TITLE). Remove ALIAS column and .accent CSS class. Part 4: Update tests to assert new code span padding, heading divider bars, divider clamping, and h3+ no-divider behavior.
1 parent becab6f commit c153a7e

File tree

7 files changed

+113
-137
lines changed

7 files changed

+113
-137
lines changed

AGENTS.md

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ mock.module("./some-module", () => ({
629629
### Architecture
630630

631631
<!-- lore:019cb8ea-c6f0-75d8-bda7-e32b4e217f92 -->
632-
* **CLI telemetry DSN is public write-onlysafe to embed in install script**: The CLI's own Sentry DSN (\`SENTRY\_CLI\_DSN\` in \`src/lib/constants.ts\`) is a public write-only ingest key (\`1188a86f...@o1.ingest.us.sentry.io/4510776311808000\`). It's already baked into every distributed binary. Safe to hardcode in the bash install script for error reporting via the envelope APIno secrets needed. Opt-out via \`SENTRY\_CLI\_NO\_TELEMETRY=1\` (same env var the binary checks). Envelope endpoint: \`https://o1.ingest.us.sentry.io/api/{PROJECT\_ID}/envelope/\` with \`x-sentry-auth\` header containing the public key.
632+
* **CLI telemetry DSN is public write-onlysafe to embed in install script**: The CLI's Sentry DSN (\`SENTRY\_CLI\_DSN\` in \`src/lib/constants.ts\`) is a public write-only ingest key already baked into every binary. Safe to hardcode in install scripts. Opt-out: \`SENTRY\_CLI\_NO\_TELEMETRY=1\`.
633633

634634
<!-- lore:019c978a-18b5-7a0d-a55f-b72f7789bdac -->
635635
* **cli.sentry.dev is served from gh-pages branch via GitHub Pages**: \`cli.sentry.dev\` is served from gh-pages branch via GitHub Pages. Craft's gh-pages target runs \`git rm -r -f .\` before extracting docs — persist extra files via \`postReleaseCommand\` in \`.craft.yml\`. Install script supports \`--channel nightly\`, downloading from the \`nightly\` release tag directly. version.json is only used by upgrade/version-check flow.
@@ -640,22 +640,13 @@ mock.module("./some-module", () => ({
640640
<!-- lore:019c972c-9f0f-75cd-9e24-9bdbb1ac03d6 -->
641641
* **Numeric issue ID resolution returns org:undefined despite API success**: Numeric issue ID resolution in \`resolveNumericIssue()\`: (1) try DSN/env/config for org, (2) if found use \`getIssueInOrg(org, id)\` with region routing, (3) else fall back to unscoped \`getIssue(id)\`, (4) extract org from \`issue.permalink\` via \`parseSentryUrl\` as final fallback. The \`explicit-org-numeric\` case uses \`getIssueInOrg\`. \`resolveOrgAndIssueId\` no longer throws for bare numeric IDs when permalink contains the org slug.
642642
643-
<!-- lore:019cb950-9b7b-731a-9832-b7f6cfb6a6a2 -->
644-
* **Self-hosted OAuth device flow requires Sentry 26.1.0+ and SENTRY\_CLIENT\_ID**: The OAuth device flow for self-hosted Sentry requires version 26.1.0+ (PR #105675 added device flow, #106169 added public client support). Users must create a public OAuth application in Settings → Developer Settings → New Public Integration, then set both \`SENTRY\_URL\` and \`SENTRY\_CLIENT\_ID\`. The client ID is NOT optional for self-hosted — without it, \`sentry auth login\` cannot use the device flow. For older instances, the fallback is \`sentry auth login --token\`. The \`getSentryUrl()\` and \`getClientId()\` functions in \`src/lib/oauth.ts\` read these lazily (not at module load) so URL parsing from arguments can set \`SENTRY\_URL\` after import.
645-
646-
<!-- lore:019ca9c3-989c-7c8d-bcd0-9f308fd2c3d7 -->
647-
* **Sentry CLI markdown-first formatting pipeline replaces ad-hoc ANSI**: Formatters build CommonMark strings; \`renderMarkdown()\` renders to ANSI for TTY or raw markdown for non-TTY. Key helpers: \`colorTag()\`, \`mdKvTable()\`, \`mdRow()\`, \`mdTableHeader()\` (\`:\` suffix = right-aligned), \`renderTextTable()\`. \`isPlainOutput()\` checks \`SENTRY\_PLAIN\_OUTPUT\` > \`NO\_COLOR\` > \`!isTTY\`. Batch path: \`formatXxxTable()\`. Streaming path: \`StreamingTable\` (TTY) or raw markdown rows (plain). Both share \`buildXxxRowCells()\`.
648-
649643
<!-- lore:019c972c-9f0d-7c8e-95b1-7beda99c36a8 -->
650644
* **parseSentryUrl does not handle subdomain-style SaaS URLs**: parseSentryUrl in src/lib/sentry-url-parser.ts handles both path-based (\`/organizations/{org}/...\`) and subdomain-style (\`https://{org}.sentry.io/issues/123/\`) URLs. \`matchSubdomainOrg()\` extracts org from hostname ending in \`.sentry.io\`. Region subdomains (\`us\`, \`de\`) filtered by requiring org slug length > 2. Supports \`/issues/{id}/\`, \`/issues/{id}/events/{eventId}/\`, and \`/traces/{traceId}/\` paths. Self-hosted uses path-based only.
651645
652-
<!-- lore:019cc000-0000-0000-0000-consola00001 -->
653-
* **Consola chosen as CLI logger with Sentry createConsolaReporter integration**: The CLI uses \`consola\` (v3.x, ~33KB) for structured logging. Two reporters: (1) built-in FancyReporter writes to stderr with color/icons, (2) \`Sentry.createConsolaReporter()\` from \`@sentry/bun\` auto-forwards all logs to Sentry structured logs (requires \`enableLogs: true\` in Sentry.init, already set). Consola respects \`NO_COLOR\` env var natively. Level controlled by \`SENTRY_LOG_LEVEL\` env var mapped to consola numeric levels: error=0, warn=1, info=3 (default), debug=4, trace=5. Use \`consola.withTag('upgrade')\` for domain-scoped logging. User-facing messages use \`logger.info()\`/\`logger.success()\`; diagnostics use \`logger.debug()\`/\`logger.trace()\`. Global \`--verbose\`/\`--log-level\` flags pre-parsed from argv before Stricli, then set \`logger.level\` directly.
654-
655646
### Decision
656647
657648
<!-- lore:019c99d5-69f2-74eb-8c86-411f8512801d -->
658-
* **Raw markdown output for non-interactive terminals, rendered for TTY**: Output raw CommonMark when stdout is not a TTY; render through marked-terminal only for TTY. Detection: \`process.stdout.isTTY\`. Override precedence: \`SENTRY\_PLAIN\_OUTPUT\` > \`NO\_COLOR\` > auto-detect. \`--json\` always outputs JSON. Streaming formatters (log/trace) use ANSI-colored text for TTY, markdown table rows for non-TTY.
649+
* **Raw markdown output for non-interactive terminals, rendered for TTY**: Markdown-first output pipeline: custom renderer in \`src/lib/formatters/markdown.ts\` walks \`marked\` tokens to produce ANSI-styled output. Commands build CommonMark using helpers (\`mdKvTable()\`, \`mdRow()\`, \`colorTag()\`, \`escapeMarkdownCell()\`, \`safeCodeSpan()\`) and pass through \`renderMarkdown()\`. \`isPlainOutput()\` precedence: \`SENTRY\_PLAIN\_OUTPUT\` > \`NO\_COLOR\` > \`FORCE\_COLOR\` > \`!isTTY\`. \`--json\` always outputs JSON. Terminal styling: code spans render as cyan (\`codeFg: #22d3ee\`) on dark teal background (\`codeBg: #1a2f3a\`) with space padding for pill look. h1/h2 headings are bold cyan with a \`\` divider bar underneath (clamped to 30 chars via \`stringWidth\`). h3+ are bold cyan without divider. Colors defined in \`COLORS\` object in \`colors.ts\`. Tests run non-TTY so assertions match raw CommonMark; use \`stripAnsi()\` helper for rendered-mode content assertions.
659650
660651
<!-- lore:00166785-609d-4ab5-911e-ee205d17b90c -->
661652
* **whoami should be separate from auth status command**: The \`sentry auth whoami\` command should be a dedicated command separate from \`sentry auth status\`. They serve different purposes: \`status\` shows everything about auth state (token, expiry, defaults, org verification), while \`whoami\` just shows user identity (name, email, username, ID) by fetching live from \`/auth/\` endpoint. \`sentry whoami\` should be a top-level alias (like \`sentry issues\`\`sentry issue list\`). \`whoami\` should support \`--json\` for machine consumption and be lightweight — no credential verification, no defaults listing.
@@ -669,13 +660,13 @@ mock.module("./some-module", () => ({
669660
* **Bun binary build requires SENTRY\_CLIENT\_ID env var**: The build script (\`script/bundle.ts\`) requires \`SENTRY\_CLIENT\_ID\` environment variable and exits with code 1 if missing. When building locally, use \`bun run --env-file=.env.local build\` or set the env var explicitly. The binary build (\`bun run build\`) also needs it. Without it you get: \`Error: SENTRY\_CLIENT\_ID environment variable is required.\`
670661
671662
<!-- lore:019c9776-e3dd-7632-88b8-358a19506218 -->
672-
* **GitHub immutable releases prevent rolling nightly tag pattern**: getsentry/cli has immutable GitHub releases — assets can't be modified and tags can NEVER be reused. Nightly uses per-version tags (e.g., \`0.13.0-dev.1772062077\`) with API-based latest discovery; deletes all existing assets before uploading. Craft minVersion >= 2.21.0 with no \`preReleaseCommand\` silently skips \`bump-version.sh\` if the only target is \`github\`. Fix: explicitly set \`preReleaseCommand: bash scripts/bump-version.sh\`.
663+
* **GitHub immutable releases prevent rolling nightly tag pattern**: getsentry/cli has immutable GitHub releases — assets can't be modified and tags can NEVER be reused. Nightly uses per-version tags (e.g., \`0.13.0-dev.1772062077\`) with API-based latest discovery. Craft with no \`preReleaseCommand\` silently skips \`bump-version.sh\` if the only target is \`github\` — must explicitly set it.
673664
674665
<!-- lore:019cb8c2-d7b5-780c-8a9f-d20001bc198f -->
675-
* **Install script: BSD sed and awk JSON parsing breaks OCI digest extraction**: The install script parses OCI manifests with awk (no jq dependency). Key trap: \`sed 's/},{/}\n{/g'\` doesn't insert newlines on macOS BSD sed (\`\n\` is literal). Also, the first layer shares a line with the config block after \`\[{\` split. Fix: use a single awk pass tracking last-seen \`"digest"\` value, printing it when \`"org.opencontainers.image.title"\` matches target. Works because \`digest\` always precedes \`annotations\` within each OCI layer object. This avoids sed entirely and handles both GNU/BSD awk. The config digest (\`sha256:44136fa...\`) is a 2-byte \`{}\` blob — downloading it instead of the real binary causes \`gunzip: unexpected end of file\`. The install script now has fire-and-forget Sentry telemetry via \`die()\` + ERR trap, which would catch such failures automatically.
666+
* **Install script: BSD sed and awk JSON parsing breaks OCI digest extraction**: The install script parses OCI manifests with awk (no jq). Key trap: BSD sed \`\n\` is literal, not newline. Fix: single awk pass tracking last-seen \`"digest"\`, printing when \`"org.opencontainers.image.title"\` matches target. The config digest (\`sha256:44136fa...\`) is a 2-byte \`{}\` blob — downloading it instead of the real binary causes \`gunzip: unexpected end of file\`.
676667
677668
<!-- lore:019cb963-cb63-722d-9365-b34336f4766d -->
678-
* **macOS SIGKILL on MAP\_SHARED mmap of signed Mach-O binaries**: macOS AMFI (code signing enforcement) sends SIGKILL when \`MAP\_SHARED\` with \`PROT\_WRITE\` is used on a code-signed Mach-O binary. \`Bun.mmap()\` defaults to \`{ shared: true }\` (MAP\_SHARED). In \`src/lib/bspatch.ts\`, \`Bun.mmap(process.execPath)\` kills the process on macOS during delta upgrades because the running CLI binary is ad-hoc signed (all Bun binaries are). Fix: pass \`{ shared: false }\` for MAP\_PRIVATE. Since the mapping is read-only in practice, no COW pages are allocated — identical performance. Linux ELF binaries have no such restriction.
669+
* **macOS SIGKILL on MAP\_SHARED mmap of signed Mach-O binaries**: macOS AMFI (code signing enforcement) sends uncatchable SIGKILL when \`Bun.mmap()\` is used on code-signed Mach-O binaries. \`Bun.mmap()\` always requests PROT\_WRITE regardless of the \`shared\` flag, and macOS rejects ANY writable mapping (MAP\_SHARED or MAP\_PRIVATE) on signed Mach-O. PR #339's MAP\_PRIVATE fix was insufficient. Fixed by replacing \`Bun.mmap(oldPath)\` with \`new Uint8Array(await Bun.file(oldPath).arrayBuffer())\` in bspatch.ts. Costs ~100 MB heap for the old binary but avoids the uncatchable SIGKILL entirely. Sentry telemetry confirmed: zero successful macOS upgrade traces from delta-enabled versions, while Linux worked fine.
679670
680671
<!-- lore:019c969a-1c90-7041-88a8-4e4d9a51ebed -->
681672
* **Multiple mockFetch calls replace each other — use unified mocks for multi-endpoint tests**: Bun test mocking gotchas: (1) \`mockFetch()\` replaces \`globalThis.fetch\` — calling it twice replaces the first mock. Use a single unified fetch mock dispatching by URL pattern. (2) \`mock.module()\` pollutes the module registry for ALL subsequent test files. Tests using it must live in \`test/isolated/\` and run via \`test:isolated\`. (3) For \`Bun.spawn\`, use direct property assignment in \`beforeEach\`/\`afterEach\`.
@@ -688,9 +679,6 @@ mock.module("./some-module", () => ({
688679
689680
### Pattern
690681
691-
<!-- lore:019c9793-fb1c-7986-936e-57949e9a30d0 -->
692-
* **Markdown table structure for marked-terminal: blank header row + separator + data rows**: Markdown tables for marked-terminal: blank header row (\`| | |\`), separator (\`|---|---|\`), then data rows (\`| \*\*Label\*\* | value |\`). Data rows before separator produce malformed output. Escape user content via \`escapeMarkdownCell()\` in \`src/lib/formatters/markdown.ts\` — backslashes first, then pipes. CodeQL flags incomplete escaping as high severity.
693-
694682
<!-- lore:019c972c-9f11-7c0d-96ce-3f8cc2641175 -->
695683
* **Org-scoped SDK calls follow getOrgSdkConfig + unwrapResult pattern**: All org-scoped API calls in src/lib/api-client.ts: (1) call \`getOrgSdkConfig(orgSlug)\` for regional URL + SDK config, (2) spread into SDK function: \`{ ...config, path: { organization\_id\_or\_slug: orgSlug, ... } }\`, (3) pass to \`unwrapResult(result, errorContext)\`. Shared helpers \`resolveAllTargets\`/\`resolveOrgAndProject\` must NOT call \`fetchProjectId\` — commands that need it enrich targets themselves.
696684
@@ -703,5 +691,5 @@ mock.module("./some-module", () => ({
703691
### Preference
704692
705693
<!-- lore:019c9700-0fc3-730c-82c3-a290d5ecc2ea -->
706-
* **CI scripts: prefer jq/sed over node -e for JSON manipulation**: Reviewer (BYK) prefers using standard Unix tools (\`jq\`, \`sed\`, \`awk\`) over \`node -e\` for simple JSON manipulation in CI workflow scripts. For example, reading/modifying package.json version: \`jq -r .version package.json\` to read, \`jq --arg v "$NEW" '.version = $v' package.json > tmp && mv tmp package.json\` to write. This avoids requiring Node.js to be installed in CI steps that only need basic JSON operations, and is more readable for shell-centric workflows.
694+
* **CI scripts: prefer jq/sed over node -e for JSON manipulation**: Prefer \`jq\`/\`sed\`/\`awk\` over \`node -e\` for JSON manipulation in CI scripts. Example: \`jq -r .version package.json\` to read, \`jq --arg v "$NEW" '.version = $v' package.json > tmp && mv tmp package.json\` to write.
707695
<!-- End lore-managed section -->

docs/src/components/FeatureTerminal.astro

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -106,33 +106,39 @@ const { title = "Terminal" } = Astro.props;
106106
height: 0.75rem;
107107
}
108108

109-
/* Table styles */
110-
.feature-terminal-body :global(.table-header) {
111-
color: rgba(255, 255, 255, 0.4);
109+
/* Table box styles */
110+
.feature-terminal-body :global(.table-box) {
111+
font-family: 'JetBrains Mono', ui-monospace, monospace;
112112
font-size: 0.7rem;
113-
text-transform: uppercase;
114-
letter-spacing: 0.03em;
115-
padding-bottom: 0.25rem;
116-
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
113+
line-height: 1.5;
114+
margin: 0;
115+
padding: 0;
116+
background: transparent;
117+
border: none;
118+
color: rgba(255, 255, 255, 0.85);
119+
overflow-x: auto;
117120
}
118121

119-
.feature-terminal-body :global(.table-row) {
120-
font-size: 0.78rem;
122+
.feature-terminal-body :global(.table-box .border) {
123+
color: rgba(255, 255, 255, 0.15);
124+
}
125+
126+
.feature-terminal-body :global(.table-box .header-text) {
127+
color: rgba(255, 255, 255, 0.4);
128+
font-size: 0.7rem;
121129
}
122130

123-
.feature-terminal-body :global(.col-id) {
124-
min-width: 70px;
131+
.feature-terminal-body :global(.red-text) {
132+
color: #ef4444;
125133
}
126134

127-
.feature-terminal-body :global(.col-title) {
128-
flex: 1;
129-
color: rgba(255, 255, 255, 0.7);
135+
.feature-terminal-body :global(.yellow-text) {
136+
color: #eab308;
130137
}
131138

132-
.feature-terminal-body :global(.col-count) {
133-
min-width: 45px;
134-
text-align: right;
135-
color: rgba(255, 255, 255, 0.5);
139+
.feature-terminal-body :global(.alias-hl) {
140+
font-weight: 700;
141+
text-decoration: underline;
136142
}
137143

138144
@media (max-width: 640px) {

0 commit comments

Comments
 (0)