@optique/core 1.3.0-dev.2524 → 1.3.0-dev.2530

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.
Files changed (42) hide show
  1. package/dist/doc.cjs +93 -84
  2. package/dist/doc.d.cts +12 -1
  3. package/dist/doc.d.ts +12 -1
  4. package/dist/doc.js +88 -79
  5. package/dist/facade.cjs +49 -15
  6. package/dist/facade.d.cts +12 -1
  7. package/dist/facade.d.ts +12 -1
  8. package/dist/facade.js +48 -14
  9. package/dist/index.cjs +3 -0
  10. package/dist/index.d.cts +3 -2
  11. package/dist/index.d.ts +3 -2
  12. package/dist/index.js +3 -2
  13. package/dist/internal/terminal.cjs +7 -0
  14. package/dist/internal/terminal.d.cts +4 -0
  15. package/dist/internal/terminal.d.ts +4 -0
  16. package/dist/internal/terminal.js +5 -0
  17. package/dist/message-registry.cjs +6 -0
  18. package/dist/message-registry.d.cts +7 -0
  19. package/dist/message-registry.d.ts +7 -0
  20. package/dist/message-registry.js +5 -0
  21. package/dist/message-renderer.cjs +26 -0
  22. package/dist/message-renderer.d.cts +19 -0
  23. package/dist/message-renderer.d.ts +19 -0
  24. package/dist/message-renderer.js +26 -0
  25. package/dist/message.cjs +49 -85
  26. package/dist/message.d.cts +31 -1
  27. package/dist/message.d.ts +31 -1
  28. package/dist/message.js +49 -86
  29. package/dist/terminal-internal.cjs +259 -0
  30. package/dist/terminal-internal.d.cts +36 -0
  31. package/dist/terminal-internal.d.ts +36 -0
  32. package/dist/terminal-internal.js +253 -0
  33. package/dist/terminal.cjs +74 -0
  34. package/dist/terminal.d.cts +129 -0
  35. package/dist/terminal.d.ts +129 -0
  36. package/dist/terminal.js +73 -0
  37. package/dist/usage.cjs +108 -134
  38. package/dist/usage.d.cts +6 -0
  39. package/dist/usage.d.ts +6 -0
  40. package/dist/usage.js +108 -134
  41. package/package.json +18 -2
  42. package/skills/optique/SKILL.md +3 -3
package/dist/doc.js CHANGED
@@ -1,7 +1,10 @@
1
+ import { messageRenderers } from "./message-registry.js";
1
2
  import { getDisplayWidth } from "./displaywidth.js";
2
- import { cloneMessage, formatMessage, text } from "./message.js";
3
+ import { cacheTerminalTheme, formatTerminalTerm, fragmentTokens, renderTerminalTerm, styleCode } from "./terminal-internal.js";
4
+ import { cloneMessage, text } from "./message.js";
5
+ import { resolveMessageFormatter } from "./message-renderer.js";
3
6
  import { validateLabel, validateProgramName } from "./validate.js";
4
- import { cloneUsageTerm, formatUsage, formatUsageTerm, isDocHidden, isUsageHidden } from "./usage.js";
7
+ import { cloneUsageTerm, formatUsage, formatUsageTerm, isDocHidden } from "./usage.js";
5
8
 
6
9
  //#region src/doc.ts
7
10
  /**
@@ -211,6 +214,46 @@ function defaultSectionOrder(a, b) {
211
214
  * ```
212
215
  */
