aontu 0.54.0 → 0.55.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/src/cli.ts CHANGED
@@ -40,10 +40,10 @@ import type { TrimReport, TrimVerdict } from './trim'
40
40
  import type { RelationReport, RelationVerdict } from './relation'
41
41
  import { reachCheck } from './reach'
42
42
  import type { ReachReport, ReachVerdict } from './reach'
43
- import { view, viewSet } from './view'
43
+ import { view, viewSet, viewDefaultProfile } from './view'
44
44
  import type {
45
45
  ViewEdges, ViewFigure, ViewKind, ViewLoss, ViewOptions, ViewProfile,
46
- ViewReport, ViewSetReport, ViewVerdict,
46
+ ViewReport, ViewSetReport, ViewStyle, ViewVerdict,
47
47
  } from './view'
48
48
  import type { QueryView } from './query'
49
49
  import type { WhyRecord } from './provenance'
@@ -190,13 +190,14 @@ kind and out file, nothing is written unless every figure rendered,
190
190
  and --check gates the committed set.
191
191
 
192
192
  View options:
193
- --as <profile> text | mermaid | dot | er | svg, per kind: tree,
194
- matrix, sets and layers draw text (default) or
195
- svg; graph draws mermaid (default), dot or er;
193
+ --as <profile> text | mermaid | dot | er | svg, per kind: doc,
194
+ tree, matrix, sets and layers draw text (default)
195
+ or svg; graph draws mermaid (default), dot or er;
196
196
  layer draws text (default), mermaid or svg; ladder
197
197
  and poset draw mermaid (default) or dot
198
198
  --at <path> Restrict the figure to nodes under this path; the
199
- path the ladder draws; where the poset compares
199
+ subtree doc draws; the path the ladder draws;
200
+ where the poset compares
200
201
  --views <path> Draw every figure the document declares at this
201
202
  path, one evaluation, all or nothing; each
202
203
  declaration names its own kind and out file
@@ -205,7 +206,20 @@ View options:
205
206
  nothing is written
206
207
  --strict Exit 1 when the loss report holds anything beyond
207
208
  edges_deduped, inverse_suppressed and crossings
209
+ --depth <n> doc: how many levels of key to draw (default 3)
208
210
  --max-rows <n> Refuse a figure above this many rows (default 60)
211
+ --style <s> auto (default), none, ansi or css. A figure's
212
+ marks carry their meaning -- a direct cell, a
213
+ closure cell, an upward edge -- and each profile
214
+ has one way to show it: SGR escapes for text, CSS
215
+ classes for svg. auto picks that mechanism where
216
+ the destination can carry it: escapes only on a
217
+ terminal (NO_COLOR is honoured), and an svg keeps
218
+ the stylesheet that makes it standalone. none
219
+ drops both; on svg the classes stay and only the
220
+ stylesheet goes, for a host page that has already
221
+ bound --av-ink and its kin. Escapes are never
222
+ written to a file
209
223
  --format <f> text (default) or json, the whole report
210
224
  --relation <n> tree, matrix, layer: draw over this relation only;
211
225
  graph: keep this predicate (repeatable)
@@ -1774,12 +1788,47 @@ const VIEW_HELP =
1774
1788
  'aontu view <kind> [options] <file>... (try --help)'
1775
1789
 
1776
1790
  const VIEW_KINDS: ViewKind[] =
1777
- ['tree', 'matrix', 'graph', 'layer', 'sets', 'layers', 'ladder', 'poset']
1791
+ ['doc', 'tree', 'matrix', 'graph', 'layer', 'sets', 'layers', 'ladder',
1792
+ 'poset']
1778
1793
 
1779
1794
  const VIEW_PROFILES: ViewProfile[] = ['text', 'mermaid', 'dot', 'er', 'svg']
1780
1795
 
1781
1796
  const VIEW_EDGES: ViewEdges[] = ['upward', 'all', 'none']
1782
1797
 
