omakit 0.4.0 → 0.4.1
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/README.md +4 -0
- package/package.json +1 -1
- package/tools/marketplace/README.md +2 -1
- package/tools/marketplace/ask.mjs +4 -4
- package/tools/marketplace/banner.mjs +10 -1
- package/tools/marketplace/cli.mjs +24 -13
- package/tools/marketplace/report.mjs +7 -3
- package/tools/marketplace/style.mjs +24 -8
- package/tools/marketplace/usage.mjs +56 -26
- package/tools/weigh/confirm.mjs +3 -3
package/README.md
CHANGED
|
@@ -40,6 +40,10 @@ and shows the upgrade command. It installs nothing automatically; scripts
|
|
|
40
40
|
and JSON stay quiet. `DISABLE_UPDATE_NOTIFIER=1` disables the notice, and
|
|
41
41
|
`omakit doctor` checks explicitly. [Update behaviour](docs/INSTALL.md#updating).
|
|
42
42
|
|
|
43
|
+
Terminal help and reports adapt to the available width, up to 120 columns
|
|
44
|
+
for readability. Narrow windows wrap sooner; pipes and text files keep the
|
|
45
|
+
stable eighty-column layout. JSON and exact issue bodies remain intact.
|
|
46
|
+
|
|
43
47
|
### `submit`
|
|
44
48
|
|
|
45
49
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omakit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "The safe place to find out: everything knowable about an Omarchy Quattro plugin submission before you post it, on your own machine. Agent-first, read-only against the marketplace, posts nothing, zero dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Maarten Tolhuijs",
|
|
@@ -81,7 +81,8 @@ side rather than the findings themselves. The GitHub side uses whatever
|
|
|
81
81
|
credential `github.mjs` resolves (a `gh` login, and only that), read-only.
|
|
82
82
|
|
|
83
83
|
Two rules about the output, stated as rules because each is an exception to
|
|
84
|
-
a wider one.
|
|
84
|
+
a wider one. Pipes use 80 columns; terminals use their available width up to
|
|
85
|
+
120 columns. Text files compose at 80 columns without colour escapes. Text
|
|
85
86
|
that will be posted verbatim, the marketplace's own baseline report and the
|
|
86
87
|
issue body rendered from the pinned form, is never wrapped and may exceed 80,
|
|
87
88
|
because a wrapped body would not be the body. And stdout is the whole result;
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// the report ends with the command line that repeats the run without asking.
|
|
16
16
|
|
|
17
17
|
import { createInterface } from "node:readline"
|
|
18
|
-
import { colourEnabled, STEP, action, styler, wrap } from "./style.mjs"
|
|
18
|
+
import { colourEnabled, outputColumns, STEP, action, styler, wrap } from "./style.mjs"
|
|
19
19
|
import { resolveCategory, resolveTags } from "./form.mjs"
|
|
20
20
|
import { watchIssueTitle } from "./watch.mjs"
|
|
21
21
|
|
|
@@ -52,10 +52,10 @@ function reader(input) {
|
|
|
52
52
|
/** Ask one question until an answer resolves; `parse` returns { ok, value } or { ok: false, reason }. */
|
|
53
53
|
async function question(lines, output, c, { name, heading, options, defaultIndexes, parse, wrapOptions = false, eofRemedy }) {
|
|
54
54
|
const step = " ".repeat(STEP)
|
|
55
|
-
output.write(`${wrap(heading, {}, c).join("\n")}\n`)
|
|
55
|
+
output.write(`${wrap(heading, { width: outputColumns(output) }, c).join("\n")}\n`)
|
|
56
56
|
for (const [index, option] of options.entries()) {
|
|
57
57
|
output.write(wrapOptions
|
|
58
|
-
? `${wrap(`${String(index + 1).padStart(2)} ${option}`, { indent: STEP }, c).join("\n")}\n`
|
|
58
|
+
? `${wrap(`${String(index + 1).padStart(2)} ${option}`, { indent: STEP, width: outputColumns(output) }, c).join("\n")}\n`
|
|
59
59
|
: `${step}${c("typeable", String(index + 1).padStart(2))} ${option}\n`)
|
|
60
60
|
}
|
|
61
61
|
const fallback = defaultIndexes.length ? defaultIndexes.map((index) => index + 1).join(",") : null
|
|
@@ -70,7 +70,7 @@ async function question(lines, output, c, { name, heading, options, defaultIndex
|
|
|
70
70
|
const text = raw.trim() || (fallback ?? "")
|
|
71
71
|
const parsed = parse(text)
|
|
72
72
|
if (parsed.ok) return parsed.value
|
|
73
|
-
output.write(`${wrap(parsed.reason, {}, c).join("\n")}\n`)
|
|
73
|
+
output.write(`${wrap(parsed.reason, { width: outputColumns(output) }, c).join("\n")}\n`)
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
// letters come from the small font below, so renaming the tool is a change to
|
|
50
50
|
// one string and not a redrawing job.
|
|
51
51
|
|
|
52
|
-
import { code, colourEnabled, DENSITY, motionEnabled, MOTION, rule as floorRule } from "./style.mjs"
|
|
52
|
+
import { code, colourEnabled, DENSITY, motionEnabled, MOTION, outputColumns, rule as floorRule, wrap } from "./style.mjs"
|
|
53
53
|
import { effectAvailable, playEffect } from "./effect.mjs"
|
|
54
54
|
|
|
55
55
|
const ESC = "\u001b["
|
|
@@ -227,6 +227,15 @@ export async function banner(options = {}) {
|
|
|
227
227
|
// and a piped run should differ from a watched one only in decoration.
|
|
228
228
|
if (!enabled) return
|
|
229
229
|
|
|
230
|
+
// Never animate artwork across physical terminal rows: cursor-up would
|
|
231
|
+
// redraw the wrong row. A narrow terminal gets a compact wordmark instead.
|
|
232
|
+
if (stream.isTTY && Number.isFinite(stream.columns) && stream.columns > 0 && stream.columns < width) {
|
|
233
|
+
stream.write(`${c(code("name"), word.toUpperCase())}\n`)
|
|
234
|
+
if (options.tagline) stream.write(`${wrap(options.tagline, { width: outputColumns(stream) }, (name, text) => c(code(name), text)).join("\n")}\n`)
|
|
235
|
+
stream.write("\n")
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
|
|
230
239
|
// A five-row animation redrawn with cursor-up needs five rows that stay put.
|
|
231
240
|
// In a terminal with no room the screen scrolls under the animation, the
|
|
232
241
|
// cursor-up lands a line off, and a row from an earlier frame is left stranded
|
|
@@ -34,7 +34,7 @@ import { updateCheckEnabled, updateNotice } from "./update-check.mjs"
|
|
|
34
34
|
import { progress } from "./progress.mjs"
|
|
35
35
|
import { banner, bannerEnabled } from "./banner.mjs"
|
|
36
36
|
import { COMMANDS, renderSummary, renderUsage, TAGLINE } from "./usage.mjs"
|
|
37
|
-
import { action, AUDIT_VERDICTS, colourEnabled, GUTTER, labelled, mark, styler, verdict, wrap } from "./style.mjs"
|
|
37
|
+
import { action, AUDIT_VERDICTS, colourEnabled, GUTTER, labelled, mark, outputColumns, styler, verdict, withOutputStream, wrap } from "./style.mjs"
|
|
38
38
|
import { omakitCacheDir, withHomeAbbreviated } from "./paths.mjs"
|
|
39
39
|
import { DEFAULTS as WEIGH_DEFAULTS, measureWeigh, planWeigh } from "../weigh/audit.mjs"
|
|
40
40
|
import { confirmationQuestion, renderList, renderWeigh, renderPlan } from "../weigh/report.mjs"
|
|
@@ -76,8 +76,10 @@ const REMEDY = Object.freeze({
|
|
|
76
76
|
*/
|
|
77
77
|
function fail(code, message, exit = 1, remedy = REMEDY[code], body = () => []) {
|
|
78
78
|
const c = styler(colourEnabled(process.stderr))
|
|
79
|
-
const lines =
|
|
80
|
-
|
|
79
|
+
const lines = withOutputStream(process.stderr, () => [
|
|
80
|
+
`${mark("fail", c)}${c("name", code)}`, ...wrap(message, { indent: GUTTER }, c), ...body(c),
|
|
81
|
+
...(remedy ? action(remedy, c) : []),
|
|
82
|
+
])
|
|
81
83
|
process.stderr.write(`${lines.join("\n")}\n`)
|
|
82
84
|
process.exit(exit)
|
|
83
85
|
}
|
|
@@ -123,6 +125,12 @@ function emit(args, text) {
|
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
127
|
|
|
128
|
+
function reportText(args, render, result, options = {}) {
|
|
129
|
+
const out = option(args, "--out")
|
|
130
|
+
return withOutputStream(out ? { isTTY: false } : process.stdout,
|
|
131
|
+
() => render(result, { ...options, ...(out ? { colour: false } : {}) }))
|
|
132
|
+
}
|
|
133
|
+
|
|
126
134
|
async function cmdSubmit(args) {
|
|
127
135
|
const target = positionals(args)[0]
|
|
128
136
|
if (!target) fail("usage", "submit needs a target: `omakit submit <target> --category <c> --tags <a,b>`", 2)
|
|
@@ -174,7 +182,7 @@ async function cmdSubmit(args) {
|
|
|
174
182
|
failFrom(error)
|
|
175
183
|
}
|
|
176
184
|
spinner.done()
|
|
177
|
-
emit(args, args.includes("--json") ? `${JSON.stringify(result, null, 2)}\n` : renderSubmit
|
|
185
|
+
emit(args, args.includes("--json") ? `${JSON.stringify(result, null, 2)}\n` : reportText(args, renderSubmit, result))
|
|
178
186
|
// Three outcomes, two exit codes: `ready` and `listed` are both healthy
|
|
179
187
|
// states, and only a refusal is a 1.
|
|
180
188
|
process.exit(result.outcome === "refused" ? 1 : 0)
|
|
@@ -215,7 +223,7 @@ async function cmdWatch(args) {
|
|
|
215
223
|
}
|
|
216
224
|
spinner.done()
|
|
217
225
|
const render = result.mode === "list" ? renderWatchList : result.mode === "all" ? renderWatchAll : renderWatch
|
|
218
|
-
emit(args, args.includes("--json") ? `${JSON.stringify(result, null, 2)}\n` : render
|
|
226
|
+
emit(args, args.includes("--json") ? `${JSON.stringify(result, null, 2)}\n` : reportText(args, render, result))
|
|
219
227
|
process.exit(result.verdict?.state === "unknown" || result.summary?.unknown > 0 ? 2 : 0)
|
|
220
228
|
}
|
|
221
229
|
|
|
@@ -250,7 +258,7 @@ async function cmdDoctor(args) {
|
|
|
250
258
|
const spinner = spinnerFor(args)
|
|
251
259
|
const result = await doctor({ repoRoot: ROOT, offline: args.includes("--offline"), onPhase: spinner.phase })
|
|
252
260
|
spinner.done()
|
|
253
|
-
emit(args, args.includes("--json") ? `${JSON.stringify(result, null, 2)}\n` : renderDoctor
|
|
261
|
+
emit(args, args.includes("--json") ? `${JSON.stringify(result, null, 2)}\n` : reportText(args, renderDoctor, result))
|
|
254
262
|
process.exit(result.problems ? 1 : 0)
|
|
255
263
|
}
|
|
256
264
|
|
|
@@ -293,7 +301,7 @@ async function cmdVerify(args) {
|
|
|
293
301
|
const blockingRules = section.invoked && section.official && !section.official.error
|
|
294
302
|
? (await consequence(requirePin(ROOT).dir, section.official)).selectivelyBlockingRules
|
|
295
303
|
: []
|
|
296
|
-
emit(args, renderVerify
|
|
304
|
+
emit(args, reportText(args, renderVerify, document, { blockingRules }))
|
|
297
305
|
}
|
|
298
306
|
|
|
299
307
|
async function cmdParity(args) {
|
|
@@ -420,7 +428,7 @@ async function cmdWeigh(args) {
|
|
|
420
428
|
// The confirmation. The plan is printed either way, so the record says
|
|
421
429
|
// what was agreed to; the question is asked only at a terminal on both
|
|
422
430
|
// ends, and --yes is the only other way past it.
|
|
423
|
-
narrate.write(`${renderPlan(plan, { colour: colourEnabled(narrate) }).join("\n")}\n`)
|
|
431
|
+
narrate.write(`${withOutputStream(narrate, () => renderPlan(plan, { colour: colourEnabled(narrate) })).join("\n")}\n`)
|
|
424
432
|
if (!parsed.options.has("--yes")) {
|
|
425
433
|
const interactive = !json && Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY)
|
|
426
434
|
if (!interactive) notWeighed("not-confirmed", `this restarts the shell ${plan.restarts} times and edits shell.json for the duration; a pipe, an agent or --json cannot answer for the person whose shell it is.`, REMEDY["not-confirmed"], 2)
|
|
@@ -487,12 +495,15 @@ const [command, ...rest] = process.argv.slice(2)
|
|
|
487
495
|
// stays empty on success, and never for setup, which is the fix.
|
|
488
496
|
if (updateCheckEnabled({ command, args: rest, stdinTTY: process.stdin.isTTY, stdoutTTY: process.stdout.isTTY, stderrTTY: process.stderr.isTTY })) {
|
|
489
497
|
const notice = await updateNotice({ repoRoot: ROOT, version: VERSION })
|
|
490
|
-
if (notice) process.stderr.write(`${wrap(notice, {}, styler(colourEnabled(process.stderr))).join("\n")}\n`)
|
|
498
|
+
if (notice) process.stderr.write(`${wrap(notice, { width: outputColumns(process.stderr) }, styler(colourEnabled(process.stderr))).join("\n")}\n`)
|
|
491
499
|
}
|
|
492
500
|
|
|
493
501
|
if (command !== "setup" && process.stderr.isTTY) {
|
|
494
502
|
const notice = staleCompletionNotice({ version: VERSION })
|
|
495
|
-
if (notice)
|
|
503
|
+
if (notice) {
|
|
504
|
+
const c = styler(colourEnabled(process.stderr))
|
|
505
|
+
process.stderr.write(`${wrap(notice, { width: outputColumns(process.stderr) }).map((line) => c("label", line)).join("\n")}\n`)
|
|
506
|
+
}
|
|
496
507
|
}
|
|
497
508
|
|
|
498
509
|
if (command === "setup") {
|
|
@@ -556,12 +567,12 @@ if (command === "setup") {
|
|
|
556
567
|
// The short list on a typo, not 53 lines of reference, in the same register
|
|
557
568
|
// as every other failure: what happened, what it means, what to run.
|
|
558
569
|
const c = styler(colourEnabled(process.stderr))
|
|
559
|
-
process.stderr.write([
|
|
570
|
+
process.stderr.write(withOutputStream(process.stderr, () => [
|
|
560
571
|
`${mark("fail", c)}${c("name", "unknown command")}`,
|
|
561
572
|
...wrap(`\`${command}\` is not something omakit does. The commands it has are listed below.`, { indent: GUTTER }, c),
|
|
562
573
|
...action("omakit help", c),
|
|
563
574
|
"",
|
|
564
|
-
renderSummary({ colour: colourEnabled(process.stderr), heading: false }),
|
|
565
|
-
].join("\n"))
|
|
575
|
+
renderSummary({ stream: process.stderr, colour: colourEnabled(process.stderr), heading: false }),
|
|
576
|
+
].join("\n")))
|
|
566
577
|
process.exit(2)
|
|
567
578
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// screen, and the last screen is the one a person is looking at.
|
|
19
19
|
|
|
20
20
|
import {
|
|
21
|
-
action, colourEnabled,
|
|
21
|
+
action, colourEnabled, outputColumns, continuation, field, GUTTER, labelled, mark, section, STEP, styler, verdict, width, wrap,
|
|
22
22
|
} from "./style.mjs"
|
|
23
23
|
import { withHomeAbbreviated } from "./paths.mjs"
|
|
24
24
|
import { watchIssueTitle } from "./watch.mjs"
|
|
@@ -42,7 +42,11 @@ function stateOf(check) {
|
|
|
42
42
|
export function head(state, id, source, c) {
|
|
43
43
|
const left = `${mark(state, c)}${c("name", id)}`
|
|
44
44
|
const tag = c("punctuation", `[${source}]`)
|
|
45
|
-
const
|
|
45
|
+
const columns = outputColumns()
|
|
46
|
+
if (width(left) + width(tag) + 2 > columns) {
|
|
47
|
+
return `${left}\n${wrap(`[${source}]`, { indent: GUTTER }).map((line) => c("punctuation", line)).join("\n")}`
|
|
48
|
+
}
|
|
49
|
+
const gap = columns - width(left) - width(tag)
|
|
46
50
|
return `${left}${" ".repeat(gap)}${tag}`
|
|
47
51
|
}
|
|
48
52
|
|
|
@@ -78,7 +82,7 @@ function commandLines(command, c) {
|
|
|
78
82
|
units.push(word)
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
|
-
const room =
|
|
85
|
+
const room = outputColumns() - STEP - " \\".length
|
|
82
86
|
const lines = []
|
|
83
87
|
let line = ""
|
|
84
88
|
for (const unit of units) {
|
|
@@ -115,9 +115,8 @@ export function code(role) {
|
|
|
115
115
|
// --- geometry ---------------------------------------------------------------
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
* The width
|
|
119
|
-
*
|
|
120
|
-
* and tests/unit/style.test.mjs renders every report and measures. The one
|
|
118
|
+
* The stable width for pipes and files. Terminals use their current column
|
|
119
|
+
* count, up to MAX_COLUMNS so wide windows still have readable prose. The one
|
|
121
120
|
* thing exempt is the marketplace's own baseline report, which is printed
|
|
122
121
|
* verbatim because rewrapping somebody else's attestation would be editing it.
|
|
123
122
|
*
|
|
@@ -125,6 +124,23 @@ export function code(role) {
|
|
|
125
124
|
* `overflows` below is the measurement, and it is the one both test files use.
|
|
126
125
|
*/
|
|
127
126
|
export const COLUMNS = 80
|
|
127
|
+
export const MAX_COLUMNS = 120
|
|
128
|
+
|
|
129
|
+
let compositionStream = null
|
|
130
|
+
|
|
131
|
+
/** Resolve at rendering time, so a resized terminal's next output uses its new width. */
|
|
132
|
+
export function outputColumns(stream = compositionStream || process.stdout) {
|
|
133
|
+
return stream?.isTTY && Number.isFinite(stream.columns) && stream.columns >= 1
|
|
134
|
+
? Math.min(MAX_COLUMNS, Math.floor(stream.columns))
|
|
135
|
+
: COLUMNS
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Scope synchronous composition to its destination, including files and stderr. */
|
|
139
|
+
export function withOutputStream(stream, render) {
|
|
140
|
+
const previous = compositionStream
|
|
141
|
+
compositionStream = stream
|
|
142
|
+
try { return render() } finally { compositionStream = previous }
|
|
143
|
+
}
|
|
128
144
|
|
|
129
145
|
/**
|
|
130
146
|
* The indent scale, in columns. Three stops, and every line in the tool starts
|
|
@@ -327,7 +343,7 @@ export function overflows(line, total = COLUMNS) {
|
|
|
327
343
|
// --- composition ------------------------------------------------------------
|
|
328
344
|
|
|
329
345
|
/**
|
|
330
|
-
* Wrap prose to the
|
|
346
|
+
* Wrap prose to the destination width, and paint it.
|
|
331
347
|
*
|
|
332
348
|
* `indent` is the column the text starts in and is part of the width, which is
|
|
333
349
|
* the bug the earlier version had: it wrapped at 78 and then indented by 7, and
|
|
@@ -346,7 +362,7 @@ export function overflows(line, total = COLUMNS) {
|
|
|
346
362
|
* @param {(name: string, text: string) => string} [c]
|
|
347
363
|
* @returns {string[]} lines, indented and painted
|
|
348
364
|
*/
|
|
349
|
-
export function wrap(text, { indent = 0, width: total =
|
|
365
|
+
export function wrap(text, { indent = 0, width: total = outputColumns(), first = indent } = {}, c = styler(false)) {
|
|
350
366
|
// A word is a run of non-spaces, or a backticked span with whatever
|
|
351
367
|
// punctuation clings to it: "(`omakit pin`)." is one word.
|
|
352
368
|
const words = String(text).match(/[^\s`]*`[^`]*`[^\s`]*|\S+/g) || []
|
|
@@ -422,7 +438,7 @@ export function continuation(value, c) {
|
|
|
422
438
|
* under itself. There is exactly one of these under any failure, and it is the
|
|
423
439
|
* only line in the tool that starts with an arrow, so it can be found by shape.
|
|
424
440
|
*/
|
|
425
|
-
export function action(text, c, { indent = GUTTER, width: total =
|
|
441
|
+
export function action(text, c, { indent = GUTTER, width: total = outputColumns() } = {}) {
|
|
426
442
|
// Painted after wrapping, and all of it cyan: the whole line is the thing
|
|
427
443
|
// to do, so a backticked word inside it has nothing to stand out from.
|
|
428
444
|
const lines = wrap(text, { indent: indent + 2, width: total })
|
|
@@ -449,11 +465,11 @@ export function labelled(label, text, c, { indent = GUTTER } = {}) {
|
|
|
449
465
|
* grey. It is the same shape the wordmark uses (the name, then its rule), so a
|
|
450
466
|
* section of a report and the front door of the tool are drawn by one idea.
|
|
451
467
|
*/
|
|
452
|
-
export function section(title, c, { width: total =
|
|
468
|
+
export function section(title, c, { width: total = outputColumns() } = {}) {
|
|
453
469
|
return [c("heading", title), c("punctuation", DENSITY.floor.repeat(total))]
|
|
454
470
|
}
|
|
455
471
|
|
|
456
472
|
/** A rule with no heading, the same floor, for a wordmark or a block that names itself. */
|
|
457
|
-
export function rule(c, { width: total =
|
|
473
|
+
export function rule(c, { width: total = outputColumns(), tint = "punctuation" } = {}) {
|
|
458
474
|
return c(tint, DENSITY.floor.repeat(total))
|
|
459
475
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { UNAUTHENTICATED_LIMIT } from "./github.mjs"
|
|
9
9
|
import { MARKETPLACE_PIN } from "./pin.mjs"
|
|
10
|
-
import { colourEnabled, paintProse, STEP, styler } from "./style.mjs"
|
|
10
|
+
import { colourEnabled, paintProse, STEP, styler, withOutputStream, wrap } from "./style.mjs"
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* "Safe" means one thing, everywhere it appears: this runs on your own
|
|
@@ -208,16 +208,40 @@ export function paintSignature(signature, c) {
|
|
|
208
208
|
*
|
|
209
209
|
* @param {{ colour?: boolean, heading?: boolean }} [options]
|
|
210
210
|
*/
|
|
211
|
-
|
|
212
|
-
const
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
211
|
+
function commandSignatures(command) {
|
|
212
|
+
const signatures = []
|
|
213
|
+
for (const line of [].concat(command.signature)) {
|
|
214
|
+
if (line.startsWith("omakit ")) signatures.push(line)
|
|
215
|
+
else signatures[signatures.length - 1] += ` ${line}`
|
|
216
216
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
217
|
+
return signatures
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function signatureLines(signature, c) {
|
|
221
|
+
// A placeholder or bracketed optional argument stays whole, like the
|
|
222
|
+
// backticked typeable spans used by the shared wrapper.
|
|
223
|
+
const grouped = signature.replace(/\[[^\]]+\]|<[^>]+>(?:@<[^>]+>)?/g, (part) => `\`${part}\``)
|
|
224
|
+
return wrap(grouped, { indent: STEP * 3, first: STEP }).map((line) => paintSignature(line, c))
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export function renderSummary({ stream = process.stdout, colour = colourEnabled(stream), heading = true } = {}) {
|
|
228
|
+
return withOutputStream(stream, () => {
|
|
229
|
+
const c = styler(colour)
|
|
230
|
+
const out = heading ? [`${c("typeable.bold", "omakit")}${c("punctuation", ":")} ${TAGLINE}`, ""] : []
|
|
231
|
+
if (stream.isTTY && heading) out.splice(0, 1, ...wrap(out[0]))
|
|
232
|
+
for (const command of COMMANDS) {
|
|
233
|
+
if (stream.isTTY) out.push(...signatureLines([].concat(command.signature)[0], c))
|
|
234
|
+
else out.push(`${INDENT}${paintSignature([].concat(command.signature)[0], c)}`)
|
|
235
|
+
}
|
|
236
|
+
out.push("")
|
|
237
|
+
if (stream.isTTY) {
|
|
238
|
+
out.push(...wrap("`omakit help` is the same list with what each command does, and what it reads. `omakit setup` is the one to run first.", { indent: STEP }, c))
|
|
239
|
+
} else {
|
|
240
|
+
out.push(`${INDENT}${paintProse("`omakit help` is the same list with what each command does, and", c)}`)
|
|
241
|
+
out.push(`${INDENT}${paintProse("what it reads. `omakit setup` is the one to run first.", c)}`)
|
|
242
|
+
}
|
|
243
|
+
return `${out.join("\n")}\n`
|
|
244
|
+
})
|
|
221
245
|
}
|
|
222
246
|
|
|
223
247
|
/**
|
|
@@ -225,23 +249,29 @@ export function renderSummary({ colour = colourEnabled(), heading = true } = {})
|
|
|
225
249
|
* when the line above has already named the tool, as under an unknown
|
|
226
250
|
* command, so the same sentence is not printed twice.
|
|
227
251
|
*/
|
|
228
|
-
export function renderUsage({ colour = colourEnabled(), heading = true } = {}) {
|
|
229
|
-
|
|
230
|
-
|
|
252
|
+
export function renderUsage({ stream = process.stdout, colour = colourEnabled(stream), heading = true } = {}) {
|
|
253
|
+
return withOutputStream(stream, () => {
|
|
254
|
+
const c = styler(colour)
|
|
255
|
+
const out = heading ? [`${c("typeable.bold", "omakit")}${c("punctuation", ":")} ${TAGLINE}`, ""] : []
|
|
256
|
+
if (stream.isTTY && heading) out.splice(0, 1, ...wrap(out[0]))
|
|
231
257
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
258
|
+
for (const command of COMMANDS) {
|
|
259
|
+
if (stream.isTTY) {
|
|
260
|
+
for (const signature of commandSignatures(command)) out.push(...signatureLines(signature, c))
|
|
261
|
+
out.push(...wrap(command.lines.join(" "), { indent: STEP * 3 }, c))
|
|
262
|
+
} else {
|
|
263
|
+
for (const line of [].concat(command.signature)) out.push(`${INDENT}${paintSignature(line, c)}`)
|
|
264
|
+
for (const line of command.lines) out.push(`${DESCRIPTION}${paintProse(line, c)}`)
|
|
265
|
+
}
|
|
266
|
+
out.push("")
|
|
238
267
|
}
|
|
239
|
-
out.push("")
|
|
240
|
-
}
|
|
241
268
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
269
|
+
if (stream.isTTY) out.push(...signatureLines(TARGET_NOTE, c))
|
|
270
|
+
else out.push(`${INDENT}${paintSignature(TARGET_NOTE, c)}`)
|
|
271
|
+
out.push("")
|
|
272
|
+
out.push(c("heading", "GitHub access:"))
|
|
273
|
+
if (stream.isTTY) out.push(...wrap(AUTHENTICATION.join(" "), { indent: STEP }, c))
|
|
274
|
+
else for (const line of AUTHENTICATION) out.push(`${INDENT}${paintProse(line, c)}`)
|
|
275
|
+
return `${out.join("\n")}\n`
|
|
276
|
+
})
|
|
247
277
|
}
|
package/tools/weigh/confirm.mjs
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// typed here is written anywhere.
|
|
6
6
|
|
|
7
7
|
import { createInterface } from "node:readline"
|
|
8
|
-
import { action, colourEnabled,
|
|
8
|
+
import { action, colourEnabled, outputColumns, styler } from "../marketplace/style.mjs"
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The question as it is written to the terminal: the arrow line, wrapped
|
|
@@ -20,7 +20,7 @@ import { action, colourEnabled, COLUMNS, styler } from "../marketplace/style.mjs
|
|
|
20
20
|
* @param {{ width?: number }} [options]
|
|
21
21
|
* @returns {string} every line, joined, ending in `[y/N]: `
|
|
22
22
|
*/
|
|
23
|
-
export function renderQuestion(question, c, { width =
|
|
23
|
+
export function renderQuestion(question, c, { width = outputColumns() } = {}) {
|
|
24
24
|
return `${action(`${question} [y/N]:`, c, { indent: 0, width }).join("\n")} `
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -28,7 +28,7 @@ export function renderQuestion(question, c, { width = COLUMNS } = {}) {
|
|
|
28
28
|
* @param {{ input?: NodeJS.ReadStream, output?: NodeJS.WriteStream, colour?: boolean, question: string, width?: number }} options
|
|
29
29
|
* @returns {Promise<boolean>} true only for `y` or `yes`, in any case; an empty line and the end of stdin are no
|
|
30
30
|
*/
|
|
31
|
-
export function askYes({ input = process.stdin, output = process.stderr, colour = colourEnabled(output), question, width =
|
|
31
|
+
export function askYes({ input = process.stdin, output = process.stderr, colour = colourEnabled(output), question, width = outputColumns(output) }) {
|
|
32
32
|
const c = styler(colour)
|
|
33
33
|
return new Promise((resolve) => {
|
|
34
34
|
const rl = createInterface({ input, terminal: false })
|