213
216
  function formatDocPage(programName, page, options = {}) {
217
+ options = {
218
+ ...options,
219
+ theme: cacheTerminalTheme(options.theme)
220
+ };
221
+ const formatMessage = resolveMessageFormatter(options);
222
+ const opaqueFormatter = options.messageFormatter != null && !messageRenderers.has(options.messageFormatter);
223
+ const defaultAmbient = options.theme?.annotationStyles?.default ?? { dim: true };
224
+ const choicesAmbient = options.theme?.annotationStyles?.choices ?? { dim: true };
225
+ const labelCache = /* @__PURE__ */ new Map();
226
+ const label = (label$1, kind) => {
227
+ const key = `${kind}:${label$1}`;
228
+ let rendered = labelCache.get(key);
229
+ if (rendered == null) {
230
+ rendered = renderTerminalTerm({
231
+ type: "label",
232
+ label: label$1,
233
+ kind
234
+ }, options.theme, options.colors, void 0, kind === "choices" ? choicesAmbient : void 0);
235
+ labelCache.set(key, rendered);
236
+ }
237
+ return rendered;
238
+ };
239
+ const punctuationCache = /* @__PURE__ */ new Map();
240
+ const punctuation = (punctuation$1, kind) => {
241
+ const key = `${kind}:${punctuation$1}`;
242
+ let rendered = punctuationCache.get(key);
243
+ if (rendered == null) {
244
+ rendered = renderTerminalTerm({
245
+ type: "syntaxPunctuation",
246
+ punctuation: punctuation$1,
247
+ kind
248
+ }, options.theme, options.colors, void 0, kind.startsWith("default") ? defaultAmbient : kind.startsWith("choices") ? choicesAmbient : void 0);
249
+ punctuationCache.set(key, rendered);
250
+ }
251
+ return rendered;
252
+ };
253
+ const usageLabel = page.usage != null && options.showUsage !== false ? label("Usage:", "usage") + " " : "";
254
+ const usageLabelWidth = getDisplayWidth(usageLabel);
255
+ const defaultStyle = styleCode(options.theme?.annotationStyles?.default ?? { dim: true });
256
+ const choicesStyle = styleCode(options.theme?.annotationStyles?.choices ?? { dim: true });
214
257
  validateProgramName(programName);
215
258
  const termIndent = options.termIndent ?? 2;
216
259
  const showUsage = options.showUsage ?? true;
@@ -238,6 +281,7 @@ function formatDocPage(programName, page, options = {}) {
238
281
  if (!needsDescriptionColumn(entry)) continue;
239
282
  const rendered = formatUsageTerm(entry.term, {
240
283
  colors: options.colors,
284
+ theme: options.theme,
241
285
  optionsSeparator: ", ",
242
286
  context: "doc"
243
287
  });
@@ -260,24 +304,27 @@ function formatDocPage(programName, page, options = {}) {
260
304
  let minDescWidth = 1;
261
305
  if (needsDescColumn) {
262
306
  if (options.showDefault && page.sections.some((s) => s.entries.some((e) => hasContent(e.default)))) {
263
- const prefix = typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [";
307
+ const prefix = punctuation(typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [", "defaultPrefix");
264
308
  minDescWidth = Math.max(minDescWidth, getDisplayWidth(prefix));
265
309
  }
266
310
  if (options.showChoices && page.sections.some((s) => s.entries.some((e) => hasContent(e.choices)))) {
267
- const prefix = typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (";
268
- const label = typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ";
269
- minDescWidth = Math.max(minDescWidth, getDisplayWidth(prefix) + getDisplayWidth(label));
311
+ const prefix = punctuation(typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (", "choicesPrefix");
312
+ const choicesLabelText = label(typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ", "choices");
313
+ minDescWidth = Math.max(minDescWidth, getDisplayWidth(prefix) + getDisplayWidth(choicesLabelText));
270
314
  }
271
315
  }
272
316
  const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
273
317
  const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
274
318
  const entryMin = needsDescColumn ? Math.min(splitEntryMin, fixedEntryMin) : hasEntries ? termIndent + 1 : 1;
275
- const programNameWidth = getDisplayWidth(programName);
276
- const usageMin = page.usage != null && showUsage ? 7 + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage), programNameWidth + 7)) : 1;
319
+ const programNameWidth = page.usage != null && showUsage ? Math.max(0, ...[...fragmentTokens(formatTerminalTerm({
320
+ type: "programName",
321
+ programName
322
+ }, options.theme))].map((token) => token.width)) : 0;
323
+ const usageMin = page.usage != null && showUsage ? usageLabelWidth + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage, options.theme), programNameWidth + usageLabelWidth)) : 1;
277
324
  let sectionMin = 1;
278
- if (hasContent(page.examples)) sectionMin = Math.max(sectionMin, 9);
279
- if (hasContent(page.author)) sectionMin = Math.max(sectionMin, 7);
280
- if (hasContent(page.bugs)) sectionMin = Math.max(sectionMin, 5);
325
+ if (hasContent(page.examples)) sectionMin = Math.max(sectionMin, getDisplayWidth(label("Examples:", "examples")));
326
+ if (hasContent(page.author)) sectionMin = Math.max(sectionMin, getDisplayWidth(label("Author:", "author")));
327
+ if (hasContent(page.bugs)) sectionMin = Math.max(sectionMin, getDisplayWidth(label("Bugs:", "bugs")));
281
328
  const minWidth = Math.max(entryMin, usageMin, sectionMin);
282
329
  if (options.maxWidth < minWidth) throw new RangeError(`maxWidth must be at least ${minWidth}, got ${options.maxWidth}.`);
283
330
  if (needsDescColumn && minDescWidth > 1) {
@@ -299,13 +346,13 @@ function formatDocPage(programName, page, options = {}) {
299
346
  output += "\n";
300
347
  }
301
348
  if (page.usage != null && showUsage) {
302
- const usageLabel = options.colors ? "\x1B[1;2mUsage:\x1B[0m " : "Usage: ";
303
349
  output += usageLabel;
304
350
  output += indentLines(formatUsage(programName, page.usage, {
305
351
  colors: options.colors,
306
- maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - 7,
352
+ theme: options.theme,
353
+ maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - usageLabelWidth,
307
354
  expandCommands: true
308
- }), 7);
355
+ }), usageLabelWidth);
309
356
  output += "\n";
310
357
  }
311
358
  if (hasContent(page.description)) {
@@ -331,12 +378,13 @@ function formatDocPage(programName, page, options = {}) {
331
378
  output += "\n";
332
379
  if (section.title != null) {
333
380
  validateLabel(section.title);
334
- const sectionLabel = options.colors ? `\x1b[1;2m${section.title}:\x1b[0m\n` : `${section.title}:\n`;
381
+ const sectionLabel = label(`${section.title}:`, "section") + "\n";
335
382
  output += sectionLabel;
336
383
  }
337
384
  for (const entry of section.entries) {
338
385
  const term = formatUsageTerm(entry.term, {
339
386
  colors: options.colors,
387
+ theme: options.theme,
340
388
  optionsSeparator: ", ",
341
389
  context: "doc",
342
390
  maxWidth: options.maxWidth == null ? void 0 : options.maxWidth - termIndent
@@ -349,12 +397,12 @@ function formatDocPage(programName, page, options = {}) {
349
397
  colors: options.colors,
350
398
  quotes: !options.colors,
351
399
  maxWidth: descColumnWidth,
352
- startWidth: extraTermOffset > 0 ? extraTermOffset : void 0
400
+ initialWidth: extraTermOffset > 0 ? extraTermOffset : void 0
353
401
  };
354
402
  let description = entry.description == null ? "" : formatMessage(entry.description, descFormatOptions);
355
403
  if (options.showDefault && hasContent(entry.default)) {
356
- const prefix = typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [";
357
- const suffix = typeof options.showDefault === "object" ? options.showDefault.suffix ?? "]" : "]";
404
+ const prefix = punctuation(typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [", "defaultPrefix");
405
+ const suffix = punctuation(typeof options.showDefault === "object" ? options.showDefault.suffix ?? "]" : "]", "defaultSuffix");
358
406
  const prefixWidth = getDisplayWidth(prefix);
359
407
  const suffixWidth = getDisplayWidth(suffix);
360
408
  let defaultStartWidth;
@@ -367,20 +415,20 @@ function formatDocPage(programName, page, options = {}) {
367
415
  } else defaultStartWidth = effectiveLastW + prefixWidth;
368
416
  }
369
417
  const defaultFormatOptions = {
370
- colors: options.colors ? { resetSuffix: "\x1B[2m" } : false,
418
+ colors: options.colors ? { resetSuffix: defaultStyle } : false,
371
419
  quotes: !options.colors,
372
420
  maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - suffixWidth,
373
- startWidth: defaultStartWidth
421
+ initialWidth: defaultStartWidth
374
422
  };
375
- const defaultContent = formatMessage(entry.default, defaultFormatOptions);
376
- const defaultText = `${prefix}${defaultContent}${suffix}`;
377
- const formattedDefault = options.colors ? `\x1b[2m${defaultText}\x1b[0m` : defaultText;
423
+ const defaultContent = formatMessage(entry.default, defaultFormatOptions, options.theme?.annotationStyles?.default ?? { dim: true });
424
+ const defaultText = `${prefix}${defaultContent}${opaqueFormatter && options.colors ? defaultStyle : ""}${suffix}`;
425
+ const formattedDefault = options.colors ? `${defaultStyle}${defaultText}${defaultStyle ? "\x1B[0m" : ""}` : defaultText;
378
426
  description += formattedDefault;
379
427
  }
380
428
  if (options.showChoices && hasContent(entry.choices)) {
381
- const prefix = typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (";
382
- const suffix = typeof options.showChoices === "object" ? options.showChoices.suffix ?? ")" : ")";
383
- const label = typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ";
429
+ const prefix = punctuation(typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (", "choicesPrefix");
430
+ const suffix = punctuation(typeof options.showChoices === "object" ? options.showChoices.suffix ?? ")" : ")", "choicesSuffix");
431
+ const choicesLabelText = label(typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ", "choices");
384
432
  const maxItems = typeof options.showChoices === "object" ? options.showChoices.maxItems ?? 8 : 8;
385
433
  const terms = Array.isArray(entry.choices) ? entry.choices : [];
386
434
  let truncatedTerms = terms;
@@ -400,7 +448,7 @@ function formatDocPage(programName, page, options = {}) {
400
448
  }
401
449
  const choicesPrefixWidth = getDisplayWidth(prefix);
402
450
  const choicesSuffixWidth = getDisplayWidth(suffix);
403
- const choicesLabelWidth = getDisplayWidth(label);
451
+ const choicesLabelWidth = getDisplayWidth(choicesLabelText);
404
452
  let choicesStartWidth;
405
453
  if (descColumnWidth != null) {
406
454
  const lastW = lastLineVisibleLength(description);
@@ -412,14 +460,14 @@ function formatDocPage(programName, page, options = {}) {
412
460
  } else choicesStartWidth = effectiveLastW + prefixLabelLen;
413
461
  }
414
462
  const choicesFormatOptions = {
415
- colors: options.colors ? { resetSuffix: "\x1B[2m" } : false,
463
+ colors: options.colors ? { resetSuffix: choicesStyle } : false,
416
464
  quotes: false,
417
465
  maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - choicesSuffixWidth,
418
- startWidth: choicesStartWidth
466
+ initialWidth: choicesStartWidth
419
467
  };
420
- const choicesDisplay = formatMessage(truncatedTerms, choicesFormatOptions);
421
- const choicesText = `${prefix}${label}${choicesDisplay}${suffix}`;
422
- const formattedChoices = options.colors ? `\x1b[2m${choicesText}\x1b[0m` : choicesText;
468
+ const choicesDisplay = formatMessage(truncatedTerms, choicesFormatOptions, options.theme?.annotationStyles?.choices ?? { dim: true });
469
+ const choicesText = `${prefix}${choicesLabelText}${choicesDisplay}${opaqueFormatter && options.colors ? choicesStyle : ""}${suffix}`;
470
+ const formattedChoices = options.colors ? `${choicesStyle}${choicesText}${choicesStyle ? "\x1B[0m" : ""}` : choicesText;
423
471
  description += formattedChoices;
424
472
  }
425
473
  output += `${" ".repeat(termIndent)}${ansiAwareRightPad(term, effectiveTermWidth)}${description === "" ? "" : ` ${indentLines(description, termIndent + effectiveTermWidth + 2)}`}\n`;
@@ -427,7 +475,7 @@ function formatDocPage(programName, page, options = {}) {
427
475
  }
428
476
  if (hasContent(page.examples)) {
429
477
  output += "\n";
430
- const examplesLabel = options.colors ? "\x1B[1;2mExamples:\x1B[0m\n" : "Examples:\n";
478
+ const examplesLabel = label("Examples:", "examples") + "\n";
431
479
  output += examplesLabel;
432
480
  const examplesContent = formatMessage(page.examples, {
433
481
  colors: options.colors,
@@ -439,7 +487,7 @@ function formatDocPage(programName, page, options = {}) {
439
487
  }
440
488
  if (hasContent(page.author)) {
441
489
  output += "\n";
442
- const authorLabel = options.colors ? "\x1B[1;2mAuthor:\x1B[0m\n" : "Author:\n";
490
+ const authorLabel = label("Author:", "author") + "\n";
443
491
  output += authorLabel;
444
492
  const authorContent = formatMessage(page.author, {
445
493
  colors: options.colors,
@@ -451,7 +499,7 @@ function formatDocPage(programName, page, options = {}) {
451
499
  }
452
500
  if (hasContent(page.bugs)) {
453
501
  output += "\n";
454
- const bugsLabel = options.colors ? "\x1B[1;2mBugs:\x1B[0m\n" : "Bugs:\n";
502
+ const bugsLabel = label("Bugs:", "bugs") + "\n";
455
503
  output += bugsLabel;
456
504
  const bugsContent = formatMessage(page.bugs, {
457
505
  colors: options.colors,
@@ -480,50 +528,11 @@ function indentLines(text$1, indent) {
480
528
  * because they are filtered out before rendering, so they do not
481
529
  * contribute to the rendered width.
482
530
  */
483
- function maxVisibleAtomicWidth(usage) {
484
- let max = 0;
485
- for (const term of usage) switch (term.type) {
486
- case "argument":
487
- if (!isUsageHidden(term.hidden)) max = Math.max(max, getDisplayWidth(term.metavar));
488
- break;
489
- case "option":
490
- if (!isUsageHidden(term.hidden) && term.names.length > 0) {
491
- for (const name of term.names) max = Math.max(max, getDisplayWidth(name));
492
- if (term.metavar != null) max = Math.max(max, getDisplayWidth(term.metavar));
493
- }
494
- break;
495
- case "command":
496
- if (!isUsageHidden(term.hidden)) max = Math.max(max, getDisplayWidth(term.name));
497
- break;
498
- case "passthrough":
499
- if (!isUsageHidden(term.hidden)) max = Math.max(max, 5);
500
- break;
501
- case "optional":
502
- max = Math.max(max, maxVisibleAtomicWidth(term.terms));
503
- break;
504
- case "multiple": {
505
- const innerMax = maxVisibleAtomicWidth(term.terms);
506
- if (innerMax > 0) max = Math.max(max, 3, innerMax);
507
- break;
508
- }
509
- case "exclusive":
510
- for (const branch of term.terms) {
511
- const first = branch[0];
512
- if (first?.type === "command" && isUsageHidden(first.hidden)) continue;
513
- max = Math.max(max, maxVisibleAtomicWidth(branch));
514
- }
515
- break;
516
- case "sequence":
517
- max = Math.max(max, maxVisibleAtomicWidth(term.terms));
518
- break;
519
- case "literal":
520
- if (term.value !== "") max = Math.max(max, getDisplayWidth(term.value));
521
- break;
522
- case "ellipsis":
523
- max = Math.max(max, 3);
524
- break;
525
- }
526
- return max;
531
+ function maxVisibleAtomicWidth(usage, theme) {
532
+ return Math.max(0, ...usage.flatMap((term) => formatUsageTerm(term, {
533
+ theme,
534
+ maxWidth: 1
535
+ }).split("\n").map(getDisplayWidth)));
527
536
  }
528
537
  function ansiAwareRightPad(text$1, length, char = " ") {
529
538
  const visibleWidth = lastLineVisibleLength(text$1);
package/dist/facade.cjs CHANGED
@@ -1,5 +1,8 @@
1
1
  const require_internal_annotations = require('./internal/annotations.cjs');
2
+ const require_displaywidth = require('./displaywidth.cjs');
3
+ const require_terminal_internal = require('./terminal-internal.cjs');
2
4
  const require_message = require('./message.cjs');
5
+ const require_message_renderer = require('./message-renderer.cjs');
3
6
  const require_validate = require('./validate.cjs');
4
7
  const require_usage = require('./usage.cjs');
5
8
  const require_doc = require('./doc.cjs');
@@ -513,14 +516,22 @@ function classifyParseFailure(failure, helpOptionNames, helpCommandNames, versio
513
516
  * Handles shell completion requests.
514
517
  * @since 0.6.0
515
518
  */
516
- function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, termWidth, completionCommandDisplayName, completionOptionDisplayName, isOptionMode, sectionOrder, showUsage, rootOptionSuggestions = []) {
519
+ function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, termWidth, completionCommandDisplayName, completionOptionDisplayName, isOptionMode, sectionOrder, showUsage, rootOptionSuggestions = [], messageFormatter, theme) {
520
+ const formatMessage = require_message_renderer.resolveMessageFormatter({
521
+ messageFormatter,
522
+ theme
523
+ });
524
+ const errorLabel = () => require_terminal_internal.renderTerminalTerm({
525
+ type: "errorLabel",
526
+ label: "Error:"
527
+ }, theme, colors) + " ";
517
528
  const shellName = completionArgs[0] || "";
518
529
  const args = completionArgs.slice(1);
519
530
  const callOnError = (code, error) => onError(code, error);
520
531
  const callOnCompletion = (code) => onCompletion(code);
521
532
  if (!shellName) {
522
533
  const error = require_message.message`Missing shell name for completion.`;
523
- stderr(`Error: ${require_message.formatMessage(error, {
534
+ stderr(`${errorLabel()}${formatMessage(error, {
524
535
  colors,
525
536
  quotes: !colors
526
537
  })}\n`);
@@ -528,6 +539,8 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
528
539
  const displayName = isOptionMode ? completionOptionDisplayName ?? "--completion" : completionCommandDisplayName ?? "completion";
529
540
  const doc = require_internal_parser.getDocPage(completionParser, [displayName]);
530
541
  if (doc) stderr(require_doc.formatDocPage(programName, doc, {
542
+ messageFormatter,
543
+ theme,
531
544
  colors,
532
545
  maxWidth,
533
546
  termWidth,
@@ -549,7 +562,7 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
549
562
  available.push(require_message.value(name));
550
563
  }
551
564
  const error = require_message.message`Unsupported shell ${shellName}. Available shells: ${available}.`;
552
- stderr(`Error: ${require_message.formatMessage(error, {
565
+ stderr(`${errorLabel()}${formatMessage(error, {
553
566
  colors,
554
567
  quotes: !colors
555
568
  })}`);
@@ -860,9 +873,24 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
860
873
  options = optionsParam ?? {};
861
874
  }
862
875
  require_validate.validateProgramName(programName);
863
- const { colors, maxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
876
+ const { colors, messageFormatter, theme, maxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
864
877
  throw new RunParserError("Failed to parse command line arguments.");
865
878
  }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
879
+ const formatMessage = require_message_renderer.resolveMessageFormatter({
880
+ messageFormatter,
881
+ theme
882
+ });
883
+ const errorLabel = () => require_terminal_internal.renderTerminalTerm({
884
+ type: "errorLabel",
885
+ label: "Error:"
886
+ }, theme, colors) + " ";
887
+ let usagePrefix;
888
+ const usageLabel = () => usagePrefix ??= require_terminal_internal.renderTerminalTerm({
889
+ type: "label",
890
+ label: "Usage:",
891
+ kind: "usageSummary"
892
+ }, theme, colors) + " ";
893
+ const usageLabelWidth = () => require_displaywidth.getDisplayWidth(usageLabel());
866
894
  const norm = (c) => c === true ? {} : c;
867
895
  const helpCommandConfig = norm(options.help?.command);
868
896
  const helpOptionConfig = norm(options.help?.option);
@@ -998,7 +1026,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
998
1026
  classified.shell,
999
1027
  ...classified.commandPath ?? [],
1000
1028
  ...classified.args
1001
- ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, termWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder, showUsage, rootOptionSuggestions);
1029
+ ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, termWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder, showUsage, rootOptionSuggestions, messageFormatter, theme);
1002
1030
  case "help": {
1003
1031
  let helpGeneratorParser;
1004
1032
  let docGeneratorParser;
@@ -1017,16 +1045,17 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1017
1045
  docGeneratorParser = classified.commands.length > 0 ? parser : helpGeneratorParser;
1018
1046
  }
1019
1047
  const reportInvalidHelpCommand = (validationError) => {
1020
- stderr(`Usage: ${indentLines(require_usage.formatUsage(programName, augmentedParser.usage, {
1048
+ stderr(`${usageLabel()}${indentLines(require_usage.formatUsage(programName, augmentedParser.usage, {
1049
+ theme,
1021
1050
  colors,
1022
- maxWidth: maxWidth == null ? void 0 : maxWidth - 7,
1051
+ maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1023
1052
  expandCommands: true
1024
- }), 7)}`);
1025
- const errorMessage = require_message.formatMessage(validationError, {
1053
+ }), usageLabelWidth())}`);
1054
+ const errorMessage = formatMessage(validationError, {
1026
1055
  colors,
1027
1056
  quotes: !colors
1028
1057
  });
1029
- stderr(`Error: ${errorMessage}`);
1058
+ stderr(`${errorLabel()}${errorMessage}`);
1030
1059
  return onError(1, validationError);
1031
1060
  };
1032
1061
  const displayHelp = (doc) => {
@@ -1046,6 +1075,8 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1046
1075
  }, commandList, isTopLevel);
1047
1076
  const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine) : augmentedDoc;
1048
1077
  stdout(require_doc.formatDocPage(programName, renderedDoc, {
1078
+ messageFormatter,
1079
+ theme,
1049
1080
  colors,
1050
1081
  maxWidth,
1051
1082
  termWidth,
@@ -1120,6 +1151,8 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1120
1151
  const defaultRootUsage = typeof usageLine === "function" && (options.help || options.version || options.completion) ? require_usage.normalizeUsage(getRootHelpGeneratorParser().usage) : augmentedDoc.usage ?? [];
1121
1152
  const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine, defaultRootUsage) : augmentedDoc;
1122
1153
  stderr(require_doc.formatDocPage(programName, renderedDoc, {
1154
+ messageFormatter,
1155
+ theme,
1123
1156
  colors,
1124
1157
  maxWidth,
1125
1158
  termWidth,
@@ -1129,16 +1162,17 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1129
1162
  showUsage
1130
1163
  }));
1131
1164
  }
1132
- if (effectiveAboveError === "usage") stderr(`Usage: ${indentLines(require_usage.formatUsage(programName, augmentedParser.usage, {
1165
+ if (effectiveAboveError === "usage") stderr(`${usageLabel()}${indentLines(require_usage.formatUsage(programName, augmentedParser.usage, {
1166
+ theme,
1133
1167
  colors,
1134
- maxWidth: maxWidth == null ? void 0 : maxWidth - 7,
1168
+ maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1135
1169
  expandCommands: true
1136
- }), 7)}`);
1137
- const errorMessage = require_message.formatMessage(classified.error, {
1170
+ }), usageLabelWidth())}`);
1171
+ const errorMessage = formatMessage(classified.error, {
1138
1172
  colors,
1139
1173
  quotes: !colors
1140
1174
  });
1141
- stderr(`Error: ${errorMessage}`);
1175
+ stderr(`${errorLabel()}${errorMessage}`);
1142
1176
  return onError(1, classified.error);
1143
1177
  };
1144
1178
  if (aboveError === "help") {
package/dist/facade.d.cts CHANGED
@@ -1,4 +1,5 @@
1
- import { Message } from "./message.cjs";
1
+ import { Message, MessageFormatter } from "./message.cjs";
2
+ import { TerminalTheme } from "./terminal.cjs";
2
3
  import { HiddenVisibility, OptionName, Usage } from "./usage.cjs";
3
4
  import { DocPage, DocSection, ShowChoicesOptions, ShowDefaultOptions } from "./doc.cjs";
4
5
  import { InferMode, InferValue, Mode, ModeValue, Parser } from "./internal/parser.cjs";
@@ -78,6 +79,16 @@ interface OptionSubConfig {
78
79
  * @template TError The return type when an error occurs.
79
80
  */
80
81
  interface RunOptions<THelp, TError> {
82
+ /**
83
+ * Custom message renderer, taking precedence over theme for messages.
84
+ * @since 1.3.0
85
+ */
86
+ readonly messageFormatter?: MessageFormatter;
87
+ /**
88
+ * Semantic terminal theme.
89
+ * @since 1.3.0
90
+ */
91
+ readonly theme?: TerminalTheme;
81
92
  /**
82
93
  * Enable colored output in help and error messages.
83
94
  *
package/dist/facade.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { Message } from "./message.js";
1
+ import { Message, MessageFormatter } from "./message.js";
2
+ import { TerminalTheme } from "./terminal.js";
2
3
  import { HiddenVisibility, OptionName, Usage } from "./usage.js";
3
4
  import { DocPage, DocSection, ShowChoicesOptions, ShowDefaultOptions } from "./doc.js";
4
5
  import { InferMode, InferValue, Mode, ModeValue, Parser } from "./internal/parser.js";
@@ -78,6 +79,16 @@ interface OptionSubConfig {
78
79
  * @template TError The return type when an error occurs.
79
80
  */
80
81
  interface RunOptions<THelp, TError> {
82
+ /**
83
+ * Custom message renderer, taking precedence over theme for messages.
84
+ * @since 1.3.0
85
+ */
86
+ readonly messageFormatter?: MessageFormatter;
87
+ /**
88
+ * Semantic terminal theme.
89
+ * @since 1.3.0
90
+ */
91
+ readonly theme?: TerminalTheme;
81
92
  /**
82
93
  * Enable colored output in help and error messages.
83
94
  *
package/dist/facade.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { injectAnnotations } from "./internal/annotations.js";
2
- import { commandLine, formatMessage, lineBreak, message, optionName, text, value } from "./message.js";
2
+ import { getDisplayWidth } from "./displaywidth.js";
3
+ import { renderTerminalTerm } from "./terminal-internal.js";
4
+ import { commandLine, lineBreak, message, optionName, text, value } from "./message.js";
5
+ import { resolveMessageFormatter } from "./message-renderer.js";
3
6
  import { validateCommandNames, validateContextIds, validateMetaNameCollisions, validateOptionNames, validateProgramName } from "./validate.js";
4
7
  import { cloneUsage, formatUsage, isSuggestionHidden, normalizeUsage } from "./usage.js";
5
8
  import { formatDocPage } from "./doc.js";
@@ -513,14 +516,22 @@ function classifyParseFailure(failure, helpOptionNames, helpCommandNames, versio
513
516
  * Handles shell completion requests.
514
517
  * @since 0.6.0
515
518
  */
516
- function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, termWidth, completionCommandDisplayName, completionOptionDisplayName, isOptionMode, sectionOrder, showUsage, rootOptionSuggestions = []) {
519
+ function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, termWidth, completionCommandDisplayName, completionOptionDisplayName, isOptionMode, sectionOrder, showUsage, rootOptionSuggestions = [], messageFormatter, theme) {
520
+ const formatMessage = resolveMessageFormatter({
521
+ messageFormatter,
522
+ theme
523
+ });
524
+ const errorLabel = () => renderTerminalTerm({
525
+ type: "errorLabel",
526
+ label: "Error:"
527
+ }, theme, colors) + " ";
517
528
  const shellName = completionArgs[0] || "";
518
529
  const args = completionArgs.slice(1);
519
530
  const callOnError = (code, error) => onError(code, error);
520
531
  const callOnCompletion = (code) => onCompletion(code);
521
532
  if (!shellName) {
522
533
  const error = message`Missing shell name for completion.`;
523
- stderr(`Error: ${formatMessage(error, {
534
+ stderr(`${errorLabel()}${formatMessage(error, {
524
535
  colors,
525
536
  quotes: !colors
526
537
  })}\n`);
@@ -528,6 +539,8 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
528
539
  const displayName = isOptionMode ? completionOptionDisplayName ?? "--completion" : completionCommandDisplayName ?? "completion";
529
540
  const doc = getDocPage(completionParser, [displayName]);
530
541
  if (doc) stderr(formatDocPage(programName, doc, {
542
+ messageFormatter,
543
+ theme,
531
544
  colors,
532
545
  maxWidth,
533
546
  termWidth,
@@ -549,7 +562,7 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
549
562
  available.push(value(name));
550
563
  }
551
564
  const error = message`Unsupported shell ${shellName}. Available shells: ${available}.`;
552
- stderr(`Error: ${formatMessage(error, {
565
+ stderr(`${errorLabel()}${formatMessage(error, {
553
566
  colors,
554
567
  quotes: !colors
555
568
  })}`);
@@ -860,9 +873,24 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
860
873
  options = optionsParam ?? {};
861
874
  }
862
875
  validateProgramName(programName);
863
- const { colors, maxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
876
+ const { colors, messageFormatter, theme, maxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
864
877
  throw new RunParserError("Failed to parse command line arguments.");
865
878
  }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
879
+ const formatMessage = resolveMessageFormatter({
880
+ messageFormatter,
881
+ theme
882
+ });
883
+ const errorLabel = () => renderTerminalTerm({
884
+ type: "errorLabel",
885
+ label: "Error:"
886
+ }, theme, colors) + " ";
887
+ let usagePrefix;
888
+ const usageLabel = () => usagePrefix ??= renderTerminalTerm({
889
+ type: "label",
890
+ label: "Usage:",
891
+ kind: "usageSummary"
892
+ }, theme, colors) + " ";
893
+ const usageLabelWidth = () => getDisplayWidth(usageLabel());
866
894
  const norm = (c) => c === true ? {} : c;
867
895
  const helpCommandConfig = norm(options.help?.command);
868
896
  const helpOptionConfig = norm(options.help?.option);
@@ -998,7 +1026,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
998
1026
  classified.shell,
999
1027
  ...classified.commandPath ?? [],
1000
1028
  ...classified.args
1001
- ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, termWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder, showUsage, rootOptionSuggestions);
1029
+ ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, termWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder, showUsage, rootOptionSuggestions, messageFormatter, theme);
1002
1030
  case "help": {
1003
1031
  let helpGeneratorParser;
1004
1032
  let docGeneratorParser;
@@ -1017,16 +1045,17 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1017
1045
  docGeneratorParser = classified.commands.length > 0 ? parser : helpGeneratorParser;
1018
1046
  }
1019
1047
  const reportInvalidHelpCommand = (validationError) => {
1020
- stderr(`Usage: ${indentLines(formatUsage(programName, augmentedParser.usage, {
1048
+ stderr(`${usageLabel()}${indentLines(formatUsage(programName, augmentedParser.usage, {
1049
+ theme,
1021
1050
  colors,
1022
- maxWidth: maxWidth == null ? void 0 : maxWidth - 7,
1051
+ maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1023
1052
  expandCommands: true
1024
- }), 7)}`);
1053
+ }), usageLabelWidth())}`);
1025
1054
  const errorMessage = formatMessage(validationError, {
1026
1055
  colors,
1027
1056
  quotes: !colors
1028
1057
  });
1029
- stderr(`Error: ${errorMessage}`);
1058
+ stderr(`${errorLabel()}${errorMessage}`);
1030
1059
  return onError(1, validationError);
1031
1060
  };
1032
1061
  const displayHelp = (doc) => {
@@ -1046,6 +1075,8 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1046
1075
  }, commandList, isTopLevel);
1047
1076
  const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine) : augmentedDoc;
1048
1077
  stdout(formatDocPage(programName, renderedDoc, {
1078
+ messageFormatter,
1079
+ theme,
1049
1080
  colors,
1050
1081
  maxWidth,
1051
1082
  termWidth,
@@ -1120,6 +1151,8 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1120
1151
  const defaultRootUsage = typeof usageLine === "function" && (options.help || options.version || options.completion) ? normalizeUsage(getRootHelpGeneratorParser().usage) : augmentedDoc.usage ?? [];
1121
1152
  const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine, defaultRootUsage) : augmentedDoc;
1122
1153
  stderr(formatDocPage(programName, renderedDoc, {
1154
+ messageFormatter,
1155
+ theme,
1123
1156
  colors,
1124
1157
  maxWidth,
1125
1158
  termWidth,
@@ -1129,16 +1162,17 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1129
1162
  showUsage
1130
1163
  }));
1131
1164
  }
1132
- if (effectiveAboveError === "usage") stderr(`Usage: ${indentLines(formatUsage(programName, augmentedParser.usage, {
1165
+ if (effectiveAboveError === "usage") stderr(`${usageLabel()}${indentLines(formatUsage(programName, augmentedParser.usage, {
1166
+ theme,
1133
1167
  colors,
1134
- maxWidth: maxWidth == null ? void 0 : maxWidth - 7,
1168
+ maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1135
1169
  expandCommands: true
1136
- }), 7)}`);
1170
+ }), usageLabelWidth())}`);
1137
1171
  const errorMessage = formatMessage(classified.error, {
1138
1172
  colors,
1139
1173
  quotes: !colors
1140
1174
  });
1141
- stderr(`Error: ${errorMessage}`);
1175
+ stderr(`${errorLabel()}${errorMessage}`);
1142
1176
  return onError(1, classified.error);
1143
1177
  };
1144
1178
  if (aboveError === "help") {
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  const require_internal_annotations = require('./internal/annotations.cjs');
2
+ const require_terminal = require('./terminal.cjs');
2
3
  const require_message = require('./message.cjs');
3
4
  const require_usage = require('./usage.cjs');
4
5
  const require_doc = require('./doc.cjs');
@@ -31,10 +32,12 @@ exports.commandLine = require_message.commandLine;
31
32
  exports.concat = require_constructs.concat;
32
33
  exports.conditional = require_constructs.conditional;
33
34
  exports.constant = require_primitives.constant;
35
+ exports.createMessageFormatter = require_message.createMessageFormatter;
34
36
  exports.createParserContext = require_internal_parser.createParserContext;
35
37
  exports.cron = require_valueparser.cron;
36
38
  exports.deduplicateDocEntries = require_doc.deduplicateDocEntries;
37
39
  exports.deduplicateDocFragments = require_doc.deduplicateDocFragments;
40
+ exports.defaultTerminalTheme = require_terminal.defaultTerminalTheme;
38
41
  exports.deferredValue = require_modifiers.deferredValue;
39
42
  exports.dependency = require_internal_dependency.dependency;
40
43
  exports.deriveFrom = require_internal_dependency.deriveFrom;