Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
getCliExitCode,
isCittyCliError,
renderCliError,
renderCliErrorDetails,
renderCliErrorJson,
shouldShowCommandExamplesOnError,
} from "@/lib/errors.ts";
Expand All @@ -39,7 +40,7 @@ import {
} from "@/lib/usage.ts";
import { findFirstPositionalToken, valueFlagsFor } from "@/lib/command-delegation.ts";
import { findEarlyBootstrapExit } from "@/lib/early-bootstrap.ts";
import { terminalError, applyTerminalColorFromContext } from "@/ui/terminal/styles.ts";
import { applyTerminalColorFromContext } from "@/ui/terminal/styles.ts";
import { maybeShowUpdateNotice } from "@/lib/updater.ts";
import { validateEnvironment } from "@/lib/env.ts";

Expand Down Expand Up @@ -140,7 +141,7 @@ function handleCliError(error: unknown): never {
console.error(renderCliError(error));

if (error instanceof CliError && error.details) {
console.error(`${terminalError("ERROR")} ${error.details}`);
console.error(renderCliErrorDetails(error.details));
}
}

Expand Down
6 changes: 4 additions & 2 deletions cli/src/commands/catalogs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { defineOperationCommand } from "@/lib/operation-command.ts";
import { defineGroupCommand, defineHttpCommand } from "@/lib/operation-command-builders.ts";
import { localPlan } from "@/lib/operation-effect.ts";
import { formatCatalogsSummary, formatCatalogsTable } from "@/features/management/render.ts";
import { terminalMetadata } from "@/ui/terminal/styles.ts";
import { span } from "@/ui/document.ts";
import { renderDisplayText } from "@/ui/terminal/styles.ts";
import {
fetchManagementCatalogRows,
managementCatalogCreateOperation,
Expand Down Expand Up @@ -89,7 +90,8 @@ const catalogsListCommand = defineOperationCommand({
...(summary !== null ? { summary } : {}),
},
humanText: formatCatalogsTable(rows),
metadataLines: summary !== null ? ["", terminalMetadata(summary)] : undefined,
metadataLines:
summary !== null ? ["", renderDisplayText([span(summary, "subtle")])] : undefined,
};
},
});
Expand Down
24 changes: 9 additions & 15 deletions cli/src/commands/completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,8 @@ import {
defaultConfigurePrompts,
type ConfigurePrompts,
} from "@/lib/profile-configure-interactive.ts";
import { document, rows, section, text } from "@/ui/document.ts";
import { document, rows, section, span, text, type DisplayText } from "@/ui/document.ts";
import { renderDocumentText } from "@/ui/renderers/terminal.ts";
import {
formatTerminalMarkdownLinks,
terminalAccent,
terminalSubtle,
terminalSuccess,
} from "@/ui/terminal/styles.ts";
import { buildCompletionSpec } from "@/lib/completion-spec.ts";
import { readEnv } from "@/lib/env.ts";
import {
Expand Down Expand Up @@ -247,11 +241,11 @@ export async function installCompletion(
}

function formatInstallMessage(result: InstallResult): string {
const startup =
const startup: DisplayText =
result.startupAction === "updated"
? `updated ${terminalAccent(result.rcPath ?? "")}`
? [span("updated "), span(result.rcPath ?? "", "accent")]
: result.startupAction === "skipped"
? `left unchanged ${terminalSubtle("(--no-rc)")}`
? [span("left unchanged "), span("(--no-rc)", "subtle")]
: "automatic";
const next =
result.startupAction === "skipped"
Expand All @@ -261,15 +255,15 @@ function formatInstallMessage(result: InstallResult): string {
return renderDocumentText(
document(
section(
text([`${terminalSuccess("✓")} Shell completion installed`]),
text([[span("✓", "success"), span(" Shell completion installed")]]),
rows([
{ label: "Shell:", value: result.shell },
{ label: "Script:", value: terminalAccent(result.completionPath) },
{ label: "Script:", value: [span(result.completionPath, "accent")] },
{ label: "Startup:", value: startup },
{ label: "Next:", value: next },
{
label: "Docs:",
value: formatTerminalMarkdownLinks(`[Shell completion](${COMPLETION_DOCS_URL})`),
value: [span("Shell completion", "accent", COMPLETION_DOCS_URL)],
},
]),
),
Expand All @@ -282,14 +276,14 @@ function formatCompletionHelpMessage(): string {
return renderDocumentText(
document(
section(
text([terminalAccent("Shell completion")]),
text([[span("Shell completion", "accent")]]),
rows([
{ label: "Install:", value: COMPLETION_GUIDANCE.install },
{ label: "Install shell:", value: COMPLETION_GUIDANCE.installShell },
{ label: "Manual:", value: COMPLETION_GUIDANCE.manual },
{
label: "Docs:",
value: formatTerminalMarkdownLinks(`[Shell completion](${COMPLETION_GUIDANCE.docs})`),
value: [span("Shell completion", "accent", COMPLETION_GUIDANCE.docs)],
},
]),
),
Expand Down
10 changes: 8 additions & 2 deletions cli/src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import {
setActiveProfile,
updateProfile,
} from "@/features/profile/model.ts";
import { terminalSuccess } from "@/ui/terminal/styles.ts";
import { readEnv, setEnv } from "@/lib/env.ts";
import { span } from "@/ui/document.ts";
import { renderDisplayText } from "@/ui/terminal/styles.ts";

function isInteractiveTerminal(): boolean {
return process.stdin.isTTY;
Expand Down Expand Up @@ -223,7 +224,12 @@ async function runLogin(args: LoginArgs, sink: OutputSink): Promise<void> {
unchanged: `using profile "${profileName}"`,
} satisfies Record<LoginProfileAction, string>;
sink.writeMetadata([
`${terminalSuccess("✓")} Logged in (${identity}) — ${profileMessages[profileAction]}; environment "${environment}".`,
renderDisplayText([
span("✓", "success"),
span(
` Logged in (${identity}) — ${profileMessages[profileAction]}; environment "${environment}".`,
),
]),
]);
}

Expand Down
11 changes: 4 additions & 7 deletions cli/src/features/api/render.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { ApiOperationDetails, ApiRouteRow } from "@/features/api/model.ts";
import { buildApiOperationDetailsView, buildApiRoutesView } from "@/features/api/views.ts";
import { renderDocument, renderDocumentText } from "@/ui/renderers/terminal.ts";
import { formatTerminalSection } from "@/ui/terminal/styles.ts";

const API_DETAILS_LABEL_WIDTH = 12;

Expand All @@ -10,11 +9,9 @@ type ApiRoutesRenderOptions = {
};

export function formatApiOperationDetails(operation: ApiOperationDetails): string {
return formatTerminalSection(
renderDocument(buildApiOperationDetailsView(operation), {
labelWidth: API_DETAILS_LABEL_WIDTH,
}),
);
return renderDocument(buildApiOperationDetailsView(operation), {
labelWidth: API_DETAILS_LABEL_WIDTH,
}).join("\n");
}

export function formatApiRoutes(rows: readonly ApiRouteRow[]): string {
Expand All @@ -38,5 +35,5 @@ export function renderApiRoutesTableSection(
rows: readonly ApiRouteRow[],
emptyMessage = "No operations found.",
): string {
return formatTerminalSection(renderDocument(buildApiRoutesView(rows, { emptyMessage })));
return renderDocumentText(buildApiRoutesView(rows, { emptyMessage }));
}
14 changes: 5 additions & 9 deletions cli/src/features/api/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import {
document,
rows,
section,
span,
table,
type DisplayDocument,
type DisplayRow,
} from "@/ui/document.ts";
import { terminalAccent } from "@/ui/terminal/styles.ts";

export type ApiRoutesViewOptions = {
emptyMessage?: string;
Expand All @@ -16,7 +16,7 @@ export type ApiRoutesViewOptions = {

function apiOperationRows(operation: ApiOperationDetails): DisplayRow[] {
return [
{ label: "Operation:", value: terminalAccent(operation.operationId) },
{ label: "Operation:", value: [span(operation.operationId, "accent")] },
{ label: "Method:", value: operation.method },
{ label: "Path:", value: operation.path },
{
Expand All @@ -42,23 +42,19 @@ export function buildApiRoutesView(
columns: [
{
header: "METHOD",
cell: (row) => row.method,
style: "httpMethod",
cell: (row) => [span(row.method, "httpMethod")],
},
{
header: "PATH",
cell: (row) => row.path,
style: "foreground",
},
{
header: "OPERATION",
cell: (row) => row.operationId,
style: "subtle",
cell: (row) => [span(row.operationId, "subtle")],
},
{
header: "SUMMARY",
cell: (row) => row.summary,
style: "muted",
cell: (row) => [span(row.summary, "muted")],
},
],
emptyMessage: options.emptyMessage ?? "No operations found.",
Expand Down
2 changes: 1 addition & 1 deletion cli/src/features/lakehouse/schema/render.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { buildSchemaTreeView } from "@/features/lakehouse/schema/views.ts";
import type { LakehouseQueryResult } from "@/lib/lakehouse-ndjson.ts";
import { renderTreeText } from "@/ui/layouts/tree.ts";
import { renderTreeText } from "@/ui/renderers/terminal.ts";

export function formatSchemaTree(result: LakehouseQueryResult, catalog: string): string {
return renderTreeText(buildSchemaTreeView(result, catalog));
Expand Down
44 changes: 25 additions & 19 deletions cli/src/features/lakehouse/schema/views.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { getQueryColumnNames } from "@/lib/lakehouse-client.ts";
import type { LakehouseQueryResult, LakehouseRow } from "@/lib/lakehouse-ndjson.ts";
import type { TreeNode, TreeView } from "@/ui/layouts/tree.ts";
import {
terminalAccent,
terminalBoolean,
terminalNumber,
terminalStrong,
terminalSubtle,
terminalWarning,
} from "@/ui/terminal/styles.ts";
import { span, type DisplaySpan } from "@/ui/document.ts";

type SchemaTreeColumn = {
name: string;
Expand Down Expand Up @@ -86,21 +79,34 @@ function buildSchemaMap(result: LakehouseQueryResult): Map<string, Map<string, S
}

function schemaColumnNode(column: SchemaTreeColumn, nameWidth: number): TreeNode {
const notNull = column.nullable === "NO" ? ` ${terminalBoolean("NOT NULL")}` : "";
const comment = column.comment ? ` ${terminalSubtle(`— ${column.comment}`)}` : "";
const label: DisplaySpan[] = [
span(column.name.padEnd(nameWidth)),
span(" "),
span(column.dataType, "number"),
];
if (column.nullable === "NO") {
label.push(span(" "), span("NOT NULL", "boolean"));
}
if (column.comment) {
label.push(span(" "), span(`— ${column.comment}`, "subtle"));
}
return {
label: `${column.name.padEnd(nameWidth)} ${terminalNumber(column.dataType)}${notNull}${comment}`,
label,
};
}

function schemaTableNode(tableName: string, table: SchemaTreeTable): TreeNode {
const typeSuffix =
table.type && table.type !== "BASE TABLE" ? ` ${terminalWarning(`(${table.type})`)}` : "";
const comment = table.comment ? ` ${terminalSubtle(`— ${table.comment}`)}` : "";
const nameWidth = Math.max(0, ...table.columns.map((column) => column.name.length));
const label: DisplaySpan[] = [span(tableName, "strong")];
if (table.type && table.type !== "BASE TABLE") {
label.push(span(" "), span(`(${table.type})`, "warning"));
}
if (table.comment) {
label.push(span(" "), span(`— ${table.comment}`, "subtle"));
}

return {
label: `${terminalStrong(tableName)}${typeSuffix}${comment}`,
label,
children: table.columns.map((column) => schemaColumnNode(column, nameWidth)),
};
}
Expand All @@ -112,8 +118,8 @@ function schemaNode(
const tableEntries = tables ? [...tables] : [];

return {
label: terminalAccent(schemaName),
emptyLabel: terminalSubtle("<no table>"),
label: [span(schemaName, "accent")],
emptyLabel: [span("<no table>", "subtle")],
children: tableEntries.map(([tableName, table]) => schemaTableNode(tableName, table)),
};
}
Expand All @@ -122,8 +128,8 @@ export function buildSchemaTreeView(result: LakehouseQueryResult, catalog: strin
const schemas = buildSchemaMap(result);

return {
title: terminalStrong(`Schemas and tables for ${catalog}`),
emptyLabel: terminalSubtle("<no schema>"),
title: [span(`Schemas and tables for ${catalog}`, "strong")],
emptyLabel: [span("<no schema>", "subtle")],
children: [...schemas.keys()]
.sort()
.map((schemaName) => schemaNode(schemaName, schemas.get(schemaName))),
Expand Down
5 changes: 2 additions & 3 deletions cli/src/features/management/render.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { CatalogRow, WhoamiResponse } from "@/features/management/model.ts";
import { buildCatalogsTableView } from "@/features/management/views.ts";
import { renderDocument } from "@/ui/renderers/terminal.ts";
import { formatTerminalSection } from "@/ui/terminal/styles.ts";
import { renderDocumentText } from "@/ui/renderers/terminal.ts";

export function formatWhoamiPrincipalLine(data: WhoamiResponse): string {
const principal = data.principal ?? {};
Expand Down Expand Up @@ -37,5 +36,5 @@ export function formatCatalogsSummary(rows: CatalogRow[]): string | null {
}

export function formatCatalogsTable(rows: CatalogRow[]): string {
return formatTerminalSection(renderDocument(buildCatalogsTableView(rows)));
return renderDocumentText(buildCatalogsTableView(rows));
}
12 changes: 6 additions & 6 deletions cli/src/features/management/views.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { CatalogRow } from "@/features/management/model.ts";
import { document, section, table, type DisplayDocument } from "@/ui/document.ts";
import { document, section, span, table, type DisplayDocument } from "@/ui/document.ts";

export function buildCatalogsTableView(rows: readonly CatalogRow[]): DisplayDocument {
return document(
section(
table({
rows,
columns: [
{ header: "SLUG", cell: (row) => row.slug, style: "accent" },
{ header: "NAME", cell: (row) => row.name, style: "strong", flex: true },
{ header: "ENGINE", cell: (row) => row.engine, style: "muted" },
{ header: "CATALOG", cell: (row) => row.catalog, style: "string", flex: true },
{ header: "TYPE", cell: (row) => row.type, style: "subtle" },
{ header: "SLUG", cell: (row) => [span(row.slug, "accent")] },
{ header: "NAME", cell: (row) => [span(row.name, "strong")], flex: true },
{ header: "ENGINE", cell: (row) => [span(row.engine, "muted")] },
{ header: "CATALOG", cell: (row) => [span(row.catalog, "string")], flex: true },
{ header: "TYPE", cell: (row) => [span(row.type, "subtle")] },
],
emptyMessage: "No catalogs found.",
options: { groupBy: (row) => row.type },
Expand Down
5 changes: 2 additions & 3 deletions cli/src/features/profile/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
TERMINAL_LABEL_WIDTH,
TERMINAL_NESTED_LABEL_WIDTH,
} from "@/ui/terminal/spacing.ts";
import { formatTerminalSection } from "@/ui/terminal/styles.ts";

export function formatProfileInspect(profile: ProfileInspect): string {
return renderDocumentText(buildProfileInspectView(profile));
Expand Down Expand Up @@ -75,15 +74,15 @@ export function formatConfigureSessionSummary(

export function formatActiveContextSummary(context: ActiveContext): string {
const lines = renderDocument(buildActiveContextSummaryView(context));
return `\n\n${formatTerminalSection(padLeft(lines))}`;
return `\n\n${padLeft(lines).join("\n")}`;
}

export function formatActiveContextDetails(context: ActiveContext): string {
const lines = renderDocument(buildActiveContextDetailsView(context), {
indent: TERMINAL_INDENT,
labelWidth: TERMINAL_LABEL_WIDTH,
});
return formatTerminalSection(lines);
return lines.join("\n");
}

export function tryFormatActiveContextSummary(profileName: string): string {
Expand Down
Loading
Loading