1798
+ // The styles the CLI accepts (VIEWS.0.md, "7. Styling"). `auto` is
1799
+ // here and NOT in ViewStyle: resolving it means knowing whether stdout
1800
+ // is a terminal, which is the CLI's to know and the library's never --
1801
+ // the same division err.ts already draws for the error frames.
1802
+ const VIEW_STYLES = ['auto', 'none', 'ansi', 'css']
1803
+
1804
+ // `--style auto` resolved, which only the CLI can do. The mechanism is
1805
+ // the PROFILE's and the library knows it -- an SVG carries its
1806
+ // stylesheet unless told not to, which is what makes a figure stand
1807
+ // alone. What the library cannot know is whether the DESTINATION is a
1808
+ // terminal, so that is the only thing decided here: escapes on the
1809
+ // text profile when stdout is a terminal and NO_COLOR is unset, the
1810
+ // same two conditions the error frames use. `undefined` leaves the
1811
+ // profile's own default in place.
1812
+ function viewStyleOf(
1813
+ asked: string | undefined, as: ViewProfile | undefined
1814
+ ): ViewStyle | undefined {
1815
+ if (undefined !== asked && 'auto' !== asked) {
1816
+ return asked as ViewStyle
1817
+ }
1818
+ // STDOUT'S OWN TERMINAL-NESS, and NO_COLOR read here rather than
1819
+ // through colorActive(). The figure goes to STDOUT and the error
1820
+ // frames go to STDERR, and they are not the same destination: main()
1821
+ // has already called setColor for stderr, so asking colorActive()
1822
+ // would answer the wrong question twice --- no escapes for
1823
+ // `aontu view tree m.aon 2>/dev/null` at a terminal, and escapes
1824
+ // into the pipe for `aontu view tree m.aon | less`. The NO_COLOR
1825
+ // rule is the one no-color.org states and err.ts implements:
1826
+ // set, to anything but empty, means no colour.
1827
+ const no = process.env.NO_COLOR
1828
+ return 'text' === as && true === process.stdout.isTTY
1829
+ && (null == no || '' === no) ? 'ansi' : undefined
1830
+ }
1831
+
1783
1832
  // The figure was drawn (0, `lossy` included: the loss report says
1784
1833
  // what it could not draw, and --strict is the gate on that), or the
1785
1834
  // document could not be drawn (4). An EMPTY figure is a drawing, not
