@textui/widgets 0.7.0 → 0.9.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.
Files changed (86) hide show
  1. package/dist/control/text-area.d.ts +2 -0
  2. package/dist/control/text-area.d.ts.map +1 -1
  3. package/dist/control/text-area.js +16 -8
  4. package/dist/control/text-input.d.ts.map +1 -1
  5. package/dist/control/text-input.js +6 -0
  6. package/dist/data/code-viewer.d.ts +2 -0
  7. package/dist/data/code-viewer.d.ts.map +1 -1
  8. package/dist/data/code-viewer.js +9 -1
  9. package/dist/data/index.d.ts +9 -0
  10. package/dist/data/index.d.ts.map +1 -1
  11. package/dist/data/index.js +8 -2
  12. package/dist/data/list.d.ts +9 -0
  13. package/dist/data/list.d.ts.map +1 -1
  14. package/dist/data/list.js +20 -3
  15. package/dist/data/markdown-view.d.ts.map +1 -1
  16. package/dist/data/markdown-view.js +4 -1
  17. package/dist/data/pagination.d.ts.map +1 -1
  18. package/dist/data/pagination.js +4 -1
  19. package/dist/data/tree.d.ts +2 -0
  20. package/dist/data/tree.d.ts.map +1 -1
  21. package/dist/data/tree.js +8 -2
  22. package/dist/display/card.d.ts.map +1 -1
  23. package/dist/display/card.js +12 -0
  24. package/dist/display/color-text.d.ts +13 -1
  25. package/dist/display/color-text.d.ts.map +1 -1
  26. package/dist/display/color-text.js +28 -6
  27. package/dist/display/font-text.d.ts +72 -0
  28. package/dist/display/font-text.d.ts.map +1 -0
  29. package/dist/display/font-text.js +35 -0
  30. package/dist/display/fonts.d.ts +138 -0
  31. package/dist/display/fonts.d.ts.map +1 -0
  32. package/dist/display/fonts.js +893 -0
  33. package/dist/display/gard.d.ts +26 -0
  34. package/dist/display/gard.d.ts.map +1 -0
  35. package/dist/display/gard.js +479 -0
  36. package/dist/display/index.d.ts +3 -0
  37. package/dist/display/index.d.ts.map +1 -1
  38. package/dist/display/index.js +7 -0
  39. package/dist/display/key-value.d.ts +25 -5
  40. package/dist/display/key-value.d.ts.map +1 -1
  41. package/dist/display/key-value.js +13 -3
  42. package/dist/display/marquee.d.ts.map +1 -1
  43. package/dist/display/marquee.js +16 -3
  44. package/dist/display/pagga.d.ts +29 -0
  45. package/dist/display/pagga.d.ts.map +1 -0
  46. package/dist/display/pagga.js +101 -0
  47. package/dist/display/pattern.d.ts +117 -0
  48. package/dist/display/pattern.d.ts.map +1 -0
  49. package/dist/display/pattern.js +257 -0
  50. package/dist/display/spinner.d.ts +2 -0
  51. package/dist/display/spinner.d.ts.map +1 -1
  52. package/dist/display/spinner.js +3 -2
  53. package/dist/navigation/menu.d.ts +11 -0
  54. package/dist/navigation/menu.d.ts.map +1 -1
  55. package/dist/navigation/menu.js +22 -9
  56. package/dist/navigation/tabs.d.ts +2 -0
  57. package/dist/navigation/tabs.d.ts.map +1 -1
  58. package/dist/navigation/tabs.js +12 -2
  59. package/dist/overlay/command-palette.d.ts.map +1 -1
  60. package/dist/overlay/command-palette.js +18 -6
  61. package/dist/shells/workbench-shell.d.ts.map +1 -1
  62. package/dist/shells/workbench-shell.js +5 -1
  63. package/package.json +2 -2
  64. package/src/control/text-area.ts +18 -7
  65. package/src/control/text-input.ts +6 -0
  66. package/src/data/code-viewer.ts +10 -1
  67. package/src/data/index.ts +9 -2
  68. package/src/data/list.ts +21 -3
  69. package/src/data/markdown-view.ts +7 -4
  70. package/src/data/pagination.ts +4 -1
  71. package/src/data/tree.ts +9 -2
  72. package/src/display/card.ts +12 -0
  73. package/src/display/color-text.ts +33 -10
  74. package/src/display/font-text.tsx +94 -0
  75. package/src/display/fonts.ts +990 -0
  76. package/src/display/gard.ts +475 -0
  77. package/src/display/index.ts +7 -0
  78. package/src/display/key-value.ts +44 -6
  79. package/src/display/marquee.ts +15 -3
  80. package/src/display/pagga.ts +102 -0
  81. package/src/display/pattern.tsx +427 -0
  82. package/src/display/spinner.ts +5 -2
  83. package/src/navigation/menu.ts +32 -9
  84. package/src/navigation/tabs.ts +13 -2
  85. package/src/overlay/command-palette.ts +13 -2
  86. package/src/shells/workbench-shell.ts +5 -1
