feat: add issue view with body cleaning and truncation (task 0003)
Add `issue view <n>` as the first detail command, introducing the content-cleaning and truncation machinery (src/body.ts) that later issue and PR slices reuse. - Default output: number, title, state, author, created, body (truncated at 500), plus comment_count; --comments expands every comment (bodies truncated at 800); --full suppresses all truncation. - cleanBody runs only when a body exceeds its limit: normalizes Gitea issue/PR URLs on the detected host to Issue#N/PR#N, strips image embeds and long URLs, and collapses quoted blocks. - Type guard: a PR number fails with VALIDATION_ERROR and a `pr view <n>` hint, detected via the fetched object's pull_request field. - renderDetail joins the detail entity, optional sub-blocks, and help.
This commit was merged in pull request #2.
This commit is contained in:
@@ -28,6 +28,11 @@ export function formatCountLine(
|
||||
return `count: ${shown} of ${total} total`;
|
||||
}
|
||||
|
||||
/** Encode a named list block, with an explicit empty-state line when there are no rows. */
|
||||
function encodeRows(noun: string, rows: Record<string, unknown>[]): string {
|
||||
return rows.length > 0 ? encode({ [noun]: rows }) : `${noun}[0]: (none)`;
|
||||
}
|
||||
|
||||
export interface RenderListOptions {
|
||||
noun: string;
|
||||
rows: Record<string, unknown>[];
|
||||
@@ -36,9 +41,28 @@ export interface RenderListOptions {
|
||||
}
|
||||
|
||||
export function renderList(options: RenderListOptions): string {
|
||||
const body =
|
||||
options.rows.length > 0
|
||||
? encode({ [options.noun]: options.rows })
|
||||
: `${options.noun}[0]: (none)`;
|
||||
const body = encodeRows(options.noun, options.rows);
|
||||
return [options.countLine, body, encode({ help: options.help })].join("\n");
|
||||
}
|
||||
|
||||
/** A secondary list block appended below a detail entity (e.g. comments). */
|
||||
export interface DetailBlock {
|
||||
noun: string;
|
||||
rows: Record<string, unknown>[];
|
||||
}
|
||||
|
||||
export interface RenderDetailOptions {
|
||||
noun: string;
|
||||
item: Record<string, unknown>;
|
||||
blocks?: DetailBlock[];
|
||||
help: string[];
|
||||
}
|
||||
|
||||
export function renderDetail(options: RenderDetailOptions): string {
|
||||
const parts = [encode({ [options.noun]: options.item })];
|
||||
for (const block of options.blocks ?? []) {
|
||||
parts.push(encodeRows(block.noun, block.rows));
|
||||
}
|
||||
parts.push(encode({ help: options.help }));
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user