@@ -1795,7 +1844,7 @@ const VIEW_EXIT: Record<ViewVerdict, number> = {
1795
1844
  const VIEW_USAGE_CODES = [
1796
1845
  'view_kind_unknown', 'view_profile_unknown', 'view_rows_exceeded',
1797
1846
  'view_at_required', 'view_sets_required', 'view_group_required',
1798
- 'view_document_shape',
1847
+ 'view_document_shape', 'view_style_profile', 'view_style_unknown',
1799
1848
  ]
1800
1849
 
1801
1850
  const MOD_HELP = 'aontu mod tidy|verify|vendor|manifest [dir] (try --help)'
@@ -1860,7 +1909,8 @@ function runMod(argv: string[]): number {
1860
1909
  if ('get' === sub || 'publish' === sub) {
1861
1910
  process.stderr.write(
1862
1911
  'aontu: mod ' + sub + ' needs a registry client, which this build ' +
1863
- 'does not ship (docs/capability-review/g6-distribution.md)\n')
1912
+ 'does not ship; vendor the module by hand and run ' +
1913
+ "'aontu mod tidy'\n")
1864
1914
  return 2
1865
1915
  }
1866
1916
 
@@ -2180,6 +2230,9 @@ function runView(argv: string[]): number {
2180
2230
  const relations: string[] = []
2181
2231
  const roots: string[] = []
2182
2232
  const opts: ViewOptions = {}
2233
+ // The style ASKED FOR, which may be `auto` -- a word ViewStyle does
2234
+ // not have, because resolving it is the CLI's job.
2235
+ let style: string | undefined = undefined
2183
2236
 
2184
2237
  // A flag that takes a value, read into `opts` by name.
2185
2238
  const valued: Record<string, keyof ViewOptions> = {
@@ -2191,6 +2244,7 @@ function runView(argv: string[]): number {
2191
2244
  const counted: Record<string, keyof ViewOptions> = {
2192
2245
  '--max-rows': 'maxRows', '--max-cols': 'maxCols',
2193
2246
  '--min-degree': 'minDegree', '--min-size': 'minSize',
2247
+ '--depth': 'depth',
2194
2248
  }
2195
2249
 
2196
2250
  for (let i = 0; i < argv.length; i++) {
@@ -2230,6 +2284,14 @@ function runView(argv: string[]): number {
2230
2284
  return 2
2231
2285
  }
2232
2286
  }
2287
+ else if ('--style' === arg) {
2288
+ style = argv[++i]
2289
+ if (null == style || !VIEW_STYLES.includes(style)) {
2290
+ process.stderr.write(
2291
+ `aontu: --style needs one of ${VIEW_STYLES.join(', ')}\n`)
2292
+ return 2
2293
+ }
2294
+ }
2233
2295
  else if ('--check' === arg) {
2234
2296
  check = true
2235
2297
  }
@@ -2273,9 +2335,26 @@ function runView(argv: string[]): number {
2273
2335
  }
2274
2336
  }
2275
2337
 
2338
+ // ESCAPES NEVER GO INTO A FILE. A pinned golden holding terminal
2339
+ // control codes is not a golden anybody can read, and a byte
2340
+ // comparison against one would fail on the reader's terminal
2341
+ // settings. `auto` resolves to `none` there on its own; asking for
2342
+ // `ansi` explicitly is a usage error rather than a silent downgrade,
2343
+ // so a script that wanted colour is told where it went.
2344
+ if ('ansi' === style && (undefined !== out || undefined !== opts.views)) {
2345
+ process.stderr.write(
2346
+ 'aontu: --style ansi writes to a terminal, not to a file\n')
2347
+ return 2
2348
+ }
2349
+
2276
2350
  // THE VIEW DOCUMENT draws every figure a document declares, so it
2277
2351
  // names no kind: the declarations do, one each.
2278
2352
  if (undefined !== opts.views) {
2353
+ // A declaration names its own profile, so the style is left to
2354
+ // each figure's own default; `--style none` still reaches every
2355
+ // one of them, which is how a host page that binds the CSS
2356
+ // variables asks for eight figures without eight stylesheets.
2357
+ opts.style = viewStyleOf(style, undefined)
2279
2358
  return runViewSet(rest, opts, trust, { format, check, strict, out })
2280
2359
  }
2281
2360
 
@@ -2341,6 +2420,7 @@ function runView(argv: string[]): number {
2341
2420
 
2342
2421
  const report = view(srcs[0], {
2343
2422
  ...opts,
2423
+ style: viewStyleOf(style, opts.as ?? viewDefaultProfile(kind)),
2344
2424
  kind,
2345
2425
  path: files[0],
2346
2426
  roots,
package/src/hints.ts CHANGED
@@ -192,6 +192,8 @@ const hints: Record<string, string> = {
192
192
  view_kind_unknown: 'The figure kind is not one the verb draws. The kinds are tree, matrix,\ngraph, layer, sets, layers, ladder and poset; the note lists them.',
193
193
 
194
194
  view_profile_unknown: 'The figure kind does not render into the profile asked for: there is no\ntext form of a node-link drawing and no Mermaid form of a matrix. The\nnote lists the profiles the kind declares; the first is its default.',
195
+ view_style_profile: 'Each profile has ONE way to carry the meaning of a figure\'s marks:\nSGR escapes for text, CSS classes for svg. Asking for the other one is\na usage error rather than a silent no-op. `none` works everywhere.',
196
+ view_style_unknown: 'The styles are none, ansi and css, plus `auto` at the command line,\nwhich the command resolves before the library runs: whether the\ndestination is a terminal is not something a library can see.',
195
197
 
196
198
  view_rows_exceeded: 'The figure has more rows than --max-rows allows. This is a REFUSAL,\nnot a truncation: a view that quietly omits things is the failure the\nverb exists to avoid. Narrow the figure with --at or --relation, or\nraise the limit.',
197
199
 
@@ -509,6 +511,8 @@ const codeClasses: Record<string, string> = {
509
511
  view_relation_unknown: 'reference',
510
512
  view_kind_unknown: 'reference',
511
513
  view_profile_unknown: 'reference',
514
+ view_style_profile: 'reference',
515
+ view_style_unknown: 'reference',
512
516
  view_rows_exceeded: 'budget',
513
517
  view_line_break: 'parse',
514
518
  view_relation_ambiguous: 'reference',
package/src/mcp.ts CHANGED
@@ -543,15 +543,30 @@ const TOOLS: ToolDef[] = [
543
543
  type: 'string',
544
544
  description: 'sets: the full element domain (optional)',
545
545
  },
546
+ depth: {
547
+ type: 'integer',
548
+ description: 'doc: how many levels of key to draw (default 3)',
549
+ },
546
550
  maxRows: {
547
551
  type: 'integer',
548
552
  description: 'Refuse a figure above this many rows (default 60)',
549
553
  },
554
+ style: {
555
+ type: 'string',
556
+ description:
557
+ 'How the figure carries the meaning of its marks: none, ansi ' +
558
+ '(SGR escapes, text only) or css (classes and the embedded ' +
559
+ 'stylesheet, svg only). Absent means the profile\'s own ' +
560
+ 'default -- svg keeps its stylesheet, everything else has no ' +
561
+ 'mechanism. There is no auto here: resolving it needs a ' +
562
+ 'terminal, which a server does not have (optional)',
563
+ },
550
564
  },
551
565
  required: ['source'],
552
566
  docs: ['source'],
553
567
  check: (a) => {
554
- const kinds = ['tree', 'matrix', 'graph', 'layer', 'sets', 'layers', 'ladder']
568
+ const kinds = ['doc', 'tree', 'matrix', 'graph', 'layer', 'sets',
569
+ 'layers', 'ladder']
555
570
  if (null != a.kind && !kinds.includes(a.kind)) {
556
571
  return `kind must be one of ${kinds.join(', ')}, not ${JSON.stringify(a.kind)}`
557
572
  }
@@ -563,6 +578,10 @@ const TOOLS: ToolDef[] = [
563
578
  if (null != a.edges && !edges.includes(a.edges)) {
564
579
  return `edges must be one of ${edges.join(', ')}, not ${JSON.stringify(a.edges)}`
565
580
  }
581
+ const styles = ['none', 'ansi', 'css']
582
+ if (null != a.style && !styles.includes(a.style)) {
583
+ return `style must be one of ${styles.join(', ')}, not ${JSON.stringify(a.style)}`
584
+ }
566
585
  return undefined
567
586
  },
568
587
  refuse: (a, finding) =>
@@ -586,7 +605,9 @@ const TOOLS: ToolDef[] = [
586
605
  sets: null == a.sets ? undefined : str(a.sets),
587
606
  member: null == a.member ? undefined : str(a.member),
588
607
  universe: null == a.universe ? undefined : str(a.universe),
608
+ depth: 'number' === typeof a.depth ? a.depth : undefined,
589
609
  maxRows: 'number' === typeof a.maxRows ? a.maxRows : undefined,
610
+ style: null == a.style ? undefined : a.style,
590
611
  }),
591
612
  },
592
613
  {
package/src/std.ts CHANGED
@@ -87,7 +87,7 @@ view: {
87
87
  # it belongs; everything else narrows the drawing, and each option
88
88
  # belongs to the kinds that read it.
89
89
  Figure: type({
90
- kind: tree | matrix | graph | layer | sets | layers | ladder
90
+ kind: doc | tree | matrix | graph | layer | sets | layers | ladder
91
91
  out: string
92
92
 
93
93
  # Every kind.
@@ -95,6 +95,9 @@ view: {
95
95
  at?: string
96
96
  maxRows?: integer & min(0)
97
97
 
98
+ # doc: how many levels of key to draw.
99
+ depth?: integer & min(0)
100
+
98
101
  # tree, matrix, layer: the relation drawn. graph: the predicates
99
102
  # kept. tree: the subtrees drawn.
100
103
  relation?: string
package/src/vet.ts CHANGED
@@ -573,7 +573,11 @@ export function anchorAt(root: any, at: string): Val | undefined {
573
573
 
574
574
 
575
575
  // The container inside a settled sizing residue, or the value itself.
576
- function throughResidue(v: any): any {
576
+ // EXPORTED for the `doc` figure, which walks the same shape the anchor
577
+ // does: a list still carrying a `unique()` is a list, and a drawing
578
+ // that stopped at the residue would omit keys the document plainly
579
+ // has.
580
+ export function throughResidue(v: any): any {
577
581
  return sizingResidue(v)?.bag ?? v
578
582
  }
579
583