@localix-ai/flowdown 0.0.0-stage → 0.5.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.
Files changed (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -2
  3. package/dist/browser/file-actions.js +80 -0
  4. package/dist/browser/file-actions.js.map +1 -0
  5. package/dist/core/block-facts.js +72 -0
  6. package/dist/core/block-facts.js.map +1 -0
  7. package/dist/core/blocks.d.ts +25 -0
  8. package/dist/core/blocks.js +464 -0
  9. package/dist/core/blocks.js.map +1 -0
  10. package/dist/core/bounded-cache.js +42 -0
  11. package/dist/core/bounded-cache.js.map +1 -0
  12. package/dist/core/continues.js +15 -0
  13. package/dist/core/continues.js.map +1 -0
  14. package/dist/core/dollars.d.ts +12 -0
  15. package/dist/core/dollars.js +88 -0
  16. package/dist/core/dollars.js.map +1 -0
  17. package/dist/core/fence-scan.js +62 -0
  18. package/dist/core/fence-scan.js.map +1 -0
  19. package/dist/core/file-tree-parse.d.ts +26 -0
  20. package/dist/core/file-tree-parse.js +51 -0
  21. package/dist/core/file-tree-parse.js.map +1 -0
  22. package/dist/core/forms-html.js +63 -0
  23. package/dist/core/forms-html.js.map +1 -0
  24. package/dist/core/forms.d.ts +28 -0
  25. package/dist/core/forms.js +23 -0
  26. package/dist/core/forms.js.map +1 -0
  27. package/dist/core/headings.d.ts +11 -0
  28. package/dist/core/headings.js +74 -0
  29. package/dist/core/headings.js.map +1 -0
  30. package/dist/core/heal.d.ts +11 -0
  31. package/dist/core/heal.js +220 -0
  32. package/dist/core/heal.js.map +1 -0
  33. package/dist/core/load-once.js +23 -0
  34. package/dist/core/load-once.js.map +1 -0
  35. package/dist/core/math-delimiters.js +33 -0
  36. package/dist/core/math-delimiters.js.map +1 -0
  37. package/dist/core/math-syntax.js +56 -0
  38. package/dist/core/math-syntax.js.map +1 -0
  39. package/dist/core/prose.d.ts +10 -0
  40. package/dist/core/prose.js +77 -0
  41. package/dist/core/prose.js.map +1 -0
  42. package/dist/core/remark-answer-blocks.d.ts +5 -0
  43. package/dist/core/remark-answer-blocks.js +172 -0
  44. package/dist/core/remark-answer-blocks.js.map +1 -0
  45. package/dist/core/remark-answer-containers.js +299 -0
  46. package/dist/core/remark-answer-containers.js.map +1 -0
  47. package/dist/core/sanitize-schema.d.ts +18 -0
  48. package/dist/core/sanitize-schema.js +62 -0
  49. package/dist/core/sanitize-schema.js.map +1 -0
  50. package/dist/core/syntax.d.ts +8 -0
  51. package/dist/core/syntax.js +32 -0
  52. package/dist/core/syntax.js.map +1 -0
  53. package/dist/dialect.d.ts +92 -0
  54. package/dist/dialect.js +176 -0
  55. package/dist/dialect.js.map +1 -0
  56. package/dist/document/fence.d.ts +25 -0
  57. package/dist/document/fence.js +102 -0
  58. package/dist/document/fence.js.map +1 -0
  59. package/dist/document/index.d.ts +46 -0
  60. package/dist/document/index.js +66 -0
  61. package/dist/document/index.js.map +1 -0
  62. package/dist/document/table.d.ts +11 -0
  63. package/dist/document/table.js +35 -0
  64. package/dist/document/table.js.map +1 -0
  65. package/dist/dom/index.d.ts +21 -0
  66. package/dist/dom/index.js +141 -0
  67. package/dist/dom/index.js.map +1 -0
  68. package/dist/dom/patch.js +104 -0
  69. package/dist/dom/patch.js.map +1 -0
  70. package/dist/element/index.d.ts +35 -0
  71. package/dist/element/index.js +64 -0
  72. package/dist/element/index.js.map +1 -0
  73. package/dist/engine/blocks.js +785 -0
  74. package/dist/engine/blocks.js.map +1 -0
  75. package/dist/engine/entities.js +83 -0
  76. package/dist/engine/entities.js.map +1 -0
  77. package/dist/engine/entity-core.js +9 -0
  78. package/dist/engine/entity-core.js.map +1 -0
  79. package/dist/engine/entity-table.js +9 -0
  80. package/dist/engine/entity-table.js.map +1 -0
  81. package/dist/engine/html.d.ts +7 -0
  82. package/dist/engine/html.js +207 -0
  83. package/dist/engine/html.js.map +1 -0
  84. package/dist/engine/index.d.ts +7 -0
  85. package/dist/engine/index.js +74 -0
  86. package/dist/engine/index.js.map +1 -0
  87. package/dist/engine/inlines.js +887 -0
  88. package/dist/engine/inlines.js.map +1 -0
  89. package/dist/engine/types.d.ts +109 -0
  90. package/dist/engine/url.d.ts +11 -0
  91. package/dist/engine/url.js +16 -0
  92. package/dist/engine/url.js.map +1 -0
  93. package/dist/engine/visit.d.ts +10 -0
  94. package/dist/engine/visit.js +29 -0
  95. package/dist/engine/visit.js.map +1 -0
  96. package/dist/highlight/highlight.d.ts +4 -0
  97. package/dist/highlight/highlight.js +144 -0
  98. package/dist/highlight/highlight.js.map +1 -0
  99. package/dist/highlight/index.d.ts +4 -0
  100. package/dist/highlight/index.js +18 -0
  101. package/dist/highlight/index.js.map +1 -0
  102. package/dist/html/index.d.ts +4 -0
  103. package/dist/html/index.js +544 -0
  104. package/dist/html/index.js.map +1 -0
  105. package/dist/index.d.ts +27 -0
  106. package/dist/index.js +13 -0
  107. package/dist/math/index.d.ts +17 -0
  108. package/dist/math/index.js +20 -0
  109. package/dist/math/index.js.map +1 -0
  110. package/dist/math/typeset.d.ts +12 -0
  111. package/dist/math/typeset.js +74 -0
  112. package/dist/math/typeset.js.map +1 -0
  113. package/dist/mermaid/diagram.d.ts +8 -0
  114. package/dist/mermaid/diagram.js +114 -0
  115. package/dist/mermaid/diagram.js.map +1 -0
  116. package/dist/mermaid/drawing.js +139 -0
  117. package/dist/mermaid/drawing.js.map +1 -0
  118. package/dist/mermaid/icons.js +16 -0
  119. package/dist/mermaid/icons.js.map +1 -0
  120. package/dist/mermaid/index.d.ts +13 -0
  121. package/dist/mermaid/index.js +3 -0
  122. package/dist/mermaid/loader.d.ts +5 -0
  123. package/dist/mermaid/loader.js +18 -0
  124. package/dist/mermaid/loader.js.map +1 -0
  125. package/dist/mermaid/look.d.ts +16 -0
  126. package/dist/mermaid/look.js +167 -0
  127. package/dist/mermaid/look.js.map +1 -0
  128. package/dist/mermaid/normalize-svg.js +44 -0
  129. package/dist/mermaid/normalize-svg.js.map +1 -0
  130. package/dist/mermaid/renderer.d.ts +22 -0
  131. package/dist/mermaid/renderer.js +132 -0
  132. package/dist/mermaid/renderer.js.map +1 -0
  133. package/dist/mermaid/viewer.js +309 -0
  134. package/dist/mermaid/viewer.js.map +1 -0
  135. package/dist/prompt/index.d.ts +19 -0
  136. package/dist/prompt/index.js +54 -0
  137. package/dist/prompt/index.js.map +1 -0
  138. package/dist/react/callout.d.ts +9 -0
  139. package/dist/react/callout.js +12 -0
  140. package/dist/react/callout.js.map +1 -0
  141. package/dist/react/code-block.d.ts +18 -0
  142. package/dist/react/code-block.js +94 -0
  143. package/dist/react/code-block.js.map +1 -0
  144. package/dist/react/context.d.ts +18 -0
  145. package/dist/react/context.js +17 -0
  146. package/dist/react/context.js.map +1 -0
  147. package/dist/react/draw.d.ts +2 -0
  148. package/dist/react/draw.js +17 -0
  149. package/dist/react/draw.js.map +1 -0
  150. package/dist/react/file-tree.d.ts +16 -0
  151. package/dist/react/file-tree.js +69 -0
  152. package/dist/react/file-tree.js.map +1 -0
  153. package/dist/react/growing-code.js +36 -0
  154. package/dist/react/growing-code.js.map +1 -0
  155. package/dist/react/helpers.d.ts +6 -0
  156. package/dist/react/helpers.js +17 -0
  157. package/dist/react/helpers.js.map +1 -0
  158. package/dist/react/icons.js +23 -0
  159. package/dist/react/icons.js.map +1 -0
  160. package/dist/react/labels.js +9 -0
  161. package/dist/react/labels.js.map +1 -0
  162. package/dist/react/markdown.d.ts +78 -0
  163. package/dist/react/markdown.js +206 -0
  164. package/dist/react/markdown.js.map +1 -0
  165. package/dist/react/render.d.ts +8 -0
  166. package/dist/react/render.js +41 -0
  167. package/dist/react/render.js.map +1 -0
  168. package/dist/react/scrolls.js +62 -0
  169. package/dist/react/scrolls.js.map +1 -0
  170. package/dist/react/table-rows.js +66 -0
  171. package/dist/react/table-rows.js.map +1 -0
  172. package/dist/react/table.js +22 -0
  173. package/dist/react/table.js.map +1 -0
  174. package/dist/react/tabs.d.ts +25 -0
  175. package/dist/react/tabs.js +58 -0
  176. package/dist/react/tabs.js.map +1 -0
  177. package/dist/react/use-smooth-text.d.ts +6 -0
  178. package/dist/react/use-smooth-text.js +157 -0
  179. package/dist/react/use-smooth-text.js.map +1 -0
  180. package/dist/react/use-stage.d.ts +9 -0
  181. package/dist/react/use-stage.js +57 -0
  182. package/dist/react/use-stage.js.map +1 -0
  183. package/dist/remark.d.ts +20 -0
  184. package/dist/remark.js +10 -0
  185. package/dist/render/factory.d.ts +15 -0
  186. package/dist/render/labels.d.ts +46 -0
  187. package/dist/render/labels.js +46 -0
  188. package/dist/render/labels.js.map +1 -0
  189. package/dist/render/tree.js +314 -0
  190. package/dist/render/tree.js.map +1 -0
  191. package/dist/render/view.d.ts +58 -0
  192. package/dist/render/view.js +306 -0
  193. package/dist/render/view.js.map +1 -0
  194. package/dist/render/views.d.ts +16 -0
  195. package/dist/render/views.js +261 -0
  196. package/dist/render/views.js.map +1 -0
  197. package/dist/render/vnode.d.ts +21 -0
  198. package/dist/render/vnode.js +194 -0
  199. package/dist/render/vnode.js.map +1 -0
  200. package/dist/static/index.d.ts +28 -0
  201. package/dist/static/index.js +27 -0
  202. package/dist/static/index.js.map +1 -0
  203. package/dist/stream.d.ts +12 -0
  204. package/dist/stream.js +3 -0
  205. package/dist/styles.css +1362 -0
  206. package/dist/tui/code.d.ts +5 -0
  207. package/dist/tui/code.js +240 -0
  208. package/dist/tui/code.js.map +1 -0
  209. package/dist/tui/index.d.ts +54 -0
  210. package/dist/tui/index.js +99 -0
  211. package/dist/tui/index.js.map +1 -0
  212. package/dist/tui/math.js +242 -0
  213. package/dist/tui/math.js.map +1 -0
  214. package/dist/tui/printable.d.ts +12 -0
  215. package/dist/tui/printable.js +31 -0
  216. package/dist/tui/printable.js.map +1 -0
  217. package/dist/tui/render.d.ts +12 -0
  218. package/dist/tui/render.js +524 -0
  219. package/dist/tui/render.js.map +1 -0
  220. package/dist/tui/style.d.ts +35 -0
  221. package/dist/tui/style.js +77 -0
  222. package/dist/tui/style.js.map +1 -0
  223. package/dist/tui/table.js +73 -0
  224. package/dist/tui/table.js.map +1 -0
  225. package/dist/tui/width.d.ts +2 -0
  226. package/dist/tui/width.js +133 -0
  227. package/dist/tui/width.js.map +1 -0
  228. package/dist/tui/wrap.d.ts +8 -0
  229. package/dist/tui/wrap.js +191 -0
  230. package/dist/tui/wrap.js.map +1 -0
  231. package/package.json +128 -4
@@ -0,0 +1,77 @@
1
+ // src/tui/style.ts
2
+ const PLAIN = {};
3
+ const BOLD = { bold: true };
4
+ const DIM = { dim: true };
5
+ /** Code, and a note. */
6
+ const CODE = { color: "cyan" };
7
+ /** A formula. */
8
+ const MATH = { italic: true };
9
+ /** What went well: a checked task, a tip, an argument for. */
10
+ const DONE = { color: "green" };
11
+ /** What needs care: a warning. */
12
+ const WAITING = { color: "yellow" };
13
+ /** What went wrong or argues against. */
14
+ const FAILED = { color: "red" };
15
+ /** `style` with what `over` sets laid over it. */
16
+ const patch = (style, over) => ({
17
+ ...style,
18
+ ...over
19
+ });
20
+ const SGR = [
21
+ ["bold", "1"],
22
+ ["dim", "2"],
23
+ ["italic", "3"],
24
+ ["underline", "4"],
25
+ ["strike", "9"]
26
+ ];
27
+ const FOREGROUND = {
28
+ red: "31",
29
+ green: "32",
30
+ yellow: "33",
31
+ cyan: "36",
32
+ magenta: "35"
33
+ };
34
+ /** The parameters of the escape sequence that turns `style` on; '' for plain text. */
35
+ function sgr(style) {
36
+ const codes = [];
37
+ for (const [field, code] of SGR) if (style[field]) codes.push(code);
38
+ if (style.color) codes.push(FOREGROUND[style.color]);
39
+ return codes.join(";");
40
+ }
41
+ /** A row as its text alone. */
42
+ const lineText = (line) => line.map((span) => span.text).join("");
43
+ /**
44
+ * Rows as text for a terminal, one per line. With `color`, each styled run is wrapped in the
45
+ * sequence that draws it and the one that resets it; without, the text is all there is.
46
+ */
47
+ function toAnsi(lines, color = true) {
48
+ let out = "";
49
+ for (let at = 0; at < lines.length; at++) {
50
+ if (at > 0) out += "\n";
51
+ for (const span of lines[at]) {
52
+ const codes = color ? sgr(span.style) : "";
53
+ out += codes ? `\u001b[${codes}m${span.text}\u001b[0m` : span.text;
54
+ }
55
+ }
56
+ return out;
57
+ }
58
+ /** A row with neighbouring runs of one style made one run. */
59
+ function merged(line) {
60
+ const out = [];
61
+ let key = "";
62
+ for (const span of line) {
63
+ if (span.text === "") continue;
64
+ const own = sgr(span.style);
65
+ const last = out.at(-1);
66
+ if (last && own === key) last.text += span.text;
67
+ else out.push({
68
+ text: span.text,
69
+ style: span.style
70
+ });
71
+ key = own;
72
+ }
73
+ return out;
74
+ }
75
+ export { BOLD, CODE, DIM, DONE, FAILED, MATH, PLAIN, WAITING, lineText, merged, patch, sgr, toAnsi };
76
+
77
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../src/tui/style.ts"],"sourcesContent":["/**\n * What a terminal row is made of: runs of text, each with how it is drawn. Nothing here knows an\n * escape sequence but {@link toAnsi}: a host with its own screen (a TUI library, a test) reads\n * the spans and draws them its own way.\n */\n\n/**\n * The colours the renderer uses: the ones every terminal theme has a readable shade of. Blue is\n * left out for that reason: on many dark themes it is too dark to read.\n */\nexport type Colour = 'red' | 'green' | 'yellow' | 'cyan' | 'magenta'\n\n/** How a run of text is drawn. A field left out is off. */\nexport interface Style {\n bold?: boolean\n dim?: boolean\n italic?: boolean\n underline?: boolean\n strike?: boolean\n color?: Colour\n}\n\n/** A run of text in one style. Its text holds no control character: see `printable.ts`. */\nexport interface Span {\n text: string\n style: Style\n}\n\n/** One row of the terminal. A blank row has no spans. */\nexport type Line = Span[]\n\nexport const PLAIN: Style = {}\nexport const BOLD: Style = { bold: true }\nexport const DIM: Style = { dim: true }\n/** Code, and a note. */\nexport const CODE: Style = { color: 'cyan' }\n/** A formula. */\nexport const MATH: Style = { italic: true }\n/** What went well: a checked task, a tip, an argument for. */\nexport const DONE: Style = { color: 'green' }\n/** What needs care: a warning. */\nexport const WAITING: Style = { color: 'yellow' }\n/** What went wrong or argues against. */\nexport const FAILED: Style = { color: 'red' }\n\n/** `style` with what `over` sets laid over it. */\nexport const patch = (style: Style, over: Style): Style => ({ ...style, ...over })\n\nconst SGR: Array<[keyof Style, string]> = [\n ['bold', '1'],\n ['dim', '2'],\n ['italic', '3'],\n ['underline', '4'],\n ['strike', '9'],\n]\n\nconst FOREGROUND: Record<Colour, string> = {\n red: '31',\n green: '32',\n yellow: '33',\n cyan: '36',\n magenta: '35',\n}\n\n/** The parameters of the escape sequence that turns `style` on; '' for plain text. */\nexport function sgr(style: Style): string {\n const codes: string[] = []\n for (const [field, code] of SGR) if (style[field]) codes.push(code)\n if (style.color) codes.push(FOREGROUND[style.color])\n return codes.join(';')\n}\n\n/** A row as its text alone. */\nexport const lineText = (line: Line): string => line.map((span) => span.text).join('')\n\n/**\n * Rows as text for a terminal, one per line. With `color`, each styled run is wrapped in the\n * sequence that draws it and the one that resets it; without, the text is all there is.\n */\nexport function toAnsi(lines: readonly Line[], color = true): string {\n let out = ''\n for (let at = 0; at < lines.length; at++) {\n if (at > 0) out += '\\n'\n for (const span of lines[at]!) {\n const codes = color ? sgr(span.style) : ''\n out += codes ? `\\u001b[${codes}m${span.text}\\u001b[0m` : span.text\n }\n }\n return out\n}\n\n/** A row with neighbouring runs of one style made one run. */\nexport function merged(line: Line): Line {\n const out: Line = []\n let key = ''\n for (const span of line) {\n if (span.text === '') continue\n const own = sgr(span.style)\n const last = out.at(-1)\n if (last && own === key) last.text += span.text\n else out.push({ text: span.text, style: span.style })\n key = own\n }\n return out\n}\n"],"mappings":";AA+BA,MAAa,QAAe,CAAC;AAC7B,MAAa,OAAc,EAAE,MAAM,KAAK;AACxC,MAAa,MAAa,EAAE,KAAK,KAAK;;AAEtC,MAAa,OAAc,EAAE,OAAO,OAAO;;AAE3C,MAAa,OAAc,EAAE,QAAQ,KAAK;;AAE1C,MAAa,OAAc,EAAE,OAAO,QAAQ;;AAE5C,MAAa,UAAiB,EAAE,OAAO,SAAS;;AAEhD,MAAa,SAAgB,EAAE,OAAO,MAAM;;AAG5C,MAAa,SAAS,OAAc,UAAwB;CAAE,GAAG;CAAO,GAAG;AAAK;AAEhF,MAAM,MAAoC;CACxC,CAAC,QAAQ,GAAG;CACZ,CAAC,OAAO,GAAG;CACX,CAAC,UAAU,GAAG;CACd,CAAC,aAAa,GAAG;CACjB,CAAC,UAAU,GAAG;AAChB;AAEA,MAAM,aAAqC;CACzC,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;CACN,SAAS;AACX;;AAGA,SAAgB,IAAI,OAAsB;CACxC,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,OAAO,SAAS,KAAK,IAAI,MAAM,QAAQ,MAAM,KAAK,IAAI;CAClE,IAAI,MAAM,OAAO,MAAM,KAAK,WAAW,MAAM,MAAM;CACnD,OAAO,MAAM,KAAK,GAAG;AACvB;;AAGA,MAAa,YAAY,SAAuB,KAAK,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE;;;;;AAMrF,SAAgB,OAAO,OAAwB,QAAQ,MAAc;CACnE,IAAI,MAAM;CACV,KAAK,IAAI,KAAK,GAAG,KAAK,MAAM,QAAQ,MAAM;EACxC,IAAI,KAAK,GAAG,OAAO;EACnB,KAAK,MAAM,QAAQ,MAAM,KAAM;GAC7B,MAAM,QAAQ,QAAQ,IAAI,KAAK,KAAK,IAAI;GACxC,OAAO,QAAQ,UAAU,MAAM,GAAG,KAAK,KAAK,aAAa,KAAK;EAChE;CACF;CACA,OAAO;AACT;;AAGA,SAAgB,OAAO,MAAkB;CACvC,MAAM,MAAY,CAAC;CACnB,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,MAAM;EACvB,IAAI,KAAK,SAAS,IAAI;EACtB,MAAM,MAAM,IAAI,KAAK,KAAK;EAC1B,MAAM,OAAO,IAAI,GAAG,EAAE;EACtB,IAAI,QAAQ,QAAQ,KAAK,KAAK,QAAQ,KAAK;OACtC,IAAI,KAAK;GAAE,MAAM,KAAK;GAAM,OAAO,KAAK;EAAM,CAAC;EACpD,MAAM;CACR;CACA,OAAO;AACT"}
@@ -0,0 +1,73 @@
1
+ import { BOLD, DIM, patch } from "./style.js";
2
+ import { lineWidth, span, wrap } from "./wrap.js";
3
+ import { width } from "./width.js";
4
+ // src/tui/table.ts
5
+ /** The width of each column: what it holds when the table fits `budget`, a fair share when not. */
6
+ function widths(rows, columns, budget) {
7
+ const most = Array.from({ length: columns }, () => 1);
8
+ const least = Array.from({ length: columns }, () => 1);
9
+ for (const row of rows) for (let at = 0; at < Math.min(columns, row.length); at++) {
10
+ const cell = row[at];
11
+ most[at] = Math.max(most[at], lineWidth(cell));
12
+ for (const run of cell) for (const word of run.text.split(" ")) least[at] = Math.max(least[at], width(word));
13
+ }
14
+ if (most.reduce((sum, own) => sum + own, 0) <= budget) return most;
15
+ const share = Math.max(1, Math.floor(budget / columns));
16
+ for (let at = 0; at < columns; at++) least[at] = Math.min(least[at], share);
17
+ const spare = Math.max(0, budget - least.reduce((sum, own) => sum + own, 0));
18
+ const more = (at) => Math.max(0, most[at] - least[at]);
19
+ let wanted = 0;
20
+ for (let at = 0; at < columns; at++) wanted += more(at);
21
+ const out = least.map((own, at) => own + Math.floor(more(at) * spare / Math.max(1, wanted)));
22
+ let left = Math.max(0, budget - out.reduce((sum, own) => sum + own, 0));
23
+ while (left > 0) {
24
+ const before = left;
25
+ for (let at = 0; at < columns; at++) if (left > 0 && out[at] < most[at]) {
26
+ out[at]++;
27
+ left--;
28
+ }
29
+ if (left === before) break;
30
+ }
31
+ return out;
32
+ }
33
+ /** `rows` of cells (the first is the header) as terminal rows no wider than `columns`. */
34
+ function drawTable(rows, aligns, columns, first, rest) {
35
+ const count = aligns.length;
36
+ if (count === 0) return [];
37
+ const room = Math.max(0, columns - Math.max(lineWidth(rest), lineWidth(first)));
38
+ const out = [];
39
+ if (room < count * 5 + 1) {
40
+ for (const row of rows) {
41
+ if (out.length > 0) out.push([...rest]);
42
+ for (const cell of row) out.push(...wrap(cell, columns, out.length === 0 ? first : rest, rest));
43
+ }
44
+ return out;
45
+ }
46
+ const sizes = widths(rows, count, Math.max(0, room - (count * 3 + 1)));
47
+ const rule = sizes.map((size) => "─".repeat(size + 2)).join("┼");
48
+ const ruleRow = [...rest, span(`├${rule}┤`, DIM)];
49
+ const laid = rows.map((row) => sizes.map((size, at) => wrap(row[at] ?? [], size, [], [])));
50
+ const ruled = laid.slice(1).some((row) => row.some((cell) => cell.length > 1));
51
+ const last = laid.length - 1;
52
+ laid.forEach((cells, index) => {
53
+ const height = Math.max(1, ...cells.map((cell) => cell.length));
54
+ for (let line = 0; line < height; line++) {
55
+ const drawn = [...out.length === 0 ? first : rest, span("│", DIM)];
56
+ for (let at = 0; at < count; at++) {
57
+ const cell = cells[at][line] ?? [];
58
+ const padding = Math.max(0, sizes[at] - lineWidth(cell));
59
+ const align = aligns[at];
60
+ const left = align === "right" ? padding : align === "center" ? padding >> 1 : 0;
61
+ drawn.push(span(" ".repeat(left + 1), {}));
62
+ for (const run of cell) drawn.push(index === 0 ? span(run.text, patch(run.style, BOLD)) : run);
63
+ drawn.push(span(" ".repeat(padding - left + 1), {}), span("│", DIM));
64
+ }
65
+ out.push(drawn);
66
+ }
67
+ if (index === 0 || ruled && index < last) out.push(ruleRow);
68
+ });
69
+ return out;
70
+ }
71
+ export { drawTable };
72
+
73
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","names":[],"sources":["../../src/tui/table.ts"],"sourcesContent":["/**\n * A table drawn in columns: each as wide as what it holds while the row fits, and shared out by\n * what each needs when it does not. A table too narrow to draw at all (under five columns of the\n * terminal for each of its own) is written cell under cell: nothing is lost, only the grid.\n */\nimport type { Align } from '../engine/types'\nimport { BOLD, DIM, patch, type Line } from './style'\nimport { lineWidth, span, wrap } from './wrap'\nimport { width } from './width'\n\n/** The width of each column: what it holds when the table fits `budget`, a fair share when not. */\nfunction widths(rows: Line[][], columns: number, budget: number): number[] {\n const most = Array.from({ length: columns }, () => 1)\n const least = Array.from({ length: columns }, () => 1)\n for (const row of rows) {\n for (let at = 0; at < Math.min(columns, row.length); at++) {\n const cell = row[at]!\n most[at] = Math.max(most[at]!, lineWidth(cell))\n for (const run of cell)\n for (const word of run.text.split(' ')) least[at] = Math.max(least[at]!, width(word))\n }\n }\n if (most.reduce((sum, own) => sum + own, 0) <= budget) return most\n // No column asks for more than its share as its least: a long word is broken, not the table.\n const share = Math.max(1, Math.floor(budget / columns))\n for (let at = 0; at < columns; at++) least[at] = Math.min(least[at]!, share)\n const spare = Math.max(0, budget - least.reduce((sum, own) => sum + own, 0))\n const more = (at: number) => Math.max(0, most[at]! - least[at]!)\n let wanted = 0\n for (let at = 0; at < columns; at++) wanted += more(at)\n const out = least.map((own, at) => own + Math.floor((more(at) * spare) / Math.max(1, wanted)))\n let left = Math.max(0, budget - out.reduce((sum, own) => sum + own, 0))\n while (left > 0) {\n const before = left\n for (let at = 0; at < columns; at++) {\n if (left > 0 && out[at]! < most[at]!) {\n out[at]!++\n left--\n }\n }\n if (left === before) break\n }\n return out\n}\n\n/** `rows` of cells (the first is the header) as terminal rows no wider than `columns`. */\nexport function drawTable(\n rows: Line[][],\n aligns: readonly Align[],\n columns: number,\n first: Line,\n rest: Line,\n): Line[] {\n const count = aligns.length\n if (count === 0) return []\n const room = Math.max(0, columns - Math.max(lineWidth(rest), lineWidth(first)))\n const out: Line[] = []\n if (room < count * 5 + 1) {\n for (const row of rows) {\n if (out.length > 0) out.push([...rest])\n for (const cell of row)\n out.push(...wrap(cell, columns, out.length === 0 ? first : rest, rest))\n }\n return out\n }\n const sizes = widths(rows, count, Math.max(0, room - (count * 3 + 1)))\n const rule = sizes.map((size) => '─'.repeat(size + 2)).join('┼')\n const ruleRow: Line = [...rest, span(`├${rule}┤`, DIM)]\n const laid = rows.map((row) => sizes.map((size, at) => wrap(row[at] ?? [], size, [], [])))\n // Rows of one line each need no rule between them; once one wraps, every row is ruled apart.\n const ruled = laid.slice(1).some((row) => row.some((cell) => cell.length > 1))\n const last = laid.length - 1\n laid.forEach((cells, index) => {\n const height = Math.max(1, ...cells.map((cell) => cell.length))\n for (let line = 0; line < height; line++) {\n const drawn: Line = [...(out.length === 0 ? first : rest), span('│', DIM)]\n for (let at = 0; at < count; at++) {\n const cell = cells[at]![line] ?? []\n const padding = Math.max(0, sizes[at]! - lineWidth(cell))\n const align = aligns[at]\n const left = align === 'right' ? padding : align === 'center' ? padding >> 1 : 0\n drawn.push(span(' '.repeat(left + 1), {}))\n for (const run of cell)\n drawn.push(index === 0 ? span(run.text, patch(run.style, BOLD)) : run)\n drawn.push(span(' '.repeat(padding - left + 1), {}), span('│', DIM))\n }\n out.push(drawn)\n }\n if (index === 0 || (ruled && index < last)) out.push(ruleRow)\n })\n return out\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ /** The columns `text` takes. */
2
+ export declare function width(text: string): number;
@@ -0,0 +1,133 @@
1
+ // src/tui/width.ts
2
+ /**
3
+ * How many columns of a terminal a text takes. Most characters take one; a CJK ideograph or an
4
+ * emoji takes two; a combining mark or a joiner takes none. A row is wrapped by these columns,
5
+ * not by its length: a row counted short would be wrapped again by the terminal itself, and every
6
+ * row under it would be one lower than the renderer believes.
7
+ */
8
+ /** Characters drawn two columns wide (East Asian Wide and Fullwidth), as inclusive ranges. */
9
+ const WIDE = [
10
+ [4352, 4447],
11
+ [8986, 8987],
12
+ [9001, 9002],
13
+ [9193, 9196],
14
+ [9200, 9200],
15
+ [9203, 9203],
16
+ [9725, 9726],
17
+ [9748, 9749],
18
+ [9800, 9811],
19
+ [9855, 9855],
20
+ [9875, 9875],
21
+ [9889, 9889],
22
+ [9898, 9899],
23
+ [9917, 9918],
24
+ [9924, 9925],
25
+ [9934, 9934],
26
+ [9940, 9940],
27
+ [9962, 9962],
28
+ [9970, 9971],
29
+ [9973, 9973],
30
+ [9978, 9978],
31
+ [9981, 9981],
32
+ [9989, 9989],
33
+ [9994, 9995],
34
+ [10024, 10024],
35
+ [10060, 10060],
36
+ [10062, 10062],
37
+ [10067, 10069],
38
+ [10071, 10071],
39
+ [10133, 10135],
40
+ [10160, 10160],
41
+ [10175, 10175],
42
+ [11035, 11036],
43
+ [11088, 11088],
44
+ [11093, 11093],
45
+ [11904, 12350],
46
+ [12353, 13311],
47
+ [13312, 19903],
48
+ [19968, 42191],
49
+ [43360, 43391],
50
+ [44032, 55203],
51
+ [63744, 64255],
52
+ [65040, 65049],
53
+ [65072, 65135],
54
+ [65281, 65376],
55
+ [65504, 65510],
56
+ [94176, 101119],
57
+ [110592, 111359],
58
+ [126980, 126980],
59
+ [127183, 127183],
60
+ [127374, 127374],
61
+ [127377, 127386],
62
+ [127488, 127569],
63
+ [127744, 127776],
64
+ [127789, 127797],
65
+ [127799, 127868],
66
+ [127870, 127891],
67
+ [127904, 127946],
68
+ [127951, 127955],
69
+ [127968, 127984],
70
+ [127988, 127988],
71
+ [127992, 128062],
72
+ [128064, 128064],
73
+ [128066, 128252],
74
+ [128255, 128317],
75
+ [128331, 128334],
76
+ [128336, 128359],
77
+ [128378, 128378],
78
+ [128405, 128406],
79
+ [128420, 128420],
80
+ [128507, 128591],
81
+ [128640, 128709],
82
+ [128716, 128716],
83
+ [128720, 128722],
84
+ [128725, 128727],
85
+ [128732, 128735],
86
+ [128747, 128748],
87
+ [128756, 128764],
88
+ [128992, 129003],
89
+ [129008, 129008],
90
+ [129292, 129338],
91
+ [129340, 129349],
92
+ [129351, 129535],
93
+ [129648, 129660],
94
+ [129664, 129673],
95
+ [129679, 129734],
96
+ [129742, 129756],
97
+ [129759, 129769],
98
+ [129776, 129784],
99
+ [131072, 196605],
100
+ [196608, 262141]
101
+ ];
102
+ /** Marks that sit on the character before them, and the invisible joiners and selectors. */
103
+ const ZERO = /^[\p{Mn}\p{Me}\p{Cf}]$/u;
104
+ const isWide = (code) => {
105
+ let low = 0;
106
+ let high = WIDE.length - 1;
107
+ while (low <= high) {
108
+ const mid = low + high >> 1;
109
+ const [from, to] = WIDE[mid];
110
+ if (code < from) high = mid - 1;
111
+ else if (code > to) low = mid + 1;
112
+ else return true;
113
+ }
114
+ return false;
115
+ };
116
+ /** The columns one character takes: 0, 1 or 2. */
117
+ function charWidth(char) {
118
+ const code = char.codePointAt(0);
119
+ if (code < 768) return code < 32 || code >= 127 && code < 160 ? 0 : 1;
120
+ if (code >= 4448 && code <= 4607 || ZERO.test(char)) return 0;
121
+ return code >= 4352 && isWide(code) ? 2 : 1;
122
+ }
123
+ const NARROW = /^[ -~]*$/;
124
+ /** The columns `text` takes. */
125
+ function width(text) {
126
+ if (NARROW.test(text)) return text.length;
127
+ let columns = 0;
128
+ for (const char of text) columns += charWidth(char);
129
+ return columns;
130
+ }
131
+ export { charWidth, width };
132
+
133
+ //# sourceMappingURL=width.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"width.js","names":[],"sources":["../../src/tui/width.ts"],"sourcesContent":["/**\n * How many columns of a terminal a text takes. Most characters take one; a CJK ideograph or an\n * emoji takes two; a combining mark or a joiner takes none. A row is wrapped by these columns,\n * not by its length: a row counted short would be wrapped again by the terminal itself, and every\n * row under it would be one lower than the renderer believes.\n */\n\n/** Characters drawn two columns wide (East Asian Wide and Fullwidth), as inclusive ranges. */\nconst WIDE: ReadonlyArray<readonly [number, number]> = [\n [0x1100, 0x115f],\n [0x231a, 0x231b],\n [0x2329, 0x232a],\n [0x23e9, 0x23ec],\n [0x23f0, 0x23f0],\n [0x23f3, 0x23f3],\n [0x25fd, 0x25fe],\n [0x2614, 0x2615],\n [0x2648, 0x2653],\n [0x267f, 0x267f],\n [0x2693, 0x2693],\n [0x26a1, 0x26a1],\n [0x26aa, 0x26ab],\n [0x26bd, 0x26be],\n [0x26c4, 0x26c5],\n [0x26ce, 0x26ce],\n [0x26d4, 0x26d4],\n [0x26ea, 0x26ea],\n [0x26f2, 0x26f3],\n [0x26f5, 0x26f5],\n [0x26fa, 0x26fa],\n [0x26fd, 0x26fd],\n [0x2705, 0x2705],\n [0x270a, 0x270b],\n [0x2728, 0x2728],\n [0x274c, 0x274c],\n [0x274e, 0x274e],\n [0x2753, 0x2755],\n [0x2757, 0x2757],\n [0x2795, 0x2797],\n [0x27b0, 0x27b0],\n [0x27bf, 0x27bf],\n [0x2b1b, 0x2b1c],\n [0x2b50, 0x2b50],\n [0x2b55, 0x2b55],\n [0x2e80, 0x303e],\n [0x3041, 0x33ff],\n [0x3400, 0x4dbf],\n [0x4e00, 0xa4cf],\n [0xa960, 0xa97f],\n [0xac00, 0xd7a3],\n [0xf900, 0xfaff],\n [0xfe10, 0xfe19],\n [0xfe30, 0xfe6f],\n [0xff01, 0xff60],\n [0xffe0, 0xffe6],\n [0x16fe0, 0x18aff],\n [0x1b000, 0x1b2ff],\n [0x1f004, 0x1f004],\n [0x1f0cf, 0x1f0cf],\n [0x1f18e, 0x1f18e],\n [0x1f191, 0x1f19a],\n [0x1f200, 0x1f251],\n [0x1f300, 0x1f320],\n [0x1f32d, 0x1f335],\n [0x1f337, 0x1f37c],\n [0x1f37e, 0x1f393],\n [0x1f3a0, 0x1f3ca],\n [0x1f3cf, 0x1f3d3],\n [0x1f3e0, 0x1f3f0],\n [0x1f3f4, 0x1f3f4],\n [0x1f3f8, 0x1f43e],\n [0x1f440, 0x1f440],\n [0x1f442, 0x1f4fc],\n [0x1f4ff, 0x1f53d],\n [0x1f54b, 0x1f54e],\n [0x1f550, 0x1f567],\n [0x1f57a, 0x1f57a],\n [0x1f595, 0x1f596],\n [0x1f5a4, 0x1f5a4],\n [0x1f5fb, 0x1f64f],\n [0x1f680, 0x1f6c5],\n [0x1f6cc, 0x1f6cc],\n [0x1f6d0, 0x1f6d2],\n [0x1f6d5, 0x1f6d7],\n [0x1f6dc, 0x1f6df],\n [0x1f6eb, 0x1f6ec],\n [0x1f6f4, 0x1f6fc],\n [0x1f7e0, 0x1f7eb],\n [0x1f7f0, 0x1f7f0],\n [0x1f90c, 0x1f93a],\n [0x1f93c, 0x1f945],\n [0x1f947, 0x1f9ff],\n [0x1fa70, 0x1fa7c],\n [0x1fa80, 0x1fa89],\n [0x1fa8f, 0x1fac6],\n [0x1face, 0x1fadc],\n [0x1fadf, 0x1fae9],\n [0x1faf0, 0x1faf8],\n [0x20000, 0x2fffd],\n [0x30000, 0x3fffd],\n]\n\n/** Marks that sit on the character before them, and the invisible joiners and selectors. */\nconst ZERO = /^[\\p{Mn}\\p{Me}\\p{Cf}]$/u\n\nconst isWide = (code: number): boolean => {\n let low = 0\n let high = WIDE.length - 1\n while (low <= high) {\n const mid = (low + high) >> 1\n const [from, to] = WIDE[mid]!\n if (code < from) high = mid - 1\n else if (code > to) low = mid + 1\n else return true\n }\n return false\n}\n\n/** The columns one character takes: 0, 1 or 2. */\nexport function charWidth(char: string): number {\n const code = char.codePointAt(0)!\n // Everything Latin, and most of what a reply is written in.\n if (code < 0x300) return code < 0x20 || (code >= 0x7f && code < 0xa0) ? 0 : 1\n // The vowels and finals of a Hangul syllable written in parts are drawn in its first part's cell.\n if ((code >= 0x1160 && code <= 0x11ff) || ZERO.test(char)) return 0\n return code >= 0x1100 && isWide(code) ? 2 : 1\n}\n\nconst NARROW = /^[ -~]*$/\n\n/** The columns `text` takes. */\nexport function width(text: string): number {\n if (NARROW.test(text)) return text.length\n let columns = 0\n for (const char of text) columns += charWidth(char)\n return columns\n}\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Styled lines wrapped to a width by display columns, at word boundaries, with a prefix for the
3
+ * first row and another for the rest (a hanging indent): a paragraph never breaks mid-word and
4
+ * its continuation sits under its own start. A word wider than the row is split; nothing is ever
5
+ * wider than `width` while the prefix leaves a column to write in.
6
+ */
7
+ import { Line } from "./style.js";
8
+ export declare const lineWidth: (line: Line) => number;
@@ -0,0 +1,191 @@
1
+ import { charWidth, width } from "./width.js";
2
+ const lineWidth = (line) => {
3
+ let columns = 0;
4
+ for (const span of line) columns += width(span.text);
5
+ return columns;
6
+ };
7
+ const SPACE = /\s/;
8
+ /** The words of `line` with their style and what follows each. */
9
+ function words(line) {
10
+ const out = [];
11
+ for (const span of line) {
12
+ let word = "";
13
+ for (const char of span.text) if (SPACE.test(char)) {
14
+ const after = char === "\xA0" ? 1 : 0;
15
+ if (word) {
16
+ out.push({
17
+ text: word,
18
+ style: span.style,
19
+ after
20
+ });
21
+ word = "";
22
+ } else if (out.length > 0) out.at(-1).after = after;
23
+ } else word += char;
24
+ if (word) out.push({
25
+ text: word,
26
+ style: span.style,
27
+ after: 2
28
+ });
29
+ }
30
+ return out;
31
+ }
32
+ /**
33
+ * For each word, the columns kept on one row from it on: the words that run into it, and where a
34
+ * code span starts, the span. A span no row holds is then broken at its spaces, as words are.
35
+ */
36
+ function runs(list) {
37
+ const out = list.map(() => 0);
38
+ let glued = 0;
39
+ let whole = 0;
40
+ for (let at = list.length - 1; at >= 0; at--) {
41
+ const { text, after } = list[at];
42
+ const own = width(text);
43
+ if (after === 0) [glued, whole] = [own, own];
44
+ else if (after === 1) [glued, whole] = [own, own + 1 + whole];
45
+ else [glued, whole] = [own + glued, own + whole];
46
+ const before = at > 0 ? list[at - 1].after : 0;
47
+ out[at] = before === 0 ? whole : before === 1 ? glued : 0;
48
+ }
49
+ return out;
50
+ }
51
+ /** Where `word` is cut to fit `columns`: its length when it fits. Always past its first character. */
52
+ function split(word, columns) {
53
+ let used = 0;
54
+ let at = 0;
55
+ for (const char of word) {
56
+ const own = charWidth(char);
57
+ if (used + own > columns && at > 0) return at;
58
+ used += own;
59
+ at += char.length;
60
+ }
61
+ return word.length;
62
+ }
63
+ /**
64
+ * How the space between two words is drawn: with what both are. Inside a link it is underlined
65
+ * with its words; after the link it is not, and the line under the link ends where the link does.
66
+ */
67
+ function shared(before, after) {
68
+ if (before === after) return before;
69
+ const out = {};
70
+ for (const [field, value] of Object.entries(before)) if (after[field] === value) out[field] = value;
71
+ return out;
72
+ }
73
+ /** `line` wrapped to `columns`: the first row starts with `first`, the others with `rest`. */
74
+ function wrap(line, columns, first, rest) {
75
+ const rows = [];
76
+ let row = [...first];
77
+ let used = lineWidth(first);
78
+ const indent = lineWidth(rest);
79
+ const room = Math.max(columns, indent + 1);
80
+ let pending = null;
81
+ let fresh = true;
82
+ const list = words(line);
83
+ const kept = runs(list);
84
+ const next = () => {
85
+ rows.push(row);
86
+ row = [...rest];
87
+ used = indent;
88
+ fresh = true;
89
+ };
90
+ for (let at = 0; at < list.length; at++) {
91
+ const { style, after } = list[at];
92
+ let word = list[at].text;
93
+ const run = kept[at];
94
+ if (!fresh && used + (pending ? 1 : 0) + run > room && indent + run <= room) next();
95
+ let own = width(word);
96
+ for (;;) {
97
+ const gap = pending && !fresh ? 1 : 0;
98
+ if (used + gap + own <= room) {
99
+ if (gap) row.push({
100
+ text: " ",
101
+ style: shared(pending, style)
102
+ });
103
+ row.push({
104
+ text: word,
105
+ style
106
+ });
107
+ used += gap + own;
108
+ fresh = false;
109
+ break;
110
+ }
111
+ if (!fresh) {
112
+ next();
113
+ continue;
114
+ }
115
+ const head = word.slice(0, split(word, Math.max(1, room - used)));
116
+ row.push({
117
+ text: head,
118
+ style
119
+ });
120
+ word = word.slice(head.length);
121
+ own -= width(head);
122
+ next();
123
+ if (!word) break;
124
+ }
125
+ pending = after === 2 ? null : style;
126
+ }
127
+ if (!fresh || rows.length === 0) rows.push(row);
128
+ return rows;
129
+ }
130
+ /**
131
+ * A line of code with each tab run out to the next fourth column: a terminal's own stops are
132
+ * eight wide, and an escaped tab is not what was written.
133
+ */
134
+ function untabbed(line) {
135
+ if (!line.includes(" ")) return line;
136
+ const pads = tabPads(line);
137
+ return line.split(" ").map((piece, at) => (at > 0 ? " ".repeat(pads[at - 1]) : "") + piece).join("");
138
+ }
139
+ /** The spaces each tab of `line` stands for, in order: what {@link untabbed} writes in its place. */
140
+ function tabPads(line) {
141
+ const pads = [];
142
+ let column = 0;
143
+ line.split(" ").forEach((piece, at) => {
144
+ if (at > 0) {
145
+ const pad = 4 - column % 4;
146
+ pads.push(pad);
147
+ column += pad;
148
+ }
149
+ column += width(piece);
150
+ });
151
+ return pads;
152
+ }
153
+ /**
154
+ * `line` in rows of at most `columns`, broken anywhere with its styles and its spaces kept: for
155
+ * code, whose indentation and alignment mean something.
156
+ */
157
+ function fit(line, columns) {
158
+ const room = Math.max(1, columns);
159
+ if (lineWidth(line) <= room) return [line];
160
+ const rows = [[]];
161
+ let used = 0;
162
+ for (const span of line) {
163
+ let text = "";
164
+ for (const char of span.text) {
165
+ const own = charWidth(char);
166
+ if (used + own > room && used > 0) {
167
+ rows.at(-1).push({
168
+ text,
169
+ style: span.style
170
+ });
171
+ text = "";
172
+ rows.push([]);
173
+ used = 0;
174
+ }
175
+ text += char;
176
+ used += own;
177
+ }
178
+ rows.at(-1).push({
179
+ text,
180
+ style: span.style
181
+ });
182
+ }
183
+ return rows;
184
+ }
185
+ const span = (text, style) => ({
186
+ text,
187
+ style
188
+ });
189
+ export { fit, lineWidth, span, tabPads, untabbed, wrap };
190
+
191
+ //# sourceMappingURL=wrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap.js","names":[],"sources":["../../src/tui/wrap.ts"],"sourcesContent":["/**\n * Styled lines wrapped to a width by display columns, at word boundaries, with a prefix for the\n * first row and another for the rest (a hanging indent): a paragraph never breaks mid-word and\n * its continuation sits under its own start. A word wider than the row is split; nothing is ever\n * wider than `width` while the prefix leaves a column to write in.\n */\nimport type { Line, Span, Style } from './style'\nimport { charWidth, width } from './width'\n\n/**\n * A space a row is not broken at while a row holds what it joins: the spaces of code. Drawn as a\n * space, so what is copied from the screen is what was written.\n */\nexport const NO_BREAK = String.fromCharCode(0xa0)\n\n/** What follows a word. */\nconst enum After {\n /** A space, where a row may end. */\n Space,\n /** A space that keeps its words on one row when a row holds them. */\n Joint,\n /** The next word, with nothing between: \"`code`,\" or \"a**b**\". */\n Nothing,\n}\n\ninterface Word {\n text: string\n style: Style\n after: After\n}\n\nexport const lineWidth = (line: Line): number => {\n let columns = 0\n for (const span of line) columns += width(span.text)\n return columns\n}\n\nconst SPACE = /\\s/\n\n/** The words of `line` with their style and what follows each. */\nfunction words(line: Line): Word[] {\n const out: Word[] = []\n for (const span of line) {\n let word = ''\n for (const char of span.text) {\n if (SPACE.test(char)) {\n const after = char === NO_BREAK ? After.Joint : After.Space\n if (word) {\n out.push({ text: word, style: span.style, after })\n word = ''\n } else if (out.length > 0) out.at(-1)!.after = after\n } else word += char\n }\n if (word) out.push({ text: word, style: span.style, after: After.Nothing })\n }\n return out\n}\n\n/**\n * For each word, the columns kept on one row from it on: the words that run into it, and where a\n * code span starts, the span. A span no row holds is then broken at its spaces, as words are.\n */\nfunction runs(list: Word[]): number[] {\n const out = list.map(() => 0)\n let glued = 0\n let whole = 0\n for (let at = list.length - 1; at >= 0; at--) {\n const { text, after } = list[at]!\n const own = width(text)\n if (after === After.Space) [glued, whole] = [own, own]\n else if (after === After.Joint) [glued, whole] = [own, own + 1 + whole]\n else [glued, whole] = [own + glued, own + whole]\n const before = at > 0 ? list[at - 1]!.after : After.Space\n out[at] = before === After.Space ? whole : before === After.Joint ? glued : 0\n }\n return out\n}\n\n/** Where `word` is cut to fit `columns`: its length when it fits. Always past its first character. */\nfunction split(word: string, columns: number): number {\n let used = 0\n let at = 0\n for (const char of word) {\n const own = charWidth(char)\n if (used + own > columns && at > 0) return at\n used += own\n at += char.length\n }\n return word.length\n}\n\n/**\n * How the space between two words is drawn: with what both are. Inside a link it is underlined\n * with its words; after the link it is not, and the line under the link ends where the link does.\n */\nfunction shared(before: Style, after: Style): Style {\n if (before === after) return before\n const out: Record<string, unknown> = {}\n for (const [field, value] of Object.entries(before))\n if ((after as Record<string, unknown>)[field] === value) out[field] = value\n return out as Style\n}\n\n/** `line` wrapped to `columns`: the first row starts with `first`, the others with `rest`. */\nexport function wrap(line: Line, columns: number, first: Line, rest: Line): Line[] {\n const rows: Line[] = []\n let row: Line = [...first]\n let used = lineWidth(first)\n const indent = lineWidth(rest)\n const room = Math.max(columns, indent + 1)\n let pending: Style | null = null\n let fresh = true\n const list = words(line)\n const kept = runs(list)\n const next = () => {\n rows.push(row)\n row = [...rest]\n used = indent\n fresh = true\n }\n for (let at = 0; at < list.length; at++) {\n const { style, after } = list[at]!\n let word = list[at]!.text\n const run = kept[at]!\n // A word and what runs into it (\"`code`,\" or \"(**b**)\"), and the words of a code span, go\n // to the next row together when a row holds them: no row starts with the comma.\n if (!fresh && used + (pending ? 1 : 0) + run > room && indent + run <= room) next()\n // Measured once, and less each piece cut off: a word of a megabyte is not measured per row.\n let own = width(word)\n for (;;) {\n const gap = pending && !fresh ? 1 : 0\n if (used + gap + own <= room) {\n if (gap) row.push({ text: ' ', style: shared(pending!, style) })\n row.push({ text: word, style })\n used += gap + own\n fresh = false\n break\n }\n if (!fresh) {\n next()\n continue\n }\n const head = word.slice(0, split(word, Math.max(1, room - used)))\n row.push({ text: head, style })\n word = word.slice(head.length)\n own -= width(head)\n next()\n if (!word) break\n }\n pending = after === After.Nothing ? null : style\n }\n if (!fresh || rows.length === 0) rows.push(row)\n return rows\n}\n\n/**\n * A line of code with each tab run out to the next fourth column: a terminal's own stops are\n * eight wide, and an escaped tab is not what was written.\n */\nexport function untabbed(line: string): string {\n if (!line.includes('\\t')) return line\n const pads = tabPads(line)\n return line\n .split('\\t')\n .map((piece, at) => (at > 0 ? ' '.repeat(pads[at - 1]!) : '') + piece)\n .join('')\n}\n\n/** The spaces each tab of `line` stands for, in order: what {@link untabbed} writes in its place. */\nexport function tabPads(line: string): number[] {\n const pads: number[] = []\n let column = 0\n line.split('\\t').forEach((piece, at) => {\n if (at > 0) {\n const pad = 4 - (column % 4)\n pads.push(pad)\n column += pad\n }\n column += width(piece)\n })\n return pads\n}\n\n/**\n * `line` in rows of at most `columns`, broken anywhere with its styles and its spaces kept: for\n * code, whose indentation and alignment mean something.\n */\nexport function fit(line: Line, columns: number): Line[] {\n const room = Math.max(1, columns)\n if (lineWidth(line) <= room) return [line]\n const rows: Line[] = [[]]\n let used = 0\n for (const span of line) {\n let text = ''\n for (const char of span.text) {\n const own = charWidth(char)\n if (used + own > room && used > 0) {\n rows.at(-1)!.push({ text, style: span.style })\n text = ''\n rows.push([])\n used = 0\n }\n text += char\n used += own\n }\n rows.at(-1)!.push({ text, style: span.style })\n }\n return rows\n}\n\nexport const span = (text: string, style: Style): Span => ({ text, style })\n"],"mappings":"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"}