@@ -29,11 +29,24 @@ export const Marquee = defineComponent('Marquee', (props) => {
29
29
  // at rather than at the rate it asked for.
30
30
  const [frame, setFrame] = useState(0);
31
31
  const seen = useRef(0);
32
- const from = useRef(0);
33
- useTicker((at) => { seen.current = at; setFrame(at - from.current); }, { fps, enabled: sliding });
32
+ // `null` until the first tick of a run, rather than 0.
33
+ //
34
+ // The ticker counts from the application's first frame, not from this
35
+ // component's, so a marquee that starts at frame 0 and subtracts a `from` of
36
+ // 0 begins at whatever number the application happens to be on - and a
37
+ // label scrolled to a cell nothing had travelled to yet is what opening a
38
+ // menu looked like. The effect below resets this, but it runs *after* the
39
+ // first tick, so the first tick has to be the one that starts the count.
40
+ const from = useRef(null);
41
+ useTicker((at) => {
42
+ seen.current = at;
43
+ if (from.current === null)
44
+ from.current = at;
45
+ setFrame(at - from.current);
46
+ }, { fps, enabled: sliding });
34
47
  // Back to the start whenever it stops or the words change, so the row you
35
48
  // leave is the row you saw when you arrived at it.
36
- useEffect(() => { from.current = seen.current; setFrame(0); }, [sliding, content]);
49
+ useEffect(() => { from.current = null; setFrame(0); }, [sliding, content]);
37
50
  if (!sliding)
38
51
  return h('text', { content, truncate: 'end', ...rest });
39
52
  const perCell = Math.max(1, Math.round(fps / speed));
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The `pagga` table, transcribed.
3
+ *
4
+ * Three rows of half cells on a `░` ground - which is the difference between
5
+ * this and `half`, and the whole reason for it. `half` draws letters and
6
+ * leaves the space around them empty; this draws them *on* something, so the
7
+ * banner is a block of texture with the letters knocked out of it rather than
8
+ * strokes floating on the terminal's background.
9
+ *
10
+ * That ground is why the glyphs are four columns rather than three. The first
11
+ * column of every one is the `░` gutter, so the letters butt together and the
12
+ * ground runs unbroken between them - and why the space is a glyph of its own
13
+ * rather than a gap, because a gap would be a hole in it.
14
+ *
15
+ * One case: the source has one set of letters and lowercase folds to it.
16
+ *
17
+ * The punctuation comes from two places and the table says which is which. The
18
+ * marks above the divider are transcribed - and the brackets among them took a
19
+ * correction, because the source lists three pairs with nothing saying which is
20
+ * which and shape alone does not settle it: the square-cornered pair reads as
21
+ * `(` `)` just as happily as `[` `]`, and it was the wrong one of the two.
22
+ *
23
+ * The marks below it are drawn to match, because the source has none of them
24
+ * at all - no full stop, no comma, no slash. Which is worth knowing when one
25
+ * of them looks out of place: it is not a transcription error, it is a
26
+ * judgement, and a paste would replace it.
27
+ */
28
+ export declare const PAGGA: Record<string, string[]>;
29
+ //# sourceMappingURL=pagga.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagga.d.ts","sourceRoot":"","sources":["../../src/display/pagga.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CA0E1C,CAAC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * The `pagga` table, transcribed.
3
+ *
4
+ * Three rows of half cells on a `░` ground - which is the difference between
5
+ * this and `half`, and the whole reason for it. `half` draws letters and
6
+ * leaves the space around them empty; this draws them *on* something, so the
7
+ * banner is a block of texture with the letters knocked out of it rather than
8
+ * strokes floating on the terminal's background.
9
+ *
10
+ * That ground is why the glyphs are four columns rather than three. The first
11
+ * column of every one is the `░` gutter, so the letters butt together and the
12
+ * ground runs unbroken between them - and why the space is a glyph of its own
13
+ * rather than a gap, because a gap would be a hole in it.
14
+ *
15
+ * One case: the source has one set of letters and lowercase folds to it.
16
+ *
17
+ * The punctuation comes from two places and the table says which is which. The
18
+ * marks above the divider are transcribed - and the brackets among them took a
19
+ * correction, because the source lists three pairs with nothing saying which is
20
+ * which and shape alone does not settle it: the square-cornered pair reads as
21
+ * `(` `)` just as happily as `[` `]`, and it was the wrong one of the two.
22
+ *
23
+ * The marks below it are drawn to match, because the source has none of them
24
+ * at all - no full stop, no comma, no slash. Which is worth knowing when one
25
+ * of them looks out of place: it is not a transcription error, it is a
26
+ * judgement, and a paste would replace it.
27
+ */
28
+ export const PAGGA = {
29
+ 0: ["░▄▀▄", "░█/█", "░░▀░"],
30
+ 1: ["░▀█░", "░░█░", "░▀▀▀"],
31
+ 2: ["░▀▀▄", "░▄▀░", "░▀▀▀"],
32
+ 3: ["░▀▀█", "░░▀▄", "░▀▀░"],
33
+ 4: ["░█░█", "░░▀█", "░░░▀"],
34
+ 5: ["░█▀▀", "░▀▀▄", "░▀▀░"],
35
+ 6: ["░▄▀▀", "░█▀▄", "░░▀░"],
36
+ 7: ["░▀▀█", "░▄▀░", "░▀░░"],
37
+ 8: ["░▄▀▄", "░▄▀▄", "░░▀░"],
38
+ 9: ["░▄▀▄", "░░▀█", "░▀▀░"],
39
+ A: ["░█▀█", "░█▀█", "░▀░▀"],
40
+ B: ["░█▀▄", "░█▀▄", "░▀▀░"],
41
+ C: ["░█▀▀", "░█░░", "░▀▀▀"],
42
+ D: ["░█▀▄", "░█░█", "░▀▀░"],
43
+ E: ["░█▀▀", "░█▀▀", "░▀▀▀"],
44
+ F: ["░█▀▀", "░█▀▀", "░▀░░"],
45
+ G: ["░█▀▀", "░█░█", "░▀▀▀"],
46
+ H: ["░█░█", "░█▀█", "░▀░▀"],
47
+ I: ["░▀█▀", "░░█░", "░▀▀▀"],
48
+ J: ["░▀▀█", "░░░█", "░▀▀░"],
49
+ K: ["░█░█", "░█▀▄", "░▀░▀"],
50
+ L: ["░█░░", "░█░░", "░▀▀▀"],
51
+ M: ["░█▄█", "░█░█", "░▀░▀"],
52
+ N: ["░█▀█", "░█░█", "░▀░▀"],
53
+ O: ["░█▀█", "░█░█", "░▀▀▀"],
54
+ P: ["░█▀█", "░█▀▀", "░▀░░"],
55
+ Q: ["░▄▀▄", "░█\\█", "░░▀\\"],
56
+ R: ["░█▀▄", "░█▀▄", "░▀░▀"],
57
+ S: ["░█▀▀", "░▀▀█", "░▀▀▀"],
58
+ T: ["░▀█▀", "░░█░", "░░▀░"],
59
+ U: ["░█░█", "░█░█", "░▀▀▀"],
60
+ V: ["░█░█", "░▀▄▀", "░░▀░"],
61
+ W: ["░█░█", "░█▄█", "░▀░▀"],
62
+ X: ["░█░█", "░▄▀▄", "░▀░▀"],
63
+ Y: ["░█░█", "░░█░", "░░▀░"],
64
+ Z: ["░▀▀█", "░▄▀░", "░▀▀▀"],
65
+ "!": ["░█", "░▀", "░▀"],
66
+ "?": ["░▄▀▄", "░█▀▀", "░░▀░"],
67
+ "#": ["░▄█▄█▄", "░▄█▄█▄", "░░▀░▀░"],
68
+ "$": ["░▄█▀", "░▀██", "░▀▀░"],
69
+ "%": ["░▀░█", "░▄▀░", "░▀░▀"],
70
+ "&": ["░▄▀░", "░▄█▀", "░░▀▀"],
71
+ "*": ["░▄░▄", "░▄█▄", "░▄▀▄"],
72
+ "[": ["░█▀░", "░█░░", "░▀▀░"],
73
+ "]": ["░▀█░", "░░█░", "░▀▀░"],
74
+ "_": ["░░░░", "░░░░", "░▀▀▀"],
75
+ "+": ["░░░░", "░▄█▄", "░░▀░"],
76
+ "-": ["░░░░", "░▄▄▄", "░░░░"],
77
+ "=": ["░░░░", "░▀▀▀", "░▀▀▀"],
78
+ "\"": ["░▀░▀", "░░░░", "░░░░"],
79
+ "'": ["░▀", "░░", "░░"],
80
+ "(": ["░▄▀░", "░█░░", "░░▀░"],
81
+ ")": ["░▀▄░", "░░█░", "░▀░░"],
82
+ "{": ["░░█▀", "░▀▄░", "░░▀▀"],
83
+ "}": ["░▀█░", "░░▄▀", "░▀▀░"],
84
+ "<": ["░░▄▀", "░▀▄░", "░░░▀"],
85
+ ">": ["░▀▄░", "░░▄▀", "░▀░░"],
86
+ " ": ["░░░░", "░░░░", "░░░░"],
87
+ // Drawn to match, not transcribed: the source has none of these. A period
88
+ // is the top half of the last cell, sitting on the line, and a comma is the
89
+ // bottom half of it, hanging below - which is what those two marks are.
90
+ ".": ["░░", "░░", "░▀"],
91
+ ",": ["░░", "░░", "░▄"],
92
+ ":": ["░░", "░▀", "░▀"],
93
+ ";": ["░░", "░▀", "░▄"],
94
+ "/": ["░░▄▀", "░▄▀░", "░▀░░"],
95
+ "\\": ["░▀▄░", "░░▀▄", "░░░▀"],
96
+ "@": ["░▄▀▄", "░█▄█", "░░▀▀"],
97
+ "^": ["░▄▀▄", "░░░░", "░░░░"],
98
+ "`": ["░▀▄", "░░░", "░░░"],
99
+ "|": ["░█", "░█", "░▀"],
100
+ "~": ["░░░░", "░▄▀▄", "░░░░"],
101
+ };
@@ -0,0 +1,117 @@
1
+ import type { BoxProps, RenderOutput } from '@textui/core';
2
+ import type { Ink } from './color-text.js';
3
+ /**
4
+ * A tile, repeated.
5
+ *
6
+ * A texture for a box, or a motif over one: the tile is stamped across and
7
+ * down until the box runs out, and `asBackground` (the default) or `asOverlay`
8
+ * says which side of the children it lands on.
9
+ *
10
+ * The two modes are document order and not much else. Painting walks the tree
11
+ * in the order components were written, so a layer emitted before the children
12
+ * ends up under them and one emitted after ends up over them. `zIndex` looks
13
+ * like it should decide this and does not: it sorts the layout boxes, while
14
+ * `paintTree` walks the instance children, so the property is inert for
15
+ * painting today. Relying on order is what actually works.
16
+ */
17
+ export interface PatternProps extends BoxProps {
18
+ /** The tile. One string per row, or one string with newlines in it. */
19
+ tile: string | string[];
20
+ /**
21
+ * The tile to draw on a terminal that cannot manage the first one.
22
+ *
23
+ * A pattern is the one component whose whole content is glyphs, so it is the
24
+ * one that breaks worst on an `unicode: 'ascii'` terminal - and unlike a
25
+ * border, the library cannot guess a fallback for a tile it has never seen.
26
+ * Whoever picks the glyphs picks the substitute.
27
+ */
28
+ ascii?: string | string[];
29
+ /**
30
+ * Repeats across.
31
+ *
32
+ * Unset or `0` draws the tile once. `-1` repeats until the box runs out.
33
+ * A positive number draws that many, and the box still clips it.
34
+ */
35
+ x?: number;
36
+ /** Repeats down. Same rules as `x`. */
37
+ y?: number;
38
+ /** Stop short of the box, in cells. Unset means the box decides. */
39
+ limit?: {
40
+ width?: number;
41
+ height?: number;
42
+ };
43
+ /**
44
+ * Cells added after each copy, before the next one starts.
45
+ *
46
+ * Unset is flush, which is what a texture wants. Spacing turns the same tile
47
+ * into a motif with air around it - the difference between wrapping paper
48
+ * and a wallpaper: `10 + 10 = 20`, and `spacing.x = 5` makes it `25`.
49
+ *
50
+ * Not `gap`. A box already has one, it means the space between *children*,
51
+ * and a component that used the same word for the space between copies of
52
+ * its own tile would be two different distances under one name.
53
+ */
54
+ spacing?: {
55
+ x?: number;
56
+ y?: number;
57
+ };
58
+ /**
59
+ * Up to this many extra cells between copies, chosen at random per step.
60
+ *
61
+ * The standard name for it: a jitter is a random displacement applied to
62
+ * positions that would otherwise be regular, and breaking a lattice is what
63
+ * it is for. Not a deviation, which in statistics is a spread about a mean,
64
+ * and not a factor, which is what it multiplied before it was a limit.
65
+ *
66
+ * A limit, not a factor: `jitter.x = 6` is "somewhere between flush and six
67
+ * cells further on", dealt again for every step. Unset or `0` is the grid,
68
+ * every copy exactly a tile from the last, which is what the default
69
+ * preserves.
70
+ *
71
+ * It stacks with `spacing` rather than replacing it. Spacing is the air you
72
+ * always want and this is how much more of it is left to chance, so
73
+ * `spacing.x = 4, jitter.x = 6` steps by a tile plus four to ten.
74
+ *
75
+ * A jitter on `x` also stops the copies lining up into columns - see
76
+ * `offsets`, and the screenshot that made it necessary.
77
+ */
78
+ jitter?: {
79
+ x?: number;
80
+ y?: number;
81
+ };
82
+ /**
83
+ * The deal, when there is a jitter.
84
+ *
85
+ * A pattern re-renders whenever its box changes, and one that reached for
86
+ * `Math.random()` while painting would shuffle itself on every frame - a
87
+ * texture that crawls. The seed makes the scatter a function of the props,
88
+ * so the same pattern is the same pattern until somebody changes the number.
89
+ */
90
+ seed?: number;
91
+ /**
92
+ * Cells holding this character are left unpainted, so whatever is behind
93
+ * shows through. `null` paints every cell, spaces included.
94
+ */
95
+ transparent?: string | null;
96
+ /**
97
+ * Colour the tile cell by cell, the way [`ColorText`](color-text.md) colours
98
+ * text: a ramp between stops, a palette walked in runs, or a function of the
99
+ * cell. The coordinates are the pattern's own - `col` and `line` count cells
100
+ * of the box it fills, not of one copy of the tile - so `{ gradient }` runs
101
+ * across the whole texture and `{ cycle, every }` walks it.
102
+ *
103
+ * Stated, the tile is painted on a canvas rather than drawn as text, and a
104
+ * canvas is not told what it was nested in: every cell takes this
105
+ * component's own `fg` where the ink declines one, not the colour of the row
106
+ * it happens to sit in. That is the trade `ColorText` makes for the same
107
+ * reason, and it is the only difference between an inked tile and a plain
108
+ * one.
109
+ */
110
+ ink?: Ink;
111
+ /** Paint under the children. The default, and what a texture wants. */
112
+ asBackground?: boolean;
113
+ /** Paint over the children instead. */
114
+ asOverlay?: boolean;
115
+ }
116
+ export declare const Pattern: (props: PatternProps) => RenderOutput;
117
+ //# sourceMappingURL=pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../src/display/pattern.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAA+B,YAAY,EAAE,MAAM,cAAc,CAAC;AAExF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,uEAAuE;IACvE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B;;;;;OAKG;IACH,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,uCAAuC;IACvC,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,oEAAoE;IACpE,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;;;;;;;;;OAaG;IACH,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,uEAAuE;IACvE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uCAAuC;IACvC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AA8HD,eAAO,MAAM,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,YA+L9C,CAAC"}
@@ -0,0 +1,257 @@
1
+ import { jsx as _jsx } from "@textui/core/jsx-runtime";
2
+ import { defineComponent, useCapabilities, useMeasure } from '@textui/core';
3
+ import { inkCellStyle, inkOwnStyle, painterOf } from './color-text.js';
4
+ /**
5
+ * The tile as equal-length rows. A ragged tile pads rather than tears.
6
+ *
7
+ * Only the blank rows at the ends go - those are how a string written across
8
+ * several lines begins and ends. A blank row in the middle is a row of
9
+ * transparent cells somebody drew on purpose, and dropping it silently
10
+ * shortens their tile.
11
+ */
12
+ function rowsOf(tile) {
13
+ const rows = Array.isArray(tile) ? [...tile] : tile.split('\n');
14
+ while (rows.length > 0 && rows[0] === '')
15
+ rows.shift();
16
+ while (rows.length > 0 && rows[rows.length - 1] === '')
17
+ rows.pop();
18
+ const width = Math.max(0, ...rows.map((row) => [...row].length));
19
+ return rows.map((row) => row.padEnd(width, ' '));
20
+ }
21
+ /**
22
+ * How many copies to draw along one axis.
23
+ *
24
+ * Unset and zero mean the same thing - once, no repetition - because "repeat
25
+ * it zero times" and "do not repeat it" are the same request, and treating
26
+ * zero as *nothing at all* makes an unset prop and a computed one behave
27
+ * differently for no reason anyone would want.
28
+ *
29
+ * `-1` is "as many as fit", which with a gap or a deviation is not arithmetic
30
+ * any more - it is however many the walk gets through. The count is an upper
31
+ * bound there rather than the answer.
32
+ */
33
+ function repeats(count, tile, room) {
34
+ if (count === undefined || count === 0)
35
+ return 1;
36
+ if (count < 0)
37
+ return tile > 0 ? Math.ceil(room / tile) : 1;
38
+ return count;
39
+ }
40
+ /**
41
+ * A small deterministic generator, so a deviation is a deal rather than a
42
+ * shuffle. `mulberry32`: the point is repeatability, not statistics.
43
+ */
44
+ function random(seed) {
45
+ let state = seed >>> 0;
46
+ return () => {
47
+ state = (state + 0x6d2b79f5) >>> 0;
48
+ let t = state;
49
+ t = Math.imul(t ^ (t >>> 15), t | 1);
50
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
51
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
52
+ };
53
+ }
54
+ /**
55
+ * Where each copy starts along one axis.
56
+ *
57
+ * The step is a tile, plus whatever the gap and the deviation ask for:
58
+ *
59
+ * - neither - `tile`, which is the grid, and exactly what this drew before
60
+ * either of them existed
61
+ * - spacing - `tile + spacing`, the same grid with air in it
62
+ * - a deviation - `tile + rand * deviation * tile`, so `1` can skip a whole
63
+ * tile and land anywhere in the one after
64
+ * - both - `tile + rand * deviation * spacing`, because once there is a
65
+ * spacing it is the spacing being shared out rather than the tile
66
+ *
67
+ * A jitter also moves where the walk *starts*, backwards, by up to a step.
68
+ *
69
+ * Without that, a tile as wide as the box is drawn once at zero and the
70
+ * jitter has nothing to act on - there is no second copy for the step to
71
+ * be different from, so every seed deals the same picture. It is the case a
72
+ * sparse tile is most likely to be in: a big tile with two marks on it is
73
+ * somebody scattering by hand, and the whole point of the seed there is that
74
+ * the scatter moves.
75
+ *
76
+ * Backwards rather than forwards, so the phase shows as a different slice of
77
+ * the pattern rather than as a blank margin down the left. The walk then needs
78
+ * one more copy to reach the far edge, which is why `fill` extends the count.
79
+ */
80
+ function offsets(count, tile, room, spacing,
81
+ /** Up to this many extra cells per step. */
82
+ jitter, next,
83
+ /** Repeating to fill, rather than drawing a stated number of copies. */
84
+ fill) {
85
+ if (tile <= 0)
86
+ return [];
87
+ const out = [];
88
+ const step = tile + spacing;
89
+ // Only when filling. A caller who asked for three copies put them there;
90
+ // sliding those off the edge would be answering a different question.
91
+ const phase = fill && jitter > 0
92
+ ? -Math.round(next() * (step + jitter))
93
+ : 0;
94
+ let at = phase;
95
+ const total = phase < 0 ? count + 1 : count;
96
+ for (let i = 0; i < total && at < room; i++) {
97
+ out.push(at);
98
+ // A limit rather than a factor: the extra is somewhere in `0..jitter`,
99
+ // dealt again for every step.
100
+ at += step + (jitter > 0 ? Math.round(next() * jitter) : 0);
101
+ }
102
+ return out;
103
+ }
104
+ /** The runs of paintable cells in one row, so gaps stay see-through. */
105
+ function runs(row, transparent) {
106
+ const cells = [...row];
107
+ if (transparent === null)
108
+ return cells.length > 0 ? [{ at: 0, text: row }] : [];
109
+ const out = [];
110
+ let start = -1;
111
+ for (let i = 0; i <= cells.length; i++) {
112
+ const paint = i < cells.length && cells[i] !== transparent;
113
+ if (paint && start < 0)
114
+ start = i;
115
+ else if (!paint && start >= 0) {
116
+ out.push({ at: start, text: cells.slice(start, i).join('') });
117
+ start = -1;
118
+ }
119
+ }
120
+ return out;
121
+ }
122
+ export const Pattern = defineComponent('Pattern', (props) => {
123
+ const { tile, ascii, x, y, limit, transparent = ' ', spacing, jitter, seed = 1, ink, asOverlay, asBackground: _asBackground, children, ...rest } = props;
124
+ const capabilities = useCapabilities();
125
+ // The box this pattern was given. Zero on the first pass, real on the
126
+ // second - which is why `-1` needs a frame to find its extent.
127
+ const measured = useMeasure();
128
+ const rows = rowsOf(capabilities.unicode === 'ascii' && ascii !== undefined ? ascii : tile);
129
+ const tileWidth = rows[0] ? [...rows[0]].length : 0;
130
+ const tileHeight = rows.length;
131
+ const room = {
132
+ width: Math.min(measured.width, limit?.width ?? Number.POSITIVE_INFINITY),
133
+ height: Math.min(measured.height, limit?.height ?? Number.POSITIVE_INFINITY),
134
+ };
135
+ const across = repeats(x, tileWidth, room.width);
136
+ const down = repeats(y, tileHeight, room.height);
137
+ // One generator for both axes, drawn from in a fixed order, so neither
138
+ // depends on the box's size.
139
+ const next = random(seed);
140
+ const lines = offsets(down, tileHeight, room.height, spacing?.y ?? 0, jitter?.y ?? 0, next, (y ?? 0) < 0);
141
+ /*
142
+ * A walk across for *each* row of copies, not one shared by all of them.
143
+ *
144
+ * This is what the difference between "irregular" and "not a lattice"
145
+ * turns out to be. With one set of column positions, every row starts its
146
+ * copies at the same offsets - so the copies still line up into columns,
147
+ * and a sky of stars drawn that way still has visible rows and columns no
148
+ * matter how uneven the spacing along each axis is. You can draw the grid
149
+ * on a screenshot with a ruler, which is exactly what happened.
150
+ *
151
+ * Re-dealing the walk per row costs one pass each and breaks the
152
+ * alignment, because row two's copies are nowhere near row one's.
153
+ * Without a jitter there is nothing to re-deal, so the walk is
154
+ * computed once and shared - and the output is the grid it always was.
155
+ */
156
+ const shared = (jitter?.x ?? 0) === 0
157
+ ? offsets(across, tileWidth, room.width, spacing?.x ?? 0, 0, next, (x ?? 0) < 0)
158
+ : null;
159
+ const columnsFor = () => shared ?? offsets(across, tileWidth, room.width, spacing?.x ?? 0, jitter?.x ?? 0, next, (x ?? 0) < 0);
160
+ /*
161
+ * Every copy placed on its own, not as a cell of a grid.
162
+ *
163
+ * Re-dealing the walk per row breaks the columns; it does not break the
164
+ * rows, because every copy in one row still shares a `top`. A sky drawn
165
+ * that way still has marks lined up in bands - which is visible the
166
+ * moment somebody puts a ruler on a screenshot.
167
+ *
168
+ * So a copy also wobbles up or down by its own amount, out of the same
169
+ * budget as the vertical step. That is the last thing holding the lattice
170
+ * together, and with it gone the placements are a scatter that happens to
171
+ * have been generated in reading order.
172
+ */
173
+ const wobble = jitter?.y ?? 0;
174
+ const placements = lines.flatMap((top) => columnsFor().map((left) => ({
175
+ left,
176
+ top: wobble > 0 ? top + Math.round(next() * wobble) - Math.round(wobble / 2) : top,
177
+ })));
178
+ const widest = Math.max(0, ...placements.map(({ left }) => left + tileWidth));
179
+ const last = {
180
+ x: Math.max(0, widest),
181
+ y: Math.max(0, (lines[lines.length - 1] ?? 0) + tileHeight),
182
+ };
183
+ const painted = {
184
+ width: Math.min(last.x, room.width),
185
+ height: Math.min(last.y, room.height),
186
+ };
187
+ /*
188
+ * Stamped into a grid, then split into runs.
189
+ *
190
+ * The rows used to be built by repeating the tile's own string, which is
191
+ * shorter and only works while every copy is exactly a tile from the last.
192
+ * With a gap or a deviation the copies are at arbitrary offsets, so the
193
+ * grid is composed first and read off afterwards - and the reading is the
194
+ * same `runs` split as before, which is what keeps a transparent cell
195
+ * transparent and adjacent cells in one `text` rather than one apiece.
196
+ *
197
+ * With neither prop set the offsets come out as `0, tile, 2*tile, ...` and
198
+ * the composed rows are identical to what the repeat produced.
199
+ */
200
+ const cells = [];
201
+ if (tileWidth > 0 && painted.width > 0 && painted.height > 0) {
202
+ const blank = transparent ?? ' ';
203
+ const grid = Array.from({ length: painted.height }, () => Array.from({ length: painted.width }, () => blank));
204
+ for (const { top, left } of placements) {
205
+ for (let r = 0; r < tileHeight; r++) {
206
+ // A phased or wobbled copy starts above and to the left of the box,
207
+ // so part of it is outside. Those cells are simply not written; the
208
+ // rest of the copy still lands.
209
+ const row = grid[top + r];
210
+ if (top + r < 0 || !row)
211
+ continue;
212
+ const source = [...rows[r]];
213
+ for (let c = 0; c < tileWidth; c++) {
214
+ const at = left + c;
215
+ if (at >= 0 && at < painted.width)
216
+ row[at] = source[c];
217
+ }
218
+ }
219
+ }
220
+ if (ink === undefined) {
221
+ grid.forEach((row, y1) => {
222
+ for (const run of runs(row.join(''), transparent)) {
223
+ cells.push(_jsx("text", { position: "absolute", top: y1, left: run.at, content: run.text }, `${y1}:${run.at}`));
224
+ }
225
+ });
226
+ }
227
+ else {
228
+ // One cell at a time, because one colour at a time is the whole point
229
+ // of an ink. A cell the `transparent` character owns is skipped rather
230
+ // than painted over: it is not the tile's to draw.
231
+ cells.push(_jsx("canvas", { position: "absolute", top: 0, left: 0, width: painted.width, height: painted.height, draw: (surface, ctx) => {
232
+ const ownProps = props;
233
+ const base = inkCellStyle(inkOwnStyle(ownProps, ctx), ctx, props.link);
234
+ const paint = painterOf(ink, ctx);
235
+ for (let y1 = 0; y1 < grid.length; y1++) {
236
+ const row = grid[y1];
237
+ for (let x1 = 0; x1 < row.length; x1++) {
238
+ const char = row[x1];
239
+ if (transparent !== null && char === transparent)
240
+ continue;
241
+ const style = paint({
242
+ char, col: x1, line: y1, index: x1,
243
+ offset: y1 * painted.width + x1,
244
+ width: painted.width, height: painted.height,
245
+ blockWidth: painted.width,
246
+ });
247
+ surface.put(x1, y1, char, style ? { ...base, ...style } : base);
248
+ }
249
+ }
250
+ } }, "ink"));
251
+ }
252
+ }
253
+ // Absolute, so the pattern never pushes the children around, and hidden,
254
+ // so a partial tile at the edge is cut rather than allowed to overflow.
255
+ const layer = (_jsx("box", { position: "absolute", top: 0, left: 0, width: painted.width, height: painted.height, overflow: "hidden", children: cells }));
256
+ return (_jsx("box", { position: "relative", ...rest, children: asOverlay === true ? [children, layer] : [layer, children] }));
257
+ });
@@ -2,6 +2,8 @@ import type { BoxProps, SemanticVariant } from '@textui/core';
2
2
  export interface SpinnerProps extends BoxProps {
3
3
  label?: string;
4
4
  tone?: SemanticVariant;
5
+ frames?: string[];
5
6
  }
7
+ export declare const DEFAULT_FRAMES: string[];
6
8
  export declare const Spinner: (props: SpinnerProps) => import("@textui/core").RenderOutput;
7
9
  //# sourceMappingURL=spinner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/display/spinner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAI9D,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,eAAe,CAAC;CACxB;AAED,eAAO,MAAM,OAAO,8DAUlB,CAAC"}
1
+ {"version":3,"file":"spinner.d.ts","sourceRoot":"","sources":["../../src/display/spinner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAI9D,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,eAAO,MAAM,cAAc,UAAwB,CAAC;AAEpD,eAAO,MAAM,OAAO,8DAUlB,CAAC"}
@@ -1,9 +1,10 @@
1
1
  import { defineComponent, h, useFrame, useTheme } from '@textui/core';
2
2
  import { TONE as TONE_COLOR } from '../tone.js';
3
- export const Spinner = defineComponent('Spinner', ({ label, tone = 'accent', ...rest }) => {
3
+ export const DEFAULT_FRAMES = ['-', '\\', '|', '/'];
4
+ export const Spinner = defineComponent('Spinner', ({ label, tone = 'accent', frames: customFrames, ...rest }) => {
4
5
  const theme = useTheme();
5
6
  const frame = useFrame(10);
6
- const frames = theme.glyphs.spinner;
7
+ const frames = customFrames ?? theme.glyphs.spinner ?? DEFAULT_FRAMES;
7
8
  const glyph = frames[frame % frames.length] ?? frames[0] ?? '*';
8
9
  return h('box', { role: 'status', direction: 'row', gap: 1, ...rest }, h('text', { content: glyph, fg: TONE_COLOR[tone] }), label ? h('text', { content: label }) : null);
9
10
  });
@@ -54,6 +54,17 @@ export interface MenuProps extends BoxProps {
54
54
  * only follows.
55
55
  */
56
56
  interactive?: boolean;
57
+ /**
58
+ * What to say when nothing is selectable.
59
+ *
60
+ * A palette answers for an empty list itself: it knows *why* it is empty, and
61
+ * its own row can say which kind of nothing this is - nothing to choose,
62
+ * still asking, no match. Given one of its own it turns this off rather than
63
+ * putting both lines on the screen.
64
+ */
65
+ noMatch?: string | false;
57
66
  }
67
+ /** The name a theme styles a menu row under. See `LIST`. */
68
+ export declare const MENU = "Menu";
58
69
  export declare const Menu: (props: MenuProps) => import("@textui/core").RenderOutput;
59
70
  //# sourceMappingURL=menu.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../src/navigation/menu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAK9D,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,SAAU,SAAQ,QAAQ;IACzC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,QAAQ,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5C,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,eAAO,MAAM,IAAI,2DAmIf,CAAC"}
1
+ {"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../src/navigation/menu.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAK9D,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,SAAU,SAAQ,QAAQ;IACzC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,QAAQ,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5C,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAClC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC1B;AAED,4DAA4D;AAC5D,eAAO,MAAM,IAAI,SAAS,CAAC;AAE3B,eAAO,MAAM,IAAI,2DA8If,CAAC"}