@outfitter/tui 0.2.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 (235) hide show
  1. package/README.md +250 -0
  2. package/dist/borders/index.d.ts +3 -0
  3. package/dist/borders/index.js +13 -0
  4. package/dist/box/index.d.ts +4 -0
  5. package/dist/box/index.js +10 -0
  6. package/dist/confirm.d.ts +37 -0
  7. package/dist/confirm.js +36 -0
  8. package/dist/demo/index.d.ts +77 -0
  9. package/dist/demo/index.js +142 -0
  10. package/dist/demo/registry.d.ts +6 -0
  11. package/dist/demo/registry.js +28 -0
  12. package/dist/demo/renderers/borders.d.ts +7 -0
  13. package/dist/demo/renderers/borders.js +14 -0
  14. package/dist/demo/renderers/box.d.ts +7 -0
  15. package/dist/demo/renderers/box.js +15 -0
  16. package/dist/demo/renderers/colors.d.ts +7 -0
  17. package/dist/demo/renderers/colors.js +15 -0
  18. package/dist/demo/renderers/indicators.d.ts +7 -0
  19. package/dist/demo/renderers/indicators.js +14 -0
  20. package/dist/demo/renderers/list.d.ts +7 -0
  21. package/dist/demo/renderers/list.js +16 -0
  22. package/dist/demo/renderers/markdown.d.ts +7 -0
  23. package/dist/demo/renderers/markdown.js +15 -0
  24. package/dist/demo/renderers/progress.d.ts +7 -0
  25. package/dist/demo/renderers/progress.js +14 -0
  26. package/dist/demo/renderers/spinner.d.ts +7 -0
  27. package/dist/demo/renderers/spinner.js +16 -0
  28. package/dist/demo/renderers/table.d.ts +7 -0
  29. package/dist/demo/renderers/table.js +16 -0
  30. package/dist/demo/renderers/text.d.ts +7 -0
  31. package/dist/demo/renderers/text.js +13 -0
  32. package/dist/demo/renderers/tree.d.ts +7 -0
  33. package/dist/demo/renderers/tree.js +15 -0
  34. package/dist/demo/section.d.ts +4 -0
  35. package/dist/demo/section.js +20 -0
  36. package/dist/demo/templates.d.ts +3 -0
  37. package/dist/demo/templates.js +10 -0
  38. package/dist/demo/types.d.ts +2 -0
  39. package/dist/demo/types.js +8 -0
  40. package/dist/index.d.ts +31 -0
  41. package/dist/index.js +77 -0
  42. package/dist/list/index.d.ts +3 -0
  43. package/dist/list/index.js +9 -0
  44. package/dist/preset/full.d.ts +12 -0
  45. package/dist/preset/full.js +37 -0
  46. package/dist/preset/standard.d.ts +9 -0
  47. package/dist/preset/standard.js +26 -0
  48. package/dist/prompt/confirm.d.ts +4 -0
  49. package/dist/prompt/confirm.js +9 -0
  50. package/dist/prompt/group.d.ts +4 -0
  51. package/dist/prompt/group.js +9 -0
  52. package/dist/prompt/index.d.ts +7 -0
  53. package/dist/prompt/index.js +32 -0
  54. package/dist/prompt/select.d.ts +4 -0
  55. package/dist/prompt/select.js +11 -0
  56. package/dist/prompt/text.d.ts +4 -0
  57. package/dist/prompt/text.js +11 -0
  58. package/dist/prompt/types.d.ts +3 -0
  59. package/dist/prompt/types.js +8 -0
  60. package/dist/prompt/validators.d.ts +2 -0
  61. package/dist/prompt/validators.js +8 -0
  62. package/dist/render/borders.d.ts +2 -0
  63. package/dist/render/borders.js +15 -0
  64. package/dist/render/box.d.ts +3 -0
  65. package/dist/render/box.js +20 -0
  66. package/dist/render/date.d.ts +2 -0
  67. package/dist/render/date.js +12 -0
  68. package/dist/render/format-relative.d.ts +2 -0
  69. package/dist/render/format-relative.js +8 -0
  70. package/dist/render/format.d.ts +2 -0
  71. package/dist/render/format.js +10 -0
  72. package/dist/render/heading.d.ts +3 -0
  73. package/dist/render/heading.js +11 -0
  74. package/dist/render/index.d.ts +31 -0
  75. package/dist/render/index.js +222 -0
  76. package/dist/render/indicators.d.ts +2 -0
  77. package/dist/render/indicators.js +16 -0
  78. package/dist/render/json.d.ts +2 -0
  79. package/dist/render/json.js +10 -0
  80. package/dist/render/layout.d.ts +5 -0
  81. package/dist/render/layout.js +22 -0
  82. package/dist/render/list.d.ts +2 -0
  83. package/dist/render/list.js +8 -0
  84. package/dist/render/markdown.d.ts +2 -0
  85. package/dist/render/markdown.js +8 -0
  86. package/dist/render/progress.d.ts +2 -0
  87. package/dist/render/progress.js +8 -0
  88. package/dist/render/separator.d.ts +3 -0
  89. package/dist/render/separator.js +11 -0
  90. package/dist/render/shapes.d.ts +2 -0
  91. package/dist/render/shapes.js +32 -0
  92. package/dist/render/spinner.d.ts +2 -0
  93. package/dist/render/spinner.js +12 -0
  94. package/dist/render/stack.d.ts +3 -0
  95. package/dist/render/stack.js +35 -0
  96. package/dist/render/table.d.ts +3 -0
  97. package/dist/render/table.js +9 -0
  98. package/dist/render/tree.d.ts +2 -0
  99. package/dist/render/tree.js +10 -0
  100. package/dist/render/types.d.ts +2 -0
  101. package/dist/render/types.js +1 -0
  102. package/dist/shared/@outfitter/tui-011579t8.d.ts +24 -0
  103. package/dist/shared/@outfitter/tui-06dntkse.js +146 -0
  104. package/dist/shared/@outfitter/tui-0awf316b.js +71 -0
  105. package/dist/shared/@outfitter/tui-0b85rht7.js +1 -0
  106. package/dist/shared/@outfitter/tui-0mpqnyc1.js +55 -0
  107. package/dist/shared/@outfitter/tui-1qh888th.d.ts +106 -0
  108. package/dist/shared/@outfitter/tui-1tk0kxa6.js +94 -0
  109. package/dist/shared/@outfitter/tui-1wggw2zj.d.ts +56 -0
  110. package/dist/shared/@outfitter/tui-2pwhzg55.js +123 -0
  111. package/dist/shared/@outfitter/tui-2tkva96b.d.ts +20 -0
  112. package/dist/shared/@outfitter/tui-2wfat6jb.js +22 -0
  113. package/dist/shared/@outfitter/tui-34q2aenf.d.ts +24 -0
  114. package/dist/shared/@outfitter/tui-37hjcqhb.d.ts +3 -0
  115. package/dist/shared/@outfitter/tui-3dyxg62j.d.ts +97 -0
  116. package/dist/shared/@outfitter/tui-3kza6p9k.d.ts +132 -0
  117. package/dist/shared/@outfitter/tui-43bnfxv9.js +30 -0
  118. package/dist/shared/@outfitter/tui-4yz7jcyq.js +214 -0
  119. package/dist/shared/@outfitter/tui-52ndmswq.js +23 -0
  120. package/dist/shared/@outfitter/tui-53ms1ba4.d.ts +41 -0
  121. package/dist/shared/@outfitter/tui-5dsn6d6d.js +86 -0
  122. package/dist/shared/@outfitter/tui-5pb72vf9.js +51 -0
  123. package/dist/shared/@outfitter/tui-6605wa2j.js +127 -0
  124. package/dist/shared/@outfitter/tui-6ptks7jj.js +19 -0
  125. package/dist/shared/@outfitter/tui-6svngg69.js +126 -0
  126. package/dist/shared/@outfitter/tui-733asbd8.js +37 -0
  127. package/dist/shared/@outfitter/tui-75bztyfp.d.ts +50 -0
  128. package/dist/shared/@outfitter/tui-83zaah9b.d.ts +17 -0
  129. package/dist/shared/@outfitter/tui-8ejx8gq5.d.ts +71 -0
  130. package/dist/shared/@outfitter/tui-8j1gbehy.d.ts +164 -0
  131. package/dist/shared/@outfitter/tui-8mypsnva.d.ts +74 -0
  132. package/dist/shared/@outfitter/tui-997aapz0.js +61 -0
  133. package/dist/shared/@outfitter/tui-9dbykt4g.d.ts +42 -0
  134. package/dist/shared/@outfitter/tui-9h1kdd98.d.ts +119 -0
  135. package/dist/shared/@outfitter/tui-9rj9yesd.js +118 -0
  136. package/dist/shared/@outfitter/tui-a65efhsk.d.ts +23 -0
  137. package/dist/shared/@outfitter/tui-a8nkrbds.js +1 -0
  138. package/dist/shared/@outfitter/tui-ajp1153q.d.ts +59 -0
  139. package/dist/shared/@outfitter/tui-ay1fv41j.d.ts +30 -0
  140. package/dist/shared/@outfitter/tui-azh1w4ak.js +67 -0
  141. package/dist/shared/@outfitter/tui-b14ry1j1.d.ts +53 -0
  142. package/dist/shared/@outfitter/tui-c56sxcm8.d.ts +87 -0
  143. package/dist/shared/@outfitter/tui-c6ft5w6q.d.ts +91 -0
  144. package/dist/shared/@outfitter/tui-cq7za0cz.js +62 -0
  145. package/dist/shared/@outfitter/tui-dh15zwg0.js +95 -0
  146. package/dist/shared/@outfitter/tui-esc46z2v.js +20 -0
  147. package/dist/shared/@outfitter/tui-f1mj6h6p.d.ts +2 -0
  148. package/dist/shared/@outfitter/tui-gcpz529w.js +122 -0
  149. package/dist/shared/@outfitter/tui-gqsdhmk9.d.ts +42 -0
  150. package/dist/shared/@outfitter/tui-hescagw2.js +32 -0
  151. package/dist/shared/@outfitter/tui-j2kd7eej.js +126 -0
  152. package/dist/shared/@outfitter/tui-j3bkjt2g.js +7 -0
  153. package/dist/shared/@outfitter/tui-jnn9d8cd.js +8 -0
  154. package/dist/shared/@outfitter/tui-jz5nws55.d.ts +51 -0
  155. package/dist/shared/@outfitter/tui-k3mby2kk.js +70 -0
  156. package/dist/shared/@outfitter/tui-kc4nxak0.js +20 -0
  157. package/dist/shared/@outfitter/tui-kcxv8txp.js +7 -0
  158. package/dist/shared/@outfitter/tui-kydbggmj.js +39 -0
  159. package/dist/shared/@outfitter/tui-mch672g9.js +30 -0
  160. package/dist/shared/@outfitter/tui-n9kxkdrh.js +82 -0
  161. package/dist/shared/@outfitter/tui-na4dnjpw.js +348 -0
  162. package/dist/shared/@outfitter/tui-ncaatp4j.js +25 -0
  163. package/dist/shared/@outfitter/tui-nce7fgtf.js +48 -0
  164. package/dist/shared/@outfitter/tui-ngz2fbdw.js +144 -0
  165. package/dist/shared/@outfitter/tui-nprd7g0d.js +52 -0
  166. package/dist/shared/@outfitter/tui-nr580mbv.js +272 -0
  167. package/dist/shared/@outfitter/tui-nr93tf31.d.ts +64 -0
  168. package/dist/shared/@outfitter/tui-pdwbbzwr.js +179 -0
  169. package/dist/shared/@outfitter/tui-qb07rtct.js +19 -0
  170. package/dist/shared/@outfitter/tui-qkyazctw.d.ts +36 -0
  171. package/dist/shared/@outfitter/tui-qn1rgz9v.d.ts +93 -0
  172. package/dist/shared/@outfitter/tui-qs3fhwp8.js +43 -0
  173. package/dist/shared/@outfitter/tui-r8hywf9m.d.ts +48 -0
  174. package/dist/shared/@outfitter/tui-rbbcc034.js +20 -0
  175. package/dist/shared/@outfitter/tui-rgkerz72.js +85 -0
  176. package/dist/shared/@outfitter/tui-rh52sycm.d.ts +45 -0
  177. package/dist/shared/@outfitter/tui-rr924x4z.d.ts +59 -0
  178. package/dist/shared/@outfitter/tui-rwpdjrt3.js +20 -0
  179. package/dist/shared/@outfitter/tui-rx9xq9s7.d.ts +26 -0
  180. package/dist/shared/@outfitter/tui-sk05ye6g.js +1 -0
  181. package/dist/shared/@outfitter/tui-sv2xmh3w.d.ts +26 -0
  182. package/dist/shared/@outfitter/tui-t1a9xgbd.js +111 -0
  183. package/dist/shared/@outfitter/tui-t9vd88jr.js +273 -0
  184. package/dist/shared/@outfitter/tui-tq1z78x0.js +11 -0
  185. package/dist/shared/@outfitter/tui-ts1f957s.d.ts +128 -0
  186. package/dist/shared/@outfitter/tui-ve0083wa.d.ts +61 -0
  187. package/dist/shared/@outfitter/tui-vt0wg6c4.js +54 -0
  188. package/dist/shared/@outfitter/tui-wfnnq0zq.d.ts +328 -0
  189. package/dist/shared/@outfitter/tui-x3yg0887.d.ts +223 -0
  190. package/dist/shared/@outfitter/tui-x9vvtfkk.js +20 -0
  191. package/dist/shared/@outfitter/tui-xbvz707j.js +20 -0
  192. package/dist/shared/@outfitter/tui-xbx6d4t7.js +1 -0
  193. package/dist/shared/@outfitter/tui-xph95b7h.js +67 -0
  194. package/dist/shared/@outfitter/tui-xqjx7d1f.js +135 -0
  195. package/dist/shared/@outfitter/tui-xrgrp99k.d.ts +112 -0
  196. package/dist/shared/@outfitter/tui-xzjv96ps.d.ts +300 -0
  197. package/dist/shared/@outfitter/tui-ykzs6v4v.d.ts +66 -0
  198. package/dist/shared/@outfitter/tui-yw9n5h6q.d.ts +54 -0
  199. package/dist/shared/@outfitter/tui-ywmakc6b.js +30 -0
  200. package/dist/shared/@outfitter/tui-z6nf9b9h.js +7 -0
  201. package/dist/shared/@outfitter/tui-zcspg6z6.d.ts +190 -0
  202. package/dist/shared/@outfitter/tui-zhbsnj7w.js +1 -0
  203. package/dist/streaming/ansi.d.ts +2 -0
  204. package/dist/streaming/ansi.js +8 -0
  205. package/dist/streaming/index.d.ts +4 -0
  206. package/dist/streaming/index.js +17 -0
  207. package/dist/streaming/spinner.d.ts +3 -0
  208. package/dist/streaming/spinner.js +10 -0
  209. package/dist/streaming/writer.d.ts +2 -0
  210. package/dist/streaming/writer.js +9 -0
  211. package/dist/table/index.d.ts +4 -0
  212. package/dist/table/index.js +10 -0
  213. package/dist/theme/context.d.ts +9 -0
  214. package/dist/theme/context.js +12 -0
  215. package/dist/theme/create.d.ts +8 -0
  216. package/dist/theme/create.js +10 -0
  217. package/dist/theme/index.d.ts +17 -0
  218. package/dist/theme/index.js +40 -0
  219. package/dist/theme/presets/bold.d.ts +8 -0
  220. package/dist/theme/presets/bold.js +10 -0
  221. package/dist/theme/presets/default.d.ts +8 -0
  222. package/dist/theme/presets/default.js +9 -0
  223. package/dist/theme/presets/index.d.ts +12 -0
  224. package/dist/theme/presets/index.js +22 -0
  225. package/dist/theme/presets/minimal.d.ts +8 -0
  226. package/dist/theme/presets/minimal.js +10 -0
  227. package/dist/theme/presets/rounded.d.ts +8 -0
  228. package/dist/theme/presets/rounded.js +10 -0
  229. package/dist/theme/resolve.d.ts +8 -0
  230. package/dist/theme/resolve.js +11 -0
  231. package/dist/theme/types.d.ts +7 -0
  232. package/dist/theme/types.js +1 -0
  233. package/dist/tree/index.d.ts +3 -0
  234. package/dist/tree/index.js +11 -0
  235. package/package.json +263 -0
