@effected/cli 0.11.0 → 0.13.0
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/CliFailure.js +57 -8
- package/CliLog.js +53 -1
- package/CliMessage.js +3 -6
- package/CliRuntime.js +20 -10
- package/CliTheme.js +28 -15
- package/Doc.js +31 -7
- package/README.md +22 -6
- package/Render.js +6 -5
- package/Status.js +5 -2
- package/index.d.ts +220 -28
- package/internal/counts.js +17 -2
- package/internal/failureTarget.js +53 -14
- package/internal/renderDoc.js +4 -3
- package/internal/renderMarkdown.js +6 -5
- package/package.json +7 -2
- package/ui/CliUi.js +91 -7
- package/ui/CliUiLive.js +63 -16
- package/ui/Select.js +5 -1
- package/ui/TextInput.js +51 -11
- package/ui/internal/lazyView.js +74 -0
- package/ui/testing/CliUiTest.js +47 -22
- package/ui/testing/fakeStreams.js +6 -3
- package/ui-testing-serializer.d.ts +14 -0
- package/ui-testing-serializer.js +33 -0
- package/ui-testing.d.ts +93 -12
- package/ui.d.ts +154 -12
package/index.d.ts
CHANGED
|
@@ -493,7 +493,9 @@ export declare class Status<Names extends string> {
|
|
|
493
493
|
* that draws it itself (an Ink tree, a reporter).
|
|
494
494
|
*
|
|
495
495
|
* @remarks
|
|
496
|
-
* Throws on an unknown name, as {@link Status.def} does.
|
|
496
|
+
* Throws on an unknown name, as {@link Status.def} does. The glyph is sanitised, as text in a document is: escape
|
|
497
|
+
* sequences and control characters in a vocabulary's glyph are removed, so a glyph built from data cannot paint the
|
|
498
|
+
* terminal, plant a hyperlink or move the cursor. Every kit path that draws a status glyph takes it from here.
|
|
497
499
|
*
|
|
498
500
|
* @param name - a name in this vocabulary
|
|
499
501
|
* @param glyphs - the glyph set, such as `Glyphs.unicode`, `Glyphs.ascii` or a theme's
|
|
@@ -567,7 +569,10 @@ interface StreamTheme {
|
|
|
567
569
|
readonly glyphs: GlyphSet;
|
|
568
570
|
/** The colour level of the stream. */
|
|
569
571
|
readonly color: ColorLevel;
|
|
570
|
-
/**
|
|
572
|
+
/**
|
|
573
|
+
* Render a status from a vocabulary: its glyph, sanitised (see `Status.glyph`) and painted with its token, then
|
|
574
|
+
* `text` when given, as it is: sanitising `text` is the caller's, as `CliMessage` and `CliLog.status` do.
|
|
575
|
+
*/
|
|
571
576
|
readonly status: <N extends string>(vocab: Status<N>, name: N, text?: string) => string;
|
|
572
577
|
}
|
|
573
578
|
/**
|
|
@@ -617,6 +622,30 @@ export declare class CliTheme extends CliTheme_base {
|
|
|
617
622
|
* @param options - token overrides and the glyph set
|
|
618
623
|
*/
|
|
619
624
|
static readonly layer: (options?: CliThemeOptions) => Layer.Layer<CliTheme, never, TerminalEnv>;
|
|
625
|
+
/**
|
|
626
|
+
* The theme an audience sees of `theme`: for an agent, the same theme at colour `none` (`paint` the identity, `sgr`
|
|
627
|
+
* empty, `status` unpainted), whatever the terminal could do, because an agent never gets an escape of any kind; for
|
|
628
|
+
* anyone else, or when the audience is not known, `theme` itself.
|
|
629
|
+
*
|
|
630
|
+
* @remarks
|
|
631
|
+
* The one rule the kit applies wherever it paints for an audience: `Render.context` takes its colour and `paint`
|
|
632
|
+
* from it, `CliMessage` and `CliLog.status` paint their glyphs through it, and `./ui` gives it to the trees it
|
|
633
|
+
* mounts, so `useTheme`, `Styled` and the widgets' colour-`none` text markers all agree. A program that paints its
|
|
634
|
+
* own lines applies the same rule with it rather than re-implementing it:
|
|
635
|
+
*
|
|
636
|
+
* ```ts
|
|
637
|
+
* const line = Effect.gen(function* () {
|
|
638
|
+
* const theme = CliTheme.forAudience((yield* CliTheme).forStream("stdout"), (yield* Audience).kind)
|
|
639
|
+
* return theme.status(Status.core, "success", Fmt.sanitize(name))
|
|
640
|
+
* })
|
|
641
|
+
* ```
|
|
642
|
+
*
|
|
643
|
+
* Pure: it reads nothing, so the audience is the caller's to pass, `undefined` when it is not known.
|
|
644
|
+
*
|
|
645
|
+
* @param theme - a stream's theme, such as `CliTheme.forStream("stdout")`
|
|
646
|
+
* @param audience - who the output is for, or `undefined` when that is not known
|
|
647
|
+
*/
|
|
648
|
+
static readonly forAudience: (theme: StreamTheme, audience: AudienceKind | undefined) => StreamTheme;
|
|
620
649
|
/**
|
|
621
650
|
* A fixed theme that needs nothing; `none` colour and Unicode glyphs unless told otherwise.
|
|
622
651
|
*
|
|
@@ -738,8 +767,16 @@ interface Column {
|
|
|
738
767
|
interface Counter {
|
|
739
768
|
/** A stable identifier, for a caller's total rule. */
|
|
740
769
|
readonly key: string;
|
|
741
|
-
/**
|
|
742
|
-
|
|
770
|
+
/**
|
|
771
|
+
* What the counter is called when shown: one label, or a singular and a plural form, `one` for a count of exactly 1
|
|
772
|
+
* and `other` for any other, 0 included. The count is the counter's own `n`, except in a share headline
|
|
773
|
+
* (`1/3 repos`), which reads by the total. A `CountsTable` heads its column with `other`, since the column holds
|
|
774
|
+
* every row's count.
|
|
775
|
+
*/
|
|
776
|
+
readonly label: string | {
|
|
777
|
+
readonly one: string;
|
|
778
|
+
readonly other: string;
|
|
779
|
+
};
|
|
743
780
|
/** The count. */
|
|
744
781
|
readonly n: number;
|
|
745
782
|
/** The status the count is painted with. */
|
|
@@ -767,7 +804,7 @@ interface Counter {
|
|
|
767
804
|
* - `CountsTable`: a table of `Counts` rows, a column per counter key, a `duration` column when some row has one, and
|
|
768
805
|
* an optional summed total row.
|
|
769
806
|
* - `Lines`: one line per entry; markdown keeps them apart with hard breaks.
|
|
770
|
-
* - `Line`: one line, which `truncate` cuts to the width instead of wrapping.
|
|
807
|
+
* - `Line`: one line, which `truncate` cuts to the width instead of wrapping, and `wrap: false` keeps whole.
|
|
771
808
|
* - `DiffText`: a unified diff, as given; `truncate` cuts each line to the width.
|
|
772
809
|
* - A `List` may be `compact`, with no blank lines between an item's children (a blank line of an item's own content
|
|
773
810
|
* keeps the item's indent in plain and `ansi`), and a `Table` may be `style: "pipe"`.
|
|
@@ -846,6 +883,7 @@ type Block = {
|
|
|
846
883
|
readonly _tag: "Line";
|
|
847
884
|
readonly content: ReadonlyArray<Inline>;
|
|
848
885
|
readonly truncate?: boolean;
|
|
886
|
+
readonly wrap?: boolean;
|
|
849
887
|
} | {
|
|
850
888
|
readonly _tag: "DiffText";
|
|
851
889
|
readonly text: string;
|
|
@@ -1208,6 +1246,11 @@ export declare class Doc {
|
|
|
1208
1246
|
/**
|
|
1209
1247
|
* Children under an optional title.
|
|
1210
1248
|
*
|
|
1249
|
+
* @remarks
|
|
1250
|
+
* The children are separated by blank lines (unless the document is compact); a title sits directly above the first.
|
|
1251
|
+
* `Doc.section(undefined, blocks)` is the way to space a document's top-level blocks, which are otherwise joined with
|
|
1252
|
+
* no blank line.
|
|
1253
|
+
*
|
|
1211
1254
|
* @param title - the title, or `undefined` for none
|
|
1212
1255
|
* @param children - the blocks
|
|
1213
1256
|
*/
|
|
@@ -1218,13 +1261,22 @@ export declare class Doc {
|
|
|
1218
1261
|
* @remarks
|
|
1219
1262
|
* A name the vocabulary does not have is a compile error.
|
|
1220
1263
|
*
|
|
1264
|
+
* The label is one string, or `{ one, other }` to pluralise by count: `one` when the count is exactly 1 and `other`
|
|
1265
|
+
* for every other count, 0 included. A count standing alone reads by its own `n` (`1 change`, `2 changes`); a
|
|
1266
|
+
* headline shown as a share of the total reads by that total, the noun it counts (`1/1 repo`, `1/3 repos`,
|
|
1267
|
+
* `2/3 repos`).
|
|
1268
|
+
*
|
|
1221
1269
|
* @param vocab - the vocabulary the status belongs to
|
|
1222
1270
|
* @param name - a status name in it
|
|
1223
|
-
* @param options - the counter's `key`, `label`
|
|
1271
|
+
* @param options - the counter's `key`, its `label` (one string, or `{ one, other }`), its count `n`, and `showZero`
|
|
1272
|
+
* to keep it when `n` is zero
|
|
1224
1273
|
*/
|
|
1225
1274
|
static counter<N extends string>(vocab: Status<N>, name: NoInfer<N>, options: {
|
|
1226
1275
|
readonly key: string;
|
|
1227
|
-
readonly label: string
|
|
1276
|
+
readonly label: string | {
|
|
1277
|
+
readonly one: string;
|
|
1278
|
+
readonly other: string;
|
|
1279
|
+
};
|
|
1228
1280
|
readonly n: number;
|
|
1229
1281
|
readonly showZero?: boolean;
|
|
1230
1282
|
}): Counter;
|
|
@@ -1264,17 +1316,22 @@ export declare class Doc {
|
|
|
1264
1316
|
*/
|
|
1265
1317
|
static lines(lines: ReadonlyArray<InlineInput>): BlockOf<"Lines">;
|
|
1266
1318
|
/**
|
|
1267
|
-
* One line of content; with `truncate`, it is cut to the width with the glyph set's ellipsis instead of wrapping
|
|
1319
|
+
* One line of content; with `truncate`, it is cut to the width with the glyph set's ellipsis instead of wrapping,
|
|
1320
|
+
* and with `wrap: false` it is kept whole on one line whatever the width.
|
|
1268
1321
|
*
|
|
1269
1322
|
* @remarks
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1323
|
+
* By default a line longer than the width wraps. `wrap: false` keeps it atomic in every audience and renderer, still
|
|
1324
|
+
* carrying its status glyphs, theme tokens and links, which {@link Doc.verbatim} (a plain string) cannot: the tool for
|
|
1325
|
+
* a finding such as `✗ path:line:col rule message` that a reader greps or reads line by line, while the prose around
|
|
1326
|
+
* it still wraps. A line break inside it is still a space. With both `truncate` and `wrap: false`, `truncate` wins:
|
|
1327
|
+
* the line is cut to the width.
|
|
1272
1328
|
*
|
|
1273
1329
|
* @param content - the line
|
|
1274
|
-
* @param options - `truncate`
|
|
1330
|
+
* @param options - `truncate`, to cut it to the width; `wrap: false`, to keep it whole
|
|
1275
1331
|
*/
|
|
1276
1332
|
static line(content: InlineInput, options?: {
|
|
1277
1333
|
readonly truncate?: boolean;
|
|
1334
|
+
readonly wrap?: boolean;
|
|
1278
1335
|
}): BlockOf<"Line">;
|
|
1279
1336
|
/**
|
|
1280
1337
|
* A unified diff as given, such as a test runner's: sanitized, its `+` and `-` lines painted `success` and
|
|
@@ -1345,11 +1402,15 @@ export declare class Doc {
|
|
|
1345
1402
|
* The context is {@link Render.context} for the stream, so the width, the colour, the links and the audience
|
|
1346
1403
|
* come from the services the program already has, and the text is written with `Console.log` or
|
|
1347
1404
|
* `Console.error`: a test captures it by swapping the `Console`. With `format: "auto"` the renderer follows
|
|
1348
|
-
* the audience, and the width is unbounded for an agent and a
|
|
1405
|
+
* the audience, and the width is unbounded for an agent, a CI, and a human whose stream is not a terminal.
|
|
1349
1406
|
*
|
|
1350
1407
|
* An agent is never written an escape of any kind, even with an explicit `format: "ansi"`: its context is
|
|
1351
1408
|
* colourless and its links are off. A document that renders to nothing prints nothing.
|
|
1352
1409
|
*
|
|
1410
|
+
* The whole document is written as one `Console.log` (or `Console.error`) call, with its line breaks embedded, so a
|
|
1411
|
+
* captured `Console` holds one entry per document, not one per line. Top-level blocks are joined with no blank
|
|
1412
|
+
* line between them; wrap them in `Doc.section(undefined, [...])` to space them.
|
|
1413
|
+
*
|
|
1353
1414
|
* `CurrentRuntimeEnv` is read if the environment has one and is not required: a `ci` audience prints
|
|
1354
1415
|
* GitHub's log format only when it says GitHub Actions, and plain text otherwise, including when it is
|
|
1355
1416
|
* absent. An explicit `format` is honoured whatever the audience.
|
|
@@ -1565,6 +1626,24 @@ export declare class CliLogger {
|
|
|
1565
1626
|
}
|
|
1566
1627
|
//#endregion
|
|
1567
1628
|
//#region src/CliLog.d.ts
|
|
1629
|
+
/**
|
|
1630
|
+
* Options for `CliLog.status`.
|
|
1631
|
+
*
|
|
1632
|
+
* @public
|
|
1633
|
+
*/
|
|
1634
|
+
interface CliLogStatusOptions {
|
|
1635
|
+
/**
|
|
1636
|
+
* The level the line is logged at. By default it follows the status's rank in its vocabulary: `Error` at or above
|
|
1637
|
+
* `failure`'s rank, `Warn` at or above `warning`'s, and `Info` below that.
|
|
1638
|
+
*/
|
|
1639
|
+
readonly level?: LogLevel.Severity | undefined;
|
|
1640
|
+
/**
|
|
1641
|
+
* What the line starts with, before the glyph, so an indented report line keeps its place in a block: a number of
|
|
1642
|
+
* spaces (floored, at most 64; zero, a negative or a non-finite number is none), or a string. A string is sanitised as text is (escapes and controls removed, a tab a space) and its line
|
|
1643
|
+
* breaks dropped, so the indent can never carry an escape onto the trusted line. None by default.
|
|
1644
|
+
*/
|
|
1645
|
+
readonly indent?: number | string | undefined;
|
|
1646
|
+
}
|
|
1568
1647
|
/**
|
|
1569
1648
|
* Options for `CliLog.layer`.
|
|
1570
1649
|
*
|
|
@@ -1861,6 +1940,41 @@ export declare class CliLog {
|
|
|
1861
1940
|
* @param options - the level, the env var, the format, the `CliLogger` options and the file sink
|
|
1862
1941
|
*/
|
|
1863
1942
|
static layer(options: CliLogFileOptions): Layer.Layer<never, never, Audience | TerminalEnv | FileSystem.FileSystem | Path.Path>;
|
|
1943
|
+
/**
|
|
1944
|
+
* Log a status line: its glyph painted through the theme, then `text`, at a level that follows the status.
|
|
1945
|
+
*
|
|
1946
|
+
* @remarks
|
|
1947
|
+
* The line goes through the logger, so it is a diagnostic like any `Effect.log*` call: filtered by the level in
|
|
1948
|
+
* force (`--log-level`, `CliLog.Level`), routed by `CliLogger`'s `stderrFrom` (stderr by default) and neutralized
|
|
1949
|
+
* under GitHub Actions. What differs is the glyph: the logger sanitises every line a program logs, which strips a
|
|
1950
|
+
* colour a program painted itself, so a glyph on the log channel was always drawn bare. Here the kit paints it and
|
|
1951
|
+
* marks the line as its own, so the plain `CliLogger` line keeps the colour, while `text` is still sanitised:
|
|
1952
|
+
* escape sequences and control characters in it are removed, as in every line the kit writes.
|
|
1953
|
+
*
|
|
1954
|
+
* The glyph is painted with stderr's theme (where diagnostics go) through {@link CliTheme.forAudience}, so an agent
|
|
1955
|
+
* gets it unpainted, and an `Audience` is read only when provided, so it stays out of the requirements. ASCII glyphs
|
|
1956
|
+
* give the status's ASCII form. A diagnostics record carries the line as its message as any record does: the `CliLog`
|
|
1957
|
+
* sink's pretty line sanitises it (the glyph is drawn bare there), and NDJSON keeps it, JSON-escaped.
|
|
1958
|
+
*
|
|
1959
|
+
* The level defaults to the status's rank in `vocab`: `Error` at or above `failure`'s, `Warn` at or above
|
|
1960
|
+
* `warning`'s, `Info` below, so a custom status follows its own rank. Pass `level` to choose it, and `indent` (a
|
|
1961
|
+
* number of spaces, or a string, sanitised) to start the line inside an indented block: ` ✗ error x: red`.
|
|
1962
|
+
*
|
|
1963
|
+
* @example
|
|
1964
|
+
* ```ts
|
|
1965
|
+
* import { CliLog, Status } from "@effected/cli"
|
|
1966
|
+
*
|
|
1967
|
+
* // ✗ in the failure colour, then the message: on stderr, filtered by the log level.
|
|
1968
|
+
* const reportError = (resource: string, message: string) =>
|
|
1969
|
+
* CliLog.status(Status.core, "failure", `${resource}: ${message}`)
|
|
1970
|
+
* ```
|
|
1971
|
+
*
|
|
1972
|
+
* @param vocab - the vocabulary the status belongs to
|
|
1973
|
+
* @param name - the status
|
|
1974
|
+
* @param text - the text after the glyph, sanitised
|
|
1975
|
+
* @param options - the level to log at, and the indent before the glyph
|
|
1976
|
+
*/
|
|
1977
|
+
static readonly status: <N extends string>(vocab: Status<N>, name: N, text: string, options?: CliLogStatusOptions) => Effect.Effect<void, never, CliTheme>;
|
|
1864
1978
|
/**
|
|
1865
1979
|
* Mark the log records an effect emits as coming from `name`.
|
|
1866
1980
|
*
|
|
@@ -1935,6 +2049,27 @@ interface CliEnvOptions {
|
|
|
1935
2049
|
* `CliRuntime.main` reads this.
|
|
1936
2050
|
*/
|
|
1937
2051
|
readonly stackFrames?: "app" | "all" | undefined;
|
|
2052
|
+
/**
|
|
2053
|
+
* Which spans the default failure report's `in: outer › inner` trail names: `app`, the default, leaves out the spans
|
|
2054
|
+
* the kit's own packages and Effect define (judged by the file of each span's definition site under `node_modules`),
|
|
2055
|
+
* `all` shows every span, and `off` drops the trail. Applies to the report `main` writes,
|
|
2056
|
+
* `FailureDetails.defaultLines` and `FailureDetails.lines`. Only `CliRuntime.main` reads this.
|
|
2057
|
+
*/
|
|
2058
|
+
readonly spans?: "app" | "all" | "off" | undefined;
|
|
2059
|
+
/**
|
|
2060
|
+
* The environment variable that sets `spans` at run time (`app`, `all` or `off`, case-insensitive), read through
|
|
2061
|
+
* `Config`, as `log.envVar` sets the log level: so a user filing a bug can run `TOOL_SPANS=all tool …` without a
|
|
2062
|
+
* rebuild. `spans` beats it (the variable is then not read at all); unset or empty is the default; a value that is
|
|
2063
|
+
* not a setting is ignored with one warning. Not read unless named. Only `CliRuntime.main` reads this.
|
|
2064
|
+
*/
|
|
2065
|
+
readonly spansEnvVar?: string | undefined;
|
|
2066
|
+
/**
|
|
2067
|
+
* A module of the running program itself, as a `file:` URL or an absolute path: pass the bin's `import.meta.url`.
|
|
2068
|
+
* `spans: "app"` keeps the spans of the package that holds it, even when it is installed under
|
|
2069
|
+
* `node_modules/@effected/` (a kit companion's bin); see `CliFailureOptions.appModule`. Only `CliRuntime.main`
|
|
2070
|
+
* reads this.
|
|
2071
|
+
*/
|
|
2072
|
+
readonly appModule?: string | undefined;
|
|
1938
2073
|
/** Whether file links open in an editor; `auto` by default. See {@link CliLinks}. */
|
|
1939
2074
|
readonly editorLinks?: EditorLinks | undefined;
|
|
1940
2075
|
/** The environment variable that overrides `editorLinks`, read through `Config`. Not read unless named. */
|
|
@@ -2137,6 +2272,32 @@ interface CliFailureOptions {
|
|
|
2137
2272
|
* shows every frame, unfiltered.
|
|
2138
2273
|
*/
|
|
2139
2274
|
readonly stackFrames?: "app" | "all" | undefined;
|
|
2275
|
+
/**
|
|
2276
|
+
* Which spans the `in: outer › inner` trail after a failure names. `app`, the default, leaves out the spans the kit
|
|
2277
|
+
* and Effect define themselves: a span whose definition site (an `Effect.fn`'s, or where the span was opened) is a
|
|
2278
|
+
* file under `node_modules/@effected/` or `node_modules/effect/`. A config that fails to decode under
|
|
2279
|
+
* `@effected/config-file` then reports no `ConfigFile.loadFrom` trail, while the program's own spans stay. `all`
|
|
2280
|
+
* shows every span, and `off` drops the trail. Under either, an `Effect.fn` call and its definition are one entry,
|
|
2281
|
+
* `name`, never `name (definition) › name`.
|
|
2282
|
+
*
|
|
2283
|
+
* @remarks
|
|
2284
|
+
* The rule reads file paths, never span names, and it fails open: when it cannot tell, it shows the span. A span
|
|
2285
|
+
* with no captured stack is kept. A kit package linked into a workspace (`link:`, `workspace:`) runs from its own
|
|
2286
|
+
* checkout rather than from `node_modules`, so its spans show as the program's. A bundled program is one file, so
|
|
2287
|
+
* `app` shows what `all` does, unless the bundle is itself installed under `node_modules/@effected/`. A program that
|
|
2288
|
+
* is installed under `node_modules/@effected/` (a kit companion's bin) names itself with `appModule`, or its own
|
|
2289
|
+
* spans are left out with the kit's.
|
|
2290
|
+
*/
|
|
2291
|
+
readonly spans?: "app" | "all" | "off" | undefined;
|
|
2292
|
+
/**
|
|
2293
|
+
* A module of the running program itself, as a `file:` URL or an absolute path: its bin's `import.meta.url`. With
|
|
2294
|
+
* `spans: "app"`, a span defined in the installed package that holds this module is the program's, kept even under
|
|
2295
|
+
* `node_modules/@effected/`, while the packages it depends on (beside it, or nested in its own `node_modules`) are
|
|
2296
|
+
* still left out. The package is the path through the last `node_modules/<name>` (or `node_modules/@scope/name`)
|
|
2297
|
+
* in it, read from the path alone with no file system. A module not under `node_modules` changes nothing: the rule
|
|
2298
|
+
* already keeps its spans. Unset by default.
|
|
2299
|
+
*/
|
|
2300
|
+
readonly appModule?: string | undefined;
|
|
2140
2301
|
}
|
|
2141
2302
|
/**
|
|
2142
2303
|
* A failure as a document: what the default report prints, and a building block for a custom one.
|
|
@@ -2155,7 +2316,7 @@ interface CliFailureOptions {
|
|
|
2155
2316
|
* `Error.cause` chain as a tree. When cleaning leaves no frame the stack says
|
|
2156
2317
|
* `no user frames (N internal frames hidden)`, never an empty block; when frames survive and some were left out, the
|
|
2157
2318
|
* count follows them as `(+N internal frames hidden)`. A reason that ran under spans is followed by
|
|
2158
|
-
* `in: outer › inner
|
|
2319
|
+
* `in: outer › inner`: by default only the program's own spans, the kit's and Effect's left out (`spans`). Interrupts are not rendered beside a real failure, and a cause with only interrupts is the
|
|
2159
2320
|
* one line `interrupted`.
|
|
2160
2321
|
*
|
|
2161
2322
|
* All text goes through the document, so a control character in a message or a stack frame never reaches the terminal.
|
|
@@ -2257,8 +2418,8 @@ interface CliMessageOptions {
|
|
|
2257
2418
|
*
|
|
2258
2419
|
* The text is whatever the caller supplies, so it is sanitised: escape sequences and control characters are removed
|
|
2259
2420
|
* (a line break is kept as one, a tab becomes a space), as in a document. Under GitHub Actions, where
|
|
2260
|
-
* `CurrentRuntimeEnv` says so, a line the runner would read as a workflow command is neutralized as well. The
|
|
2261
|
-
*
|
|
2421
|
+
* `CurrentRuntimeEnv` says so, a line the runner would read as a workflow command is neutralized as well. The glyph
|
|
2422
|
+
* comes from the vocabulary, sanitised too (`Status.glyph`), so a glyph built from data cannot inject an escape either.
|
|
2262
2423
|
*
|
|
2263
2424
|
* @public
|
|
2264
2425
|
*/
|
|
@@ -2407,6 +2568,24 @@ interface FailureDetails {
|
|
|
2407
2568
|
* failure from the error channel.
|
|
2408
2569
|
*/
|
|
2409
2570
|
readonly isDefect: boolean;
|
|
2571
|
+
/**
|
|
2572
|
+
* `true` when the squashed `error` is the kit's {@link Cancelled}: the person quit a prompt or screen (Esc, Ctrl-C).
|
|
2573
|
+
* It is not a bug and there is nothing to report, whichever channel it arrived through.
|
|
2574
|
+
*
|
|
2575
|
+
* @remarks
|
|
2576
|
+
* A cancel from `CliPrompt.fallback` is a defect (`isDefect` is `true`) and one from `CliUi.run` is a typed failure
|
|
2577
|
+
* (`isDefect` is `false`), so a `render` that gives a defect the "please report this" treatment must test this flag
|
|
2578
|
+
* first, or Esc prints an issue-report request. `isDefect` keeps its plain meaning, the absence of a typed failure,
|
|
2579
|
+
* and the exit code is unchanged (`130` for the `interrupt` reason). `defaultLines` and `lines()` already draw it as
|
|
2580
|
+
* its one fixed line.
|
|
2581
|
+
*/
|
|
2582
|
+
readonly isCancelled: boolean;
|
|
2583
|
+
/**
|
|
2584
|
+
* `true` when the squashed `error` is the kit's {@link NotInteractive}: the program asked for a prompt or a screen in
|
|
2585
|
+
* a run that cannot show one (a pipe, an agent, a CI). It is a usage problem, not a bug, whichever channel it
|
|
2586
|
+
* arrived through; `isDefect` is as for {@link FailureDetails.isCancelled}.
|
|
2587
|
+
*/
|
|
2588
|
+
readonly isNotInteractive: boolean;
|
|
2410
2589
|
/**
|
|
2411
2590
|
* The report the kit writes for this failure when there is no `render`: for this run, in this audience, with its
|
|
2412
2591
|
* colour, links and `displayPath`. A `render` that hands a failure back returns these lines unchanged, and the
|
|
@@ -2429,10 +2608,14 @@ interface FailureDetails {
|
|
|
2429
2608
|
* details.lines({ status: false }).map((line, i) => (i === 0 ? `prog: ${line}` : line))
|
|
2430
2609
|
* ```
|
|
2431
2610
|
*
|
|
2432
|
-
*
|
|
2611
|
+
* `spans` chooses the `in:` trail for these lines alone, as `CliFailureOptions.spans` does (`app`, `all` or
|
|
2612
|
+
* `off`); without it the run's own setting (`env.spans`, `app` by default) applies.
|
|
2613
|
+
*
|
|
2614
|
+
* @param options - `status: false` leaves off the leading status; `spans` chooses the span trail
|
|
2433
2615
|
*/
|
|
2434
2616
|
readonly lines: (options?: {
|
|
2435
2617
|
readonly status?: boolean | undefined;
|
|
2618
|
+
readonly spans?: "app" | "all" | "off" | undefined;
|
|
2436
2619
|
}) => ReadonlyArray<string>;
|
|
2437
2620
|
}
|
|
2438
2621
|
/**
|
|
@@ -2522,9 +2705,11 @@ interface MainOptions<RP, EP> extends ReportFailuresOptions {
|
|
|
2522
2705
|
* one that writes output must use `Console` or `Stdio`, never `Terminal`.
|
|
2523
2706
|
*
|
|
2524
2707
|
* Stderr's colour mirrors stdout's terminal check unless `env.stderrIsTerminal` says otherwise, so with stderr
|
|
2525
|
-
* redirected and stdout a terminal the failure report is painted into the file. On Node, pass the real check
|
|
2526
|
-
*
|
|
2527
|
-
*
|
|
2708
|
+
* redirected and stdout a terminal the failure report is painted into the file. On Node, pass the real check from
|
|
2709
|
+
* the bin's entry, the one place it reads the host:
|
|
2710
|
+
* `env: { stderrIsTerminal: Effect.sync(() => process.stderr.isTTY === true) }`. Core's `Stdio` reports only
|
|
2711
|
+
* stdout (upstream Effect-TS/effect#8639); once core has a stderr check, this option reads it and the bin passes
|
|
2712
|
+
* nothing.
|
|
2528
2713
|
*/
|
|
2529
2714
|
readonly env?: CliEnvOptions | undefined;
|
|
2530
2715
|
/**
|
|
@@ -2541,8 +2726,12 @@ interface MainOptions<RP, EP> extends ReportFailuresOptions {
|
|
|
2541
2726
|
*
|
|
2542
2727
|
* Two cases keep help on stdout even under `"stderr"`. A `CliOutput`
|
|
2543
2728
|
* Formatter or a `Console` provided inside `program` is not seen by
|
|
2544
|
-
* `main`, so its help is not rerouted
|
|
2545
|
-
* `
|
|
2729
|
+
* `main`, so its help is not rerouted. To change the Formatter, pass
|
|
2730
|
+
* `env.formatter` (for example `formatVersion`): `main` installs its own
|
|
2731
|
+
* Formatter inside the platform, so that is the way in, and a Formatter
|
|
2732
|
+
* provided inside the program is invisible to the routing. A Formatter
|
|
2733
|
+
* the platform provides is shadowed by `main`'s, so pass it through
|
|
2734
|
+
* `env.formatter` as well. And with `Command.runWith`'s `renderErrors: false`
|
|
2546
2735
|
* no errors are printed, so nothing marks the help as a usage error's.
|
|
2547
2736
|
*/
|
|
2548
2737
|
readonly helpOnUsageError?: "stdout" | "stderr" | undefined;
|
|
@@ -3007,8 +3196,9 @@ interface RenderContext {
|
|
|
3007
3196
|
*
|
|
3008
3197
|
* @remarks
|
|
3009
3198
|
* `Infinity` is no limit. A renderer clamps what it is given: zero or a negative width is 1, and `NaN` is 80.
|
|
3010
|
-
* `Render.context` gives a human `TerminalEnv.width()`, which is the terminal's columns as
|
|
3011
|
-
* for a context built for `"stderr"` (core's `Terminal` has one width)
|
|
3199
|
+
* `Render.context` gives a human writing to a terminal `TerminalEnv.width()`, which is the terminal's columns as
|
|
3200
|
+
* stdout reports them, even for a context built for `"stderr"` (core's `Terminal` has one width), and gives no limit
|
|
3201
|
+
* when the stream is not a terminal; pass `width` to override it.
|
|
3012
3202
|
*/
|
|
3013
3203
|
readonly width: number;
|
|
3014
3204
|
/** Who the output is for. */
|
|
@@ -3088,8 +3278,9 @@ interface RenderContextOfOptions {
|
|
|
3088
3278
|
*/
|
|
3089
3279
|
interface RenderContextOptions {
|
|
3090
3280
|
/**
|
|
3091
|
-
* The display columns to lay out at. By default a human gets `TerminalEnv.width()`, the
|
|
3092
|
-
* stdout reports them even when the stream is `"stderr"
|
|
3281
|
+
* The display columns to lay out at. By default a human writing to a terminal gets `TerminalEnv.width()`, the
|
|
3282
|
+
* terminal's columns as stdout reports them even when the stream is `"stderr"`; a human whose stream is not a
|
|
3283
|
+
* terminal (a pipe, a file), an agent and a CI get no limit at all.
|
|
3093
3284
|
*/
|
|
3094
3285
|
readonly width?: number | undefined;
|
|
3095
3286
|
/** Turns an absolute path into its display form, for example relative to the working directory; the identity by default. */
|
|
@@ -3131,8 +3322,9 @@ export declare class Render {
|
|
|
3131
3322
|
* gets an escape and a terminal without OSC 8 gets the label;
|
|
3132
3323
|
* - `neutralizeWorkflowCommands` is set when `CurrentRuntimeEnv` says GitHub Actions (read if present, not
|
|
3133
3324
|
* required), for every audience, since the runner reads whatever is written there;
|
|
3134
|
-
* - `width` is the option, else `TerminalEnv.width()` for a human, and **unbounded**
|
|
3135
|
-
*
|
|
3325
|
+
* - `width` is the option, else `TerminalEnv.width()` for a human whose stream is a terminal, and **unbounded**
|
|
3326
|
+
* (`Infinity`) for a human whose stream is not one (`tool | grep`, `tool > out.txt`), an agent or a CI, so nothing
|
|
3327
|
+
* a reader needs is truncated or wrapped for a terminal that is not there: a pipe has no width to honour.
|
|
3136
3328
|
*
|
|
3137
3329
|
* @param stream - the stream the output is for
|
|
3138
3330
|
* @param options - an explicit width and a path display function
|
|
@@ -3335,5 +3527,5 @@ export declare class SchemaIssueRenderer {
|
|
|
3335
3527
|
static readonly render: (issue: unknown) => ReadonlyArray<string>;
|
|
3336
3528
|
}
|
|
3337
3529
|
//#endregion
|
|
3338
|
-
export type { AnnotationLevel, AnnotationOptions, AudienceFlagInput, Block, BlockOf, CliAudienceFlagsOptions, CliDocSource, CliEnvOptions, CliEnvServices, CliEnvTestOptions, CliEnvTestServices, CliExitShape, CliFailureOptions, CliLinksLinkerOptions, CliLinksOptions, CliLinksShape, CliLogFile, CliLogFileOptions, CliLogOptions, CliLoggerOptions, CliMessageOptions, CliPromptFallbackOptions, CliPromptTarget, CliThemeOptions, CliThemeShape, CliThemeTestOptions, Column, CoreStatusName, Counter, CountsOptions, CountsRow, CountsTableOptions, DocPrintOptions, Document, EditorLinks, FailureDetails, GithubAnnotationProperties, GlyphSelectOptions, GlyphSet, Inline, InlineInput, InlineOf, LinkOptions, LinkTarget, ListOptions, MainOptions, NamedColor, OverflowOptions, PercentOptions, RenderContext, RenderContextOfOptions, RenderContextOptions, ReportFailuresOptions, RequiresAudienceFlags, StatusDef, StatusRef, StreamTheme, Style, TableOptions, TokenName, TreeInput, TreeNode, TruncateOptions };
|
|
3530
|
+
export type { AnnotationLevel, AnnotationOptions, AudienceFlagInput, Block, BlockOf, CliAudienceFlagsOptions, CliDocSource, CliEnvOptions, CliEnvServices, CliEnvTestOptions, CliEnvTestServices, CliExitShape, CliFailureOptions, CliLinksLinkerOptions, CliLinksOptions, CliLinksShape, CliLogFile, CliLogFileOptions, CliLogOptions, CliLogStatusOptions, CliLoggerOptions, CliMessageOptions, CliPromptFallbackOptions, CliPromptTarget, CliThemeOptions, CliThemeShape, CliThemeTestOptions, Column, CoreStatusName, Counter, CountsOptions, CountsRow, CountsTableOptions, DocPrintOptions, Document, EditorLinks, FailureDetails, GithubAnnotationProperties, GlyphSelectOptions, GlyphSet, Inline, InlineInput, InlineOf, LinkOptions, LinkTarget, ListOptions, MainOptions, NamedColor, OverflowOptions, PercentOptions, RenderContext, RenderContextOfOptions, RenderContextOptions, ReportFailuresOptions, RequiresAudienceFlags, StatusDef, StatusRef, StreamTheme, Style, TableOptions, TokenName, TreeInput, TreeNode, TruncateOptions };
|
|
3339
3531
|
//# sourceMappingURL=index.d.ts.map
|
package/internal/counts.js
CHANGED
|
@@ -2,6 +2,21 @@ import { Fmt } from "../Fmt.js";
|
|
|
2
2
|
|
|
3
3
|
//#region src/internal/counts.ts
|
|
4
4
|
/**
|
|
5
|
+
* The label a counter shows for a count: its one label, or `one` when the count is exactly 1 and `other` otherwise.
|
|
6
|
+
* The count is the counter's own `n`, except in a share headline (`1/3 repos`), which reads by the denominator, the
|
|
7
|
+
* total, and passes it.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
const counterLabel = (counter, count = counter.n) => typeof counter.label === "string" ? counter.label : count === 1 ? counter.label.one : counter.label.other;
|
|
12
|
+
/**
|
|
13
|
+
* The label a counter's column is headed with in a `CountsTable`: its one label, or its plural form, since a column
|
|
14
|
+
* holds every row's count.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
const columnLabel = (counter) => typeof counter.label === "string" ? counter.label : counter.label.other;
|
|
19
|
+
/**
|
|
5
20
|
* The total of a `Counts` block: the caller's rule when it has one, otherwise the sum of `n` over every counter.
|
|
6
21
|
*
|
|
7
22
|
* @remarks
|
|
@@ -58,7 +73,7 @@ const countsTableOf = (block) => {
|
|
|
58
73
|
_tag: "Table",
|
|
59
74
|
columns: [
|
|
60
75
|
{ header: block.labelHeader ?? [] },
|
|
61
|
-
...keys.map((key) => ({ header: [text(key
|
|
76
|
+
...keys.map((key) => ({ header: [text(columnLabel(key))] })),
|
|
62
77
|
...timed ? [{ header: block.durationHeader ?? [text("duration")] }] : []
|
|
63
78
|
],
|
|
64
79
|
rows: [...rows, ...total]
|
|
@@ -66,4 +81,4 @@ const countsTableOf = (block) => {
|
|
|
66
81
|
};
|
|
67
82
|
|
|
68
83
|
//#endregion
|
|
69
|
-
export { countsTableOf, totalOf, visibleCountersOf };
|
|
84
|
+
export { columnLabel, counterLabel, countsTableOf, totalOf, visibleCountersOf };
|
|
@@ -5,7 +5,7 @@ import { Glyphs } from "../Glyphs.js";
|
|
|
5
5
|
import { CliTheme } from "../CliTheme.js";
|
|
6
6
|
import { Render } from "../Render.js";
|
|
7
7
|
import { CliFailure } from "../CliFailure.js";
|
|
8
|
-
import { Context, Effect, MutableRef, Option } from "effect";
|
|
8
|
+
import { Config, Context, Effect, MutableRef, Option } from "effect";
|
|
9
9
|
import { Audience, TerminalEnv } from "@effected/env";
|
|
10
10
|
import { CommandNeutralizer } from "@effected/github-commands";
|
|
11
11
|
|
|
@@ -52,16 +52,14 @@ const build = (audience, settings = {}) => Effect.gen(function* () {
|
|
|
52
52
|
if (Option.isNone(theme) || Option.isNone(terminal) || Option.isNone(links)) return void 0;
|
|
53
53
|
const shape = audience ?? (Option.isSome(current) ? current.value : void 0);
|
|
54
54
|
if (shape === void 0) return void 0;
|
|
55
|
-
const { displayPath, stackFrames } = settings;
|
|
55
|
+
const { displayPath, stackFrames, spans, appModule } = settings;
|
|
56
56
|
const ctx = yield* Render.context("stderr", displayPath === void 0 ? void 0 : { displayPath }).pipe(Effect.provideService(CliTheme, theme.value), Effect.provideService(TerminalEnv, terminal.value), Effect.provideService(CliLinks, links.value), Effect.provideService(Audience, shape));
|
|
57
|
-
|
|
58
|
-
return stackFrames === void 0 ? {
|
|
57
|
+
return {
|
|
59
58
|
ctx,
|
|
60
|
-
format
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
stackFrames
|
|
59
|
+
format: yield* autoFormat(ctx.audience),
|
|
60
|
+
...stackFrames === void 0 ? {} : { stackFrames },
|
|
61
|
+
...spans === void 0 ? {} : { spans },
|
|
62
|
+
...appModule === void 0 ? {} : { appModule }
|
|
65
63
|
};
|
|
66
64
|
});
|
|
67
65
|
/**
|
|
@@ -75,7 +73,9 @@ const refreshFailureTarget = (audience, settings) => Effect.gen(function* () {
|
|
|
75
73
|
const recorded = MutableRef.get(cell);
|
|
76
74
|
const target = yield* build(audience, settings ?? {
|
|
77
75
|
displayPath: recorded?.ctx.displayPath,
|
|
78
|
-
stackFrames: recorded?.stackFrames
|
|
76
|
+
stackFrames: recorded?.stackFrames,
|
|
77
|
+
spans: recorded?.spans,
|
|
78
|
+
appModule: recorded?.appModule
|
|
79
79
|
});
|
|
80
80
|
if (target !== void 0) MutableRef.set(cell, target);
|
|
81
81
|
});
|
|
@@ -119,10 +119,12 @@ const withoutStatus = (doc) => doc.map((block) => {
|
|
|
119
119
|
*
|
|
120
120
|
* @internal
|
|
121
121
|
*/
|
|
122
|
-
const linesOf = (cause, target, status = true) => {
|
|
122
|
+
const linesOf = (cause, target, status = true, spans = target.spans) => {
|
|
123
123
|
const full = CliFailure.toDoc(cause, {
|
|
124
124
|
displayPath: target.ctx.displayPath,
|
|
125
|
-
...target.stackFrames === void 0 ? {} : { stackFrames: target.stackFrames }
|
|
125
|
+
...target.stackFrames === void 0 ? {} : { stackFrames: target.stackFrames },
|
|
126
|
+
...spans === void 0 ? {} : { spans },
|
|
127
|
+
...target.appModule === void 0 ? {} : { appModule: target.appModule }
|
|
126
128
|
});
|
|
127
129
|
const doc = status ? full : withoutStatus(full);
|
|
128
130
|
const text = Render[target.format](doc, target.ctx);
|
|
@@ -150,7 +152,44 @@ const guardConsumerLines = (lines) => Effect.map(currentTarget, (target) => {
|
|
|
150
152
|
*
|
|
151
153
|
* @internal
|
|
152
154
|
*/
|
|
153
|
-
const plainFailureLines = (cause, status = true) => linesOf(cause, fallbackTarget, status);
|
|
155
|
+
const plainFailureLines = (cause, status = true, spans) => linesOf(cause, fallbackTarget, status, spans);
|
|
156
|
+
const SPAN_SETTINGS = [
|
|
157
|
+
"app",
|
|
158
|
+
"all",
|
|
159
|
+
"off"
|
|
160
|
+
];
|
|
161
|
+
/**
|
|
162
|
+
* The span trail setting, as `CliLog`'s level is read: the explicit `spans` when given (the variable is then not read
|
|
163
|
+
* at all), else the variable named `envVar` through `Config`, case-insensitive, unset or empty meaning the default.
|
|
164
|
+
* A value that is not a setting is ignored, with the warning to log.
|
|
165
|
+
*
|
|
166
|
+
* @internal
|
|
167
|
+
*/
|
|
168
|
+
const readSpans = (explicit, envVar) => Effect.gen(function* () {
|
|
169
|
+
if (explicit !== void 0) return {
|
|
170
|
+
spans: explicit,
|
|
171
|
+
invalid: void 0
|
|
172
|
+
};
|
|
173
|
+
if (envVar === void 0) return {
|
|
174
|
+
spans: void 0,
|
|
175
|
+
invalid: void 0
|
|
176
|
+
};
|
|
177
|
+
const raw = yield* Config.option(Config.String(envVar)).pipe(Effect.orElseSucceed(() => Option.none()));
|
|
178
|
+
if (Option.isNone(raw) || raw.value === "") return {
|
|
179
|
+
spans: void 0,
|
|
180
|
+
invalid: void 0
|
|
181
|
+
};
|
|
182
|
+
const value = raw.value.toLowerCase();
|
|
183
|
+
const spans = SPAN_SETTINGS.find((setting) => setting === value);
|
|
184
|
+
if (spans !== void 0) return {
|
|
185
|
+
spans,
|
|
186
|
+
invalid: void 0
|
|
187
|
+
};
|
|
188
|
+
return {
|
|
189
|
+
spans: void 0,
|
|
190
|
+
invalid: `${envVar}=${raw.value} is not a span setting (${SPAN_SETTINGS.join("|")}); ignoring it`
|
|
191
|
+
};
|
|
192
|
+
});
|
|
154
193
|
|
|
155
194
|
//#endregion
|
|
156
|
-
export { FailureTargetCell, currentTarget, fallbackTarget, guardConsumerLines, linesOf, plainFailureLines, refreshFailureTarget };
|
|
195
|
+
export { FailureTargetCell, currentTarget, fallbackTarget, guardConsumerLines, linesOf, plainFailureLines, readSpans, refreshFailureTarget };
|
package/internal/renderDoc.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fmt, sanitize } from "../Fmt.js";
|
|
2
|
-
import { countsTableOf, totalOf, visibleCountersOf } from "./counts.js";
|
|
2
|
+
import { counterLabel, countsTableOf, totalOf, visibleCountersOf } from "./counts.js";
|
|
3
3
|
import { truncateSpans, widthOf, wrapSpans } from "./layout.js";
|
|
4
4
|
|
|
5
5
|
//#region src/internal/renderDoc.ts
|
|
@@ -190,10 +190,10 @@ const countsLayout = (walk, block) => {
|
|
|
190
190
|
const qualifier = block.qualifier === void 0 ? [] : toned(oneLine(inline(walk, block.qualifier)), "muted");
|
|
191
191
|
const duration = block.durationMs === void 0 ? "" : Fmt.duration(block.durationMs);
|
|
192
192
|
const suffix = block.suffix === void 0 ? [] : toned(oneLine(inline(walk, block.suffix)), "muted");
|
|
193
|
-
const nameOf = (counter) => sanitize(counter
|
|
193
|
+
const nameOf = (counter, count) => sanitize(counterLabel(counter, count)).replace(/\r\n|\r|\n/g, " ");
|
|
194
194
|
const counters = visible.map((counter, index) => {
|
|
195
|
-
const name = nameOf(counter);
|
|
196
195
|
const share = index === 0 && block.share !== false;
|
|
196
|
+
const name = nameOf(counter, share ? total : counter.n);
|
|
197
197
|
return [span(share ? `${counter.n}/${total} ${name}` : `${counter.n} ${name}`, counter.status.def.token)];
|
|
198
198
|
});
|
|
199
199
|
if (block.layout === "row") return [trimLine(joinChunks([
|
|
@@ -281,6 +281,7 @@ const blockLines = (walk, block, width, compact = false) => {
|
|
|
281
281
|
case "Line": {
|
|
282
282
|
const spans = oneLine(inline(walk, block.content));
|
|
283
283
|
if (block.truncate === true) return [trimLine(truncateSpans(spans, width, walk.ctx.glyphs.ellipsis))];
|
|
284
|
+
if (block.wrap === false) return [trimLine(spans)];
|
|
284
285
|
return spans.length === 0 ? [[]] : wrapSpans(spans, width, { hardBreak: false }).map(trimLine);
|
|
285
286
|
}
|
|
286
287
|
case "DiffText": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fmt, sanitize } from "../Fmt.js";
|
|
2
|
-
import { countsTableOf, totalOf, visibleCountersOf } from "./counts.js";
|
|
2
|
+
import { counterLabel, countsTableOf, totalOf, visibleCountersOf } from "./counts.js";
|
|
3
3
|
import { isAllowedLinkUrl } from "./linkScheme.js";
|
|
4
4
|
import { DRIVE, encodePath, fileUrlPath } from "./linkTarget.js";
|
|
5
5
|
import { flatten } from "./layout.js";
|
|
@@ -52,7 +52,7 @@ const codeSpan = (text, mode) => {
|
|
|
52
52
|
const longest = Math.max(0, ...[...flat.matchAll(/`+/g)].map((run) => run[0].length));
|
|
53
53
|
const fence = "`".repeat(longest + 1);
|
|
54
54
|
const padded = /^`|`$/.test(flat) || /^ .* $/.test(flat) && flat.trim() !== "" || flat === "";
|
|
55
|
-
const body = mode === "cell" ? flat.
|
|
55
|
+
const body = mode === "cell" ? flat.split("|").join("\\|") : flat;
|
|
56
56
|
const pad = padded ? " " : "";
|
|
57
57
|
return `${fence}${pad}${body === "" ? " " : body}${pad}${fence}`;
|
|
58
58
|
};
|
|
@@ -194,9 +194,10 @@ const countsMd = (walk, block) => {
|
|
|
194
194
|
const qualifier = block.qualifier === void 0 ? "" : inlineMd(block.qualifier, ctx, "line").trim();
|
|
195
195
|
const duration = block.durationMs === void 0 ? "" : Fmt.duration(block.durationMs);
|
|
196
196
|
const suffix = block.suffix === void 0 ? "" : inlineMd(block.suffix, ctx, "line").trim();
|
|
197
|
-
const name = (counter) => escapeText(sanitize(counter
|
|
197
|
+
const name = (counter, count) => escapeText(sanitize(counterLabel(counter, count)).replace(/\r\n|\r|\n/g, " "));
|
|
198
|
+
const headlineName = (counter, index) => index === 0 && block.share !== false ? name(counter, total) : name(counter);
|
|
198
199
|
if (block.layout === "row") {
|
|
199
|
-
const header = [...visible.map(
|
|
200
|
+
const header = [...visible.map(headlineName), ...duration === "" ? [] : ["duration"]];
|
|
200
201
|
const cells = [...visible.map((counter, index) => index === 0 && block.share !== false ? `${counter.n}/${total}` : String(counter.n)), ...duration === "" ? [] : [duration]];
|
|
201
202
|
const after = [qualifier, suffix].filter((part) => part !== "").join(" ");
|
|
202
203
|
return [
|
|
@@ -212,7 +213,7 @@ const countsMd = (walk, block) => {
|
|
|
212
213
|
...duration === "" ? [] : [[duration]],
|
|
213
214
|
...suffix === "" ? [] : [flowLines(suffix)]
|
|
214
215
|
];
|
|
215
|
-
const tally = visible.map((counter, index) => index === 0 && block.share !== false ? `${counter.n}/${total} ${
|
|
216
|
+
const tally = visible.map((counter, index) => index === 0 && block.share !== false ? `${counter.n}/${total} ${headlineName(counter, index)}` : `${counter.n} ${name(counter)}`).join(", ");
|
|
216
217
|
const line = [
|
|
217
218
|
[label === "" ? "" : `${label}:`, tally].filter((part) => part !== "").join(" "),
|
|
218
219
|
qualifier,
|