@@ -0,0 +1,26 @@
1
+ import { VisualTheme } from "./tui-8j1gbehy";
2
+ /**
3
+ * Minimal visual theme.
4
+ *
5
+ * Features:
6
+ * - ASCII-only borders (+, -, |)
7
+ * - ASCII-safe list bullets and checkboxes
8
+ * - Line spinner (ASCII-compatible)
9
+ * - No unicode characters anywhere
10
+ *
11
+ * Use this theme when targeting terminals without unicode support
12
+ * or when running in CI/log environments.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { minimalTheme } from "@outfitter/tui/theme/presets";
17
+ *
18
+ * // Use ASCII-safe styling
19
+ * const box = renderBox("Hello", { border: minimalTheme.border });
20
+ * // +-------+
21
+ * // | Hello |
22
+ * // +-------+
23
+ * ```
24
+ */
25
+ declare const minimalTheme: VisualTheme;
26
+ export { minimalTheme };
@@ -0,0 +1,111 @@
1
+ // @bun
2
+ import {
3
+ getBorderCharacters,
4
+ init_borders
5
+ } from "./tui-2pwhzg55.js";
6
+
7
+ // packages/tui/src/render/table.ts
8
+ init_borders();
9
+ import { getStringWidth, padText, truncateText } from "@outfitter/cli/text";
10
+ function renderTable(data, options) {
11
+ if (data.length === 0) {
12
+ return "";
13
+ }
14
+ const allKeys = new Set;
15
+ for (const row of data) {
16
+ for (const key of Object.keys(row)) {
17
+ allKeys.add(key);
18
+ }
19
+ }
20
+ const keys = Array.from(allKeys);
21
+ const headers = options?.headers ?? {};
22
+ const getHeader = (key) => headers[key] ?? key;
23
+ const columnWidths = {};
24
+ for (const key of keys) {
25
+ const headerWidth = getStringWidth(getHeader(key));
26
+ let maxDataWidth = 0;
27
+ for (const row of data) {
28
+ const value = row[key];
29
+ const strValue = value === undefined || value === null ? "" : String(value);
30
+ const width = getStringWidth(strValue);
31
+ if (width > maxDataWidth) {
32
+ maxDataWidth = width;
33
+ }
34
+ }
35
+ const optionWidth = options?.columnWidths?.[key];
36
+ columnWidths[key] = optionWidth ?? Math.max(headerWidth, maxDataWidth);
37
+ }
38
+ const compact = options?.compact ?? false;
39
+ const borderStyle = compact ? "none" : options?.border ?? "single";
40
+ const chars = getBorderCharacters(borderStyle);
41
+ const hasBorders = borderStyle !== "none";
42
+ return hasBorders ? renderWithBorders(data, keys, columnWidths, getHeader, chars) : renderCompact(data, keys, columnWidths, getHeader);
43
+ }
44
+ function renderWithBorders(data, keys, columnWidths, getHeader, chars) {
45
+ const lines = [];
46
+ const colWidthsForBorder = keys.map((k) => (columnWidths[k] ?? 0) + 2);
47
+ lines.push(drawHorizontalBorder(chars, colWidthsForBorder, "top"));
48
+ lines.push(drawDataRow(keys.map((k) => getHeader(k)), keys.map((k) => columnWidths[k] ?? 0), chars.vertical));
49
+ lines.push(drawHorizontalBorder(chars, colWidthsForBorder, "middle"));
50
+ for (const row of data) {
51
+ const values = keys.map((k) => {
52
+ const value = row[k];
53
+ let strValue = value === undefined || value === null ? "" : String(value);
54
+ const width = columnWidths[k] ?? 0;
55
+ if (getStringWidth(strValue) > width) {
56
+ strValue = truncateText(strValue, width);
57
+ }
58
+ return strValue;
59
+ });
60
+ lines.push(drawDataRow(values, keys.map((k) => columnWidths[k] ?? 0), chars.vertical));
61
+ }
62
+ lines.push(drawHorizontalBorder(chars, colWidthsForBorder, "bottom"));
63
+ return lines.join(`
64
+ `);
65
+ }
66
+ function renderCompact(data, keys, columnWidths, getHeader) {
67
+ const lines = [];
68
+ const headerValues = keys.map((k) => {
69
+ const header = getHeader(k);
70
+ const width = columnWidths[k] ?? 0;
71
+ return padText(header, width);
72
+ });
73
+ lines.push(headerValues.join(" "));
74
+ for (const row of data) {
75
+ const values = keys.map((k) => {
76
+ const value = row[k];
77
+ let strValue = value === undefined || value === null ? "" : String(value);
78
+ const width = columnWidths[k] ?? 0;
79
+ if (getStringWidth(strValue) > width) {
80
+ strValue = truncateText(strValue, width);
81
+ }
82
+ return padText(strValue, width);
83
+ });
84
+ lines.push(values.join(" "));
85
+ }
86
+ return lines.join(`
87
+ `);
88
+ }
89
+ function drawHorizontalBorder(chars, colWidths, position) {
90
+ const positionChars = {
91
+ top: { left: chars.topLeft, right: chars.topRight, cross: chars.topT },
92
+ middle: { left: chars.leftT, right: chars.rightT, cross: chars.cross },
93
+ bottom: {
94
+ left: chars.bottomLeft,
95
+ right: chars.bottomRight,
96
+ cross: chars.bottomT
97
+ }
98
+ };
99
+ const { left, right, cross } = positionChars[position];
100
+ const segments = colWidths.map((w) => chars.horizontal.repeat(w));
101
+ return `${left}${segments.join(cross)}${right}`;
102
+ }
103
+ function drawDataRow(values, widths, vertical) {
104
+ const cells = values.map((value, i) => {
105
+ const width = widths[i] ?? 0;
106
+ return ` ${padText(value, width)} `;
107
+ });
108
+ return `${vertical}${cells.join(vertical)}${vertical}`;
109
+ }
110
+
111
+ export { renderTable };
@@ -0,0 +1,273 @@
1
+ // @bun
2
+ import {
3
+ getBorderCharacters,
4
+ init_borders
5
+ } from "./tui-2pwhzg55.js";
6
+ import {
7
+ __esm,
8
+ __export
9
+ } from "./tui-hescagw2.js";
10
+
11
+ // packages/tui/src/render/box.ts
12
+ var exports_box = {};
13
+ __export(exports_box, {
14
+ renderBox: () => renderBox,
15
+ normalizePadding: () => normalizePadding,
16
+ normalizeMargin: () => normalizeMargin,
17
+ normalizeBorders: () => normalizeBorders,
18
+ createBox: () => createBox
19
+ });
20
+ import { getStringWidth, truncateText, wrapText } from "@outfitter/cli/text";
21
+ function alignLine(line, width, align) {
22
+ const lineWidth = getStringWidth(line);
23
+ const padding = width - lineWidth;
24
+ if (padding <= 0) {
25
+ return line;
26
+ }
27
+ switch (align) {
28
+ case "center": {
29
+ const leftPad = Math.floor(padding / 2);
30
+ const rightPad = padding - leftPad;
31
+ return " ".repeat(leftPad) + line + " ".repeat(rightPad);
32
+ }
33
+ case "right":
34
+ return " ".repeat(padding) + line;
35
+ default:
36
+ return line + " ".repeat(padding);
37
+ }
38
+ }
39
+ function normalizePadding(padding, defaultValue) {
40
+ if (padding === undefined) {
41
+ return { top: 0, right: defaultValue, bottom: 0, left: defaultValue };
42
+ }
43
+ if (typeof padding === "number") {
44
+ return { top: 0, right: padding, bottom: 0, left: padding };
45
+ }
46
+ return {
47
+ top: padding.top ?? 0,
48
+ right: padding.right ?? defaultValue,
49
+ bottom: padding.bottom ?? 0,
50
+ left: padding.left ?? defaultValue
51
+ };
52
+ }
53
+ function normalizeMargin(margin, defaultValue) {
54
+ if (margin === undefined) {
55
+ return {
56
+ top: defaultValue,
57
+ right: defaultValue,
58
+ bottom: defaultValue,
59
+ left: defaultValue
60
+ };
61
+ }
62
+ if (typeof margin === "number") {
63
+ return { top: margin, right: margin, bottom: margin, left: margin };
64
+ }
65
+ return {
66
+ top: margin.top ?? defaultValue,
67
+ right: margin.right ?? defaultValue,
68
+ bottom: margin.bottom ?? defaultValue,
69
+ left: margin.left ?? defaultValue
70
+ };
71
+ }
72
+ function normalizeBorders(borders) {
73
+ if (borders === undefined) {
74
+ return { top: true, right: true, bottom: true, left: true };
75
+ }
76
+ return {
77
+ top: borders.top ?? true,
78
+ right: borders.right ?? true,
79
+ bottom: borders.bottom ?? true,
80
+ left: borders.left ?? true
81
+ };
82
+ }
83
+ function renderBox(content, options) {
84
+ const border = options?.border ?? "single";
85
+ const title = options?.title;
86
+ const align = options?.align ?? "left";
87
+ const fixedWidth = options?.width;
88
+ const sections = options?.sections;
89
+ const pad = normalizePadding(options?.padding, 1);
90
+ const margin = normalizeMargin(options?.margin, 0);
91
+ const borders = normalizeBorders(options?.borders);
92
+ const chars = getBorderCharacters(border);
93
+ let lines;
94
+ let sectionBoundaries = [];
95
+ if (sections && sections.length > 0) {
96
+ lines = [];
97
+ for (const [idx, section] of sections.entries()) {
98
+ const sectionLines = typeof section === "string" ? section.split(`
99
+ `) : section;
100
+ lines.push(...sectionLines);
101
+ if (idx < sections.length - 1) {
102
+ sectionBoundaries.push(lines.length);
103
+ }
104
+ }
105
+ } else if (typeof content === "string") {
106
+ lines = content.split(`
107
+ `);
108
+ } else {
109
+ lines = content;
110
+ }
111
+ const leftOverhead = (borders.left ? 1 : 0) + pad.left;
112
+ const rightOverhead = (borders.right ? 1 : 0) + pad.right;
113
+ const horizontalOverhead = leftOverhead + rightOverhead;
114
+ if (fixedWidth) {
115
+ const contentWidthForWrap = fixedWidth - horizontalOverhead;
116
+ if (contentWidthForWrap > 0) {
117
+ const wrappedLines = [];
118
+ const newBoundaries = [];
119
+ let boundaryIdx2 = 0;
120
+ let originalLineCount = 0;
121
+ for (const line of lines) {
122
+ if (getStringWidth(line) > contentWidthForWrap) {
123
+ const wrapped = wrapText(line, contentWidthForWrap);
124
+ wrappedLines.push(...wrapped.split(`
125
+ `));
126
+ } else {
127
+ wrappedLines.push(line);
128
+ }
129
+ originalLineCount++;
130
+ const boundary = sectionBoundaries[boundaryIdx2];
131
+ if (boundary !== undefined && originalLineCount === boundary) {
132
+ newBoundaries.push(wrappedLines.length);
133
+ boundaryIdx2++;
134
+ }
135
+ }
136
+ lines = wrappedLines;
137
+ sectionBoundaries = newBoundaries;
138
+ }
139
+ }
140
+ let contentWidth = 0;
141
+ for (const line of lines) {
142
+ const w = getStringWidth(line);
143
+ if (w > contentWidth) {
144
+ contentWidth = w;
145
+ }
146
+ }
147
+ let boxWidth;
148
+ if (fixedWidth) {
149
+ boxWidth = fixedWidth;
150
+ contentWidth = fixedWidth - horizontalOverhead;
151
+ } else {
152
+ boxWidth = horizontalOverhead + contentWidth;
153
+ if (title && borders.top) {
154
+ const minBoxWidthForTitle = getStringWidth(title) + 4 + (borders.left ? 1 : 0) + (borders.right ? 1 : 0);
155
+ if (boxWidth < minBoxWidthForTitle) {
156
+ boxWidth = minBoxWidthForTitle;
157
+ contentWidth = boxWidth - horizontalOverhead;
158
+ }
159
+ }
160
+ }
161
+ const output = [];
162
+ const leftPaddingStr = " ".repeat(pad.left);
163
+ const rightPaddingStr = " ".repeat(pad.right);
164
+ const marginLeftStr = " ".repeat(margin.left);
165
+ const marginRightStr = " ".repeat(margin.right);
166
+ const buildContentLine = (lineContent) => {
167
+ const aligned = alignLine(lineContent, contentWidth, align);
168
+ const leftBorder = borders.left ? chars.vertical : "";
169
+ const rightBorder = borders.right ? chars.vertical : "";
170
+ return marginLeftStr + leftBorder + leftPaddingStr + aligned + rightPaddingStr + rightBorder + marginRightStr;
171
+ };
172
+ const innerWidth = boxWidth - (borders.left ? 1 : 0) - (borders.right ? 1 : 0);
173
+ for (let i = 0;i < margin.top; i++) {
174
+ output.push("");
175
+ }
176
+ if (borders.top) {
177
+ let topBorder;
178
+ if (title) {
179
+ const maxTitleWidth = Math.max(0, innerWidth - 4);
180
+ let displayTitle = title;
181
+ if (maxTitleWidth === 0) {
182
+ topBorder = marginLeftStr + (borders.left ? chars.topLeft : "") + chars.horizontal.repeat(Math.max(0, innerWidth)) + (borders.right ? chars.topRight : "") + marginRightStr;
183
+ } else {
184
+ if (getStringWidth(title) > maxTitleWidth) {
185
+ displayTitle = truncateText(title, maxTitleWidth);
186
+ }
187
+ const titlePart = `${chars.horizontal} ${displayTitle} `;
188
+ const remainingWidth = Math.max(0, innerWidth - getStringWidth(titlePart));
189
+ topBorder = marginLeftStr + (borders.left ? chars.topLeft : "") + titlePart + chars.horizontal.repeat(remainingWidth) + (borders.right ? chars.topRight : "") + marginRightStr;
190
+ }
191
+ } else {
192
+ topBorder = marginLeftStr + (borders.left ? chars.topLeft : "") + chars.horizontal.repeat(innerWidth) + (borders.right ? chars.topRight : "") + marginRightStr;
193
+ }
194
+ output.push(topBorder);
195
+ }
196
+ for (let i = 0;i < pad.top; i++) {
197
+ output.push(buildContentLine(""));
198
+ }
199
+ let boundaryIdx = 0;
200
+ for (const [idx, line] of lines.entries()) {
201
+ output.push(buildContentLine(line));
202
+ const boundary = sectionBoundaries[boundaryIdx];
203
+ if (boundary !== undefined && idx + 1 === boundary) {
204
+ const divider = marginLeftStr + (borders.left ? chars.leftT : "") + chars.horizontal.repeat(innerWidth) + (borders.right ? chars.rightT : "") + marginRightStr;
205
+ output.push(divider);
206
+ boundaryIdx++;
207
+ }
208
+ }
209
+ for (let i = 0;i < pad.bottom; i++) {
210
+ output.push(buildContentLine(""));
211
+ }
212
+ if (borders.bottom) {
213
+ const bottomBorder = marginLeftStr + (borders.left ? chars.bottomLeft : "") + chars.horizontal.repeat(innerWidth) + (borders.right ? chars.bottomRight : "") + marginRightStr;
214
+ output.push(bottomBorder);
215
+ }
216
+ for (let i = 0;i < margin.bottom; i++) {
217
+ output.push("");
218
+ }
219
+ return output.join(`
220
+ `);
221
+ }
222
+ function isBox(value) {
223
+ return typeof value === "object" && value !== null && "output" in value && "width" in value && "height" in value && typeof value.output === "string" && typeof value.width === "number" && typeof value.height === "number";
224
+ }
225
+ function contentToLines(content) {
226
+ if (Array.isArray(content)) {
227
+ const lines = [];
228
+ for (const item of content) {
229
+ if (isBox(item)) {
230
+ lines.push(...item.output.split(`
231
+ `));
232
+ } else if (Array.isArray(item)) {
233
+ lines.push(...item);
234
+ } else {
235
+ lines.push(...item.split(`
236
+ `));
237
+ }
238
+ }
239
+ return lines;
240
+ }
241
+ if (isBox(content)) {
242
+ return content.output.split(`
243
+ `);
244
+ }
245
+ if (typeof content === "string") {
246
+ return content.split(`
247
+ `);
248
+ }
249
+ return content;
250
+ }
251
+ function createBox(content, options) {
252
+ const lines = contentToLines(content);
253
+ const output = renderBox(lines, options);
254
+ const outputLines = output.split(`
255
+ `);
256
+ let maxWidth = 0;
257
+ for (const line of outputLines) {
258
+ const lineWidth = getStringWidth(line);
259
+ if (lineWidth > maxWidth) {
260
+ maxWidth = lineWidth;
261
+ }
262
+ }
263
+ return {
264
+ output,
265
+ width: maxWidth,
266
+ height: outputLines.length
267
+ };
268
+ }
269
+ var init_box = __esm(() => {
270
+ init_borders();
271
+ });
272
+
273
+ export { normalizePadding, normalizeMargin, normalizeBorders, renderBox, createBox, exports_box, init_box };
@@ -0,0 +1,11 @@
1
+ // @bun
2
+ // packages/tui/src/render/json.ts
3
+ function renderJson(data) {
4
+ const json = JSON.stringify(data, null, 2);
5
+ return json;
6
+ }
7
+ function renderText(text) {
8
+ return text;
9
+ }
10
+
11
+ export { renderJson, renderText };
@@ -0,0 +1,128 @@
1
+ import { CaseMode, SeparatorStyle } from "./tui-jz5nws55";
2
+ import { Theme } from "@outfitter/cli/colors";
3
+ /**
4
+ * Options for creating a demo section.
5
+ */
6
+ interface SectionOptions {
7
+ /** Separator character style (default: "─") */
8
+ separator?: SeparatorStyle;
9
+ /** Case transformation (default: "title") */
10
+ case?: CaseMode;
11
+ }
12
+ /**
13
+ * Options for creating a subsection.
14
+ */
15
+ interface SubsectionOptions {
16
+ /** Separator character style (default: "─") */
17
+ separator?: SeparatorStyle;
18
+ /** Case transformation (default: "title") */
19
+ case?: CaseMode;
20
+ }
21
+ /**
22
+ * Creates a demo section heading.
23
+ *
24
+ * Sections are major divisions in a demo, rendered with title case text
25
+ * and thin Unicode line separators by default.
26
+ *
27
+ * @param title - The section title
28
+ * @param options - Optional rendering options
29
+ * @returns Formatted section heading string
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { demoSection } from "@outfitter/tui/demo";
34
+ *
35
+ * console.log(demoSection("Theme Colors"));
36
+ * // Theme Colors
37
+ * // ────────────
38
+ * ```
39
+ */
40
+ declare function demoSection(title: string, options?: SectionOptions): string;
41
+ /**
42
+ * Creates a demo subsection heading.
43
+ *
44
+ * Subsections are minor divisions within a section, rendered with title case
45
+ * text and thin Unicode line separators by default.
46
+ *
47
+ * @param title - The subsection title
48
+ * @param options - Optional rendering options
49
+ * @returns Formatted subsection heading string
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * import { demoSubsection } from "@outfitter/tui/demo";
54
+ *
55
+ * console.log(demoSubsection("Status"));
56
+ * // Status
57
+ * // ──────
58
+ * ```
59
+ */
60
+ declare function demoSubsection(title: string, options?: SubsectionOptions): string;
61
+ /**
62
+ * Creates a code block for demo sections.
63
+ *
64
+ * Code blocks are displayed when `showCode` is enabled and provide
65
+ * usage examples for the primitives being demonstrated.
66
+ *
67
+ * @param lines - Lines of code to display
68
+ * @param show - Whether to show the code block (default: true)
69
+ * @returns Array of code lines, or empty array if hidden
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * import { codeBlock } from "@outfitter/tui/demo";
74
+ *
75
+ * const code = codeBlock([
76
+ * 'import { renderBox } from "@outfitter/tui/render";',
77
+ * "",
78
+ * 'renderBox("Hello")',
79
+ * ], true);
80
+ * ```
81
+ */
82
+ declare function codeBlock(lines: string[], show?: boolean): string[];
83
+ /**
84
+ * Creates a description line styled with muted theme.
85
+ *
86
+ * Descriptions provide contextual information about a demo section
87
+ * when `showDescriptions` is enabled.
88
+ *
89
+ * @param text - The description text
90
+ * @param theme - Theme for styling
91
+ * @param show - Whether to show the description (default: true)
92
+ * @returns Array with styled description, or empty array if hidden
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * import { description } from "@outfitter/tui/demo";
97
+ *
98
+ * const desc = description(
99
+ * "Control padding per side with an object.",
100
+ * theme,
101
+ * true
102
+ * );
103
+ * ```
104
+ */
105
+ declare function description(text: string, theme: Theme, show?: boolean): string[];
106
+ /**
107
+ * Joins demo content with vertical spacing.
108
+ *
109
+ * Wraps `joinVertical` with demo-appropriate defaults.
110
+ *
111
+ * @param blocks - Content blocks to join
112
+ * @param gap - Lines of spacing between blocks (default: 1)
113
+ * @returns Combined string with blocks stacked
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * import { demoSection, demoContent } from "@outfitter/tui/demo";
118
+ *
119
+ * const output = demoContent([
120
+ * demoSection("Colors"),
121
+ * "Color examples here...",
122
+ * demoSection("Borders"),
123
+ * "Border examples here...",
124
+ * ]);
125
+ * ```
126
+ */
127
+ declare function demoContent(blocks: string[], gap?: number): string;
128
+ export { SectionOptions, SubsectionOptions, demoSection, demoSubsection, codeBlock, description, demoContent };
@@ -0,0 +1,61 @@
1
+ import { PartialVisualTheme, VisualTheme } from "./tui-8j1gbehy";
2
+ /**
3
+ * Options for creating a visual theme.
4
+ */
5
+ interface CreateVisualThemeOptions {
6
+ /**
7
+ * Base theme to extend from.
8
+ * Properties not overridden will be inherited from this theme.
9
+ * @default defaultTheme
10
+ */
11
+ extends?: VisualTheme;
12
+ /**
13
+ * Properties to override on the base/extended theme.
14
+ * Supports partial overrides for nested objects (colors, spacing, markers).
15
+ */
16
+ overrides?: PartialVisualTheme;
17
+ }
18
+ /**
19
+ * Creates a visual theme by extending and/or overriding a base theme.
20
+ *
21
+ * This is the primary factory for creating custom themes. You can:
22
+ * - Start from scratch (no options → defaultTheme)
23
+ * - Extend an existing preset
24
+ * - Override specific properties
25
+ * - Combine extension and overrides
26
+ *
27
+ * When changing the `border` property, `borderChars` is automatically
28
+ * updated to match the new border style.
29
+ *
30
+ * @param options - Configuration for theme creation
31
+ * @returns A complete VisualTheme
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * // Start with defaults
36
+ * const theme = createVisualTheme();
37
+ *
38
+ * // Extend a preset
39
+ * const rounded = createVisualTheme({ extends: roundedTheme });
40
+ *
41
+ * // Override specific properties
42
+ * const custom = createVisualTheme({
43
+ * overrides: {
44
+ * border: "double",
45
+ * colors: { success: "\x1b[38;5;82m" },
46
+ * spacing: { boxPadding: 2 }
47
+ * }
48
+ * });
49
+ *
50
+ * // Extend and override
51
+ * const brandTheme = createVisualTheme({
52
+ * extends: roundedTheme,
53
+ * overrides: {
54
+ * name: "brand",
55
+ * colors: { accent: "\x1b[38;5;39m" }
56
+ * }
57
+ * });
58
+ * ```
59
+ */
60
+ declare function createVisualTheme(options?: CreateVisualThemeOptions): VisualTheme;
61
+ export { CreateVisualThemeOptions, createVisualTheme };
@@ -0,0 +1,54 @@
1
+ // @bun
2
+ // packages/tui/src/render/markdown.ts
3
+ import { ANSI } from "@outfitter/cli/colors";
4
+ import { supportsColor } from "@outfitter/cli/terminal";
5
+ function renderMarkdown(markdown) {
6
+ const colorEnabled = supportsColor();
7
+ let result = markdown;
8
+ result = result.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, _lang, code) => {
9
+ const trimmed = code.trimEnd();
10
+ if (colorEnabled) {
11
+ return `${ANSI.dim}${trimmed}${ANSI.reset}`;
12
+ }
13
+ return trimmed;
14
+ });
15
+ result = result.replace(/^(#{1,6})\s+(.+)$/gm, (_match, _hashes, text) => {
16
+ if (colorEnabled) {
17
+ return `${ANSI.bold}${text}${ANSI.reset}`;
18
+ }
19
+ return text;
20
+ });
21
+ result = result.replace(/\*\*(.+?)\*\*/g, (_match, text) => {
22
+ if (colorEnabled) {
23
+ return `${ANSI.bold}${text}${ANSI.reset}`;
24
+ }
25
+ return text;
26
+ });
27
+ result = result.replace(/__(.+?)__/g, (_match, text) => {
28
+ if (colorEnabled) {
29
+ return `${ANSI.bold}${text}${ANSI.reset}`;
30
+ }
31
+ return text;
32
+ });
33
+ result = result.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, (_match, text) => {
34
+ if (colorEnabled) {
35
+ return `${ANSI.italic}${text}${ANSI.reset}`;
36
+ }
37
+ return text;
38
+ });
39
+ result = result.replace(/(?<!_)_([^_]+)_(?!_)/g, (_match, text) => {
40
+ if (colorEnabled) {
41
+ return `${ANSI.italic}${text}${ANSI.reset}`;
42
+ }
43
+ return text;
44
+ });
45
+ result = result.replace(/`([^`]+)`/g, (_match, text) => {
46
+ if (colorEnabled) {
47
+ return `${ANSI.cyan}${text}${ANSI.reset}`;
48
+ }
49
+ return text;
50
+ });
51
+ return result;
52
+ }
53
+
54
+ export { renderMarkdown };