@silvery/examples 0.5.6 → 0.17.4

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 (112) hide show
  1. package/dist/UPNG-Cy7ViL8f.mjs +5074 -0
  2. package/dist/__vite-browser-external-2447137e-BML7CYau.mjs +4 -0
  3. package/dist/_banner-DLPxCqVy.mjs +44 -0
  4. package/dist/ansi-CCE2pVS0.mjs +16397 -0
  5. package/dist/apng-HhhBjRGt.mjs +68 -0
  6. package/dist/apng-mwUQbTTF.mjs +3 -0
  7. package/dist/apps/aichat/index.mjs +1299 -0
  8. package/dist/apps/app-todo.mjs +139 -0
  9. package/dist/apps/async-data.mjs +204 -0
  10. package/dist/apps/cli-wizard.mjs +339 -0
  11. package/dist/apps/clipboard.mjs +198 -0
  12. package/dist/apps/components.mjs +864 -0
  13. package/dist/apps/data-explorer.mjs +483 -0
  14. package/dist/apps/dev-tools.mjs +397 -0
  15. package/dist/apps/explorer.mjs +698 -0
  16. package/dist/apps/gallery.mjs +766 -0
  17. package/dist/apps/inline-bench.mjs +115 -0
  18. package/dist/apps/kanban.mjs +280 -0
  19. package/dist/apps/layout-ref.mjs +187 -0
  20. package/dist/apps/outline.mjs +203 -0
  21. package/dist/apps/paste-demo.mjs +189 -0
  22. package/dist/apps/scroll.mjs +86 -0
  23. package/dist/apps/search-filter.mjs +287 -0
  24. package/dist/apps/selection.mjs +355 -0
  25. package/dist/apps/spatial-focus-demo.mjs +388 -0
  26. package/dist/apps/task-list.mjs +258 -0
  27. package/dist/apps/terminal-caps-demo.mjs +315 -0
  28. package/dist/apps/terminal.mjs +872 -0
  29. package/dist/apps/text-selection-demo.mjs +254 -0
  30. package/dist/apps/textarea.mjs +178 -0
  31. package/dist/apps/theme.mjs +661 -0
  32. package/dist/apps/transform.mjs +215 -0
  33. package/dist/apps/virtual-10k.mjs +422 -0
  34. package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
  35. package/dist/backends-Bahh9mKN.mjs +1179 -0
  36. package/dist/backends-CCtCDQ94.mjs +3 -0
  37. package/dist/{cli.mjs → bin/cli.mjs} +21 -25
  38. package/dist/chunk-BSw8zbkd.mjs +37 -0
  39. package/dist/components/counter.mjs +48 -0
  40. package/dist/components/hello.mjs +31 -0
  41. package/dist/components/progress-bar.mjs +59 -0
  42. package/dist/components/select-list.mjs +85 -0
  43. package/dist/components/spinner.mjs +57 -0
  44. package/dist/components/text-input.mjs +62 -0
  45. package/dist/components/virtual-list.mjs +51 -0
  46. package/dist/flexily-zero-adapter-UB-ra8fR.mjs +3374 -0
  47. package/dist/gif-BZaqPPVX.mjs +3 -0
  48. package/dist/gif-BtnXuxLF.mjs +71 -0
  49. package/dist/gifenc-CLRW41dk.mjs +728 -0
  50. package/dist/jsx-runtime-dMs_8fNu.mjs +241 -0
  51. package/dist/key-mapping-5oYQdAQE.mjs +3 -0
  52. package/dist/key-mapping-D4LR1go6.mjs +130 -0
  53. package/dist/layout/dashboard.mjs +1204 -0
  54. package/dist/layout/live-resize.mjs +303 -0
  55. package/dist/layout/overflow.mjs +70 -0
  56. package/dist/layout/text-layout.mjs +335 -0
  57. package/dist/node-NuJ94BWl.mjs +1083 -0
  58. package/dist/plugins-D1KtkT4a.mjs +3057 -0
  59. package/dist/resvg-js-C_8Wps1F.mjs +201 -0
  60. package/dist/src-BTEVGpd9.mjs +23538 -0
  61. package/dist/src-CUUOuRH6.mjs +5322 -0
  62. package/dist/src-CzfRafCQ.mjs +814 -0
  63. package/dist/usingCtx-CsEf0xO3.mjs +57 -0
  64. package/dist/yoga-adapter-BVtQ5OJR.mjs +237 -0
  65. package/package.json +19 -14
  66. package/_banner.tsx +0 -60
  67. package/apps/aichat/components.tsx +0 -469
  68. package/apps/aichat/index.tsx +0 -220
  69. package/apps/aichat/script.ts +0 -460
  70. package/apps/aichat/state.ts +0 -325
  71. package/apps/aichat/types.ts +0 -19
  72. package/apps/app-todo.tsx +0 -201
  73. package/apps/async-data.tsx +0 -196
  74. package/apps/cli-wizard.tsx +0 -332
  75. package/apps/clipboard.tsx +0 -183
  76. package/apps/components.tsx +0 -658
  77. package/apps/data-explorer.tsx +0 -490
  78. package/apps/dev-tools.tsx +0 -395
  79. package/apps/explorer.tsx +0 -731
  80. package/apps/gallery.tsx +0 -653
  81. package/apps/inline-bench.tsx +0 -138
  82. package/apps/kanban.tsx +0 -265
  83. package/apps/layout-ref.tsx +0 -173
  84. package/apps/outline.tsx +0 -160
  85. package/apps/panes/index.tsx +0 -203
  86. package/apps/paste-demo.tsx +0 -185
  87. package/apps/scroll.tsx +0 -77
  88. package/apps/search-filter.tsx +0 -240
  89. package/apps/selection.tsx +0 -342
  90. package/apps/spatial-focus-demo.tsx +0 -368
  91. package/apps/task-list.tsx +0 -271
  92. package/apps/terminal-caps-demo.tsx +0 -334
  93. package/apps/terminal.tsx +0 -800
  94. package/apps/text-selection-demo.tsx +0 -189
  95. package/apps/textarea.tsx +0 -155
  96. package/apps/theme.tsx +0 -515
  97. package/apps/transform.tsx +0 -229
  98. package/apps/virtual-10k.tsx +0 -405
  99. package/apps/vterm-demo/index.tsx +0 -216
  100. package/components/counter.tsx +0 -45
  101. package/components/hello.tsx +0 -34
  102. package/components/progress-bar.tsx +0 -48
  103. package/components/select-list.tsx +0 -50
  104. package/components/spinner.tsx +0 -40
  105. package/components/text-input.tsx +0 -57
  106. package/components/virtual-list.tsx +0 -52
  107. package/dist/cli.d.mts +0 -1
  108. package/dist/cli.mjs.map +0 -1
  109. package/layout/dashboard.tsx +0 -953
  110. package/layout/live-resize.tsx +0 -282
  111. package/layout/overflow.tsx +0 -51
  112. package/layout/text-layout.tsx +0 -283
@@ -0,0 +1,303 @@
1
+ import { t as require_jsx_runtime } from "../jsx-runtime-dMs_8fNu.mjs";
2
+ import { t as ExampleBanner } from "../_banner-DLPxCqVy.mjs";
3
+ import { useCallback } from "react";
4
+ import { Box, H1, H3, Kbd, Muted, Small, Text, useBoxRect } from "silvery";
5
+ import { run, useInput as useInput$1 } from "silvery/runtime";
6
+ //#region layout/live-resize.tsx
7
+ var import_jsx_runtime = require_jsx_runtime();
8
+ const meta = {
9
+ name: "Live Resize",
10
+ description: "Responsive multi-column grid that reflows based on terminal width",
11
+ features: [
12
+ "useBoxRect()",
13
+ "responsive breakpoints",
14
+ "Box flexDirection"
15
+ ]
16
+ };
17
+ const CARDS = [
18
+ {
19
+ title: "CPU Usage",
20
+ icon: "💻",
21
+ value: "42%",
22
+ detail: "4 cores, 2.4 GHz base",
23
+ color: "green",
24
+ sparkline: "▂▃▅▇▆▄▃▅▇█▆▄▃▂▃▅"
25
+ },
26
+ {
27
+ title: "Memory",
28
+ icon: "🧠",
29
+ value: "8.2 GB",
30
+ detail: "of 16 GB (51% used)",
31
+ color: "cyan",
32
+ sparkline: "▄▄▅▅▅▆▆▆▅▅▆▆▇▇▆▆"
33
+ },
34
+ {
35
+ title: "Disk I/O",
36
+ icon: "💾",
37
+ value: "234 MB/s",
38
+ detail: "Read: 180 MB/s Write: 54 MB/s",
39
+ color: "yellow",
40
+ sparkline: "▁▂▃▇█▇▄▂▁▂▅▇▆▃▂▁"
41
+ },
42
+ {
43
+ title: "Network",
44
+ icon: "🌐",
45
+ value: "1.2 Gb/s",
46
+ detail: "In: 800 Mb/s Out: 400 Mb/s",
47
+ color: "magenta",
48
+ sparkline: "▃▄▅▆▇▆▅▄▅▆▇█▇▆▅▄"
49
+ },
50
+ {
51
+ title: "Processes",
52
+ icon: "⚙️",
53
+ value: "247",
54
+ detail: "12 running, 235 sleeping",
55
+ color: "blue",
56
+ sparkline: "▅▅▅▆▅▅▅▅▆▅▅▅▆▅▅▅"
57
+ },
58
+ {
59
+ title: "Temperature",
60
+ icon: "🌡️",
61
+ value: "62 C",
62
+ detail: "Max: 85 C (safe range)",
63
+ color: "red",
64
+ sparkline: "▃▃▄▄▅▅▆▆▅▅▄▄▃▄▅▅"
65
+ }
66
+ ];
67
+ function MetricCard({ card, compact }) {
68
+ if (compact) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
69
+ borderStyle: "round",
70
+ borderColor: card.color,
71
+ paddingX: 1,
72
+ flexDirection: "row",
73
+ justifyContent: "space-between",
74
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H1, {
75
+ color: card.color,
76
+ children: card.title
77
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(H3, { children: card.value })]
78
+ });
79
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
80
+ borderStyle: "round",
81
+ borderColor: card.color,
82
+ paddingX: 1,
83
+ flexDirection: "column",
84
+ flexGrow: 1,
85
+ children: [
86
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
87
+ justifyContent: "space-between",
88
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H1, {
89
+ color: card.color,
90
+ children: card.title
91
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(H1, {
92
+ color: card.color,
93
+ children: card.value
94
+ })]
95
+ }),
96
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
97
+ color: card.color,
98
+ children: card.sparkline
99
+ }),
100
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Small, { children: card.detail })
101
+ ]
102
+ });
103
+ }
104
+ function BreakpointIndicator({ width, columns }) {
105
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
106
+ gap: 2,
107
+ paddingX: 1,
108
+ children: [
109
+ {
110
+ threshold: 0,
111
+ cols: 1,
112
+ label: "< 60"
113
+ },
114
+ {
115
+ threshold: 60,
116
+ cols: 2,
117
+ label: "60-99"
118
+ },
119
+ {
120
+ threshold: 100,
121
+ cols: 3,
122
+ label: "100+"
123
+ }
124
+ ].map((bp) => {
125
+ const isActive = bp.cols === columns;
126
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
127
+ gap: 1,
128
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
129
+ color: isActive ? "green" : "gray",
130
+ bold: isActive,
131
+ children: isActive ? "●" : "○"
132
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
133
+ color: isActive ? "white" : "gray",
134
+ bold: isActive,
135
+ children: [
136
+ bp.cols,
137
+ " col",
138
+ bp.cols > 1 ? "s" : " ",
139
+ " (",
140
+ bp.label,
141
+ ")"
142
+ ]
143
+ })]
144
+ }, bp.cols);
145
+ })
146
+ });
147
+ }
148
+ function GridLayout({ cards, columns, compact }) {
149
+ if (columns === 1) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
150
+ flexDirection: "column",
151
+ gap: compact ? 0 : 1,
152
+ flexGrow: 1,
153
+ children: cards.map((card) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricCard, {
154
+ card,
155
+ compact
156
+ }, card.title))
157
+ });
158
+ const rows = [];
159
+ for (let i = 0; i < cards.length; i += columns) rows.push(cards.slice(i, i + columns));
160
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
161
+ flexDirection: "column",
162
+ gap: 1,
163
+ flexGrow: 1,
164
+ children: rows.map((row, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
165
+ flexDirection: "row",
166
+ gap: 1,
167
+ children: [row.map((card) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
168
+ flexGrow: 1,
169
+ flexBasis: 0,
170
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MetricCard, {
171
+ card,
172
+ compact: false
173
+ })
174
+ }, card.title)), row.length < columns && Array.from({ length: columns - row.length }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
175
+ flexGrow: 1,
176
+ flexBasis: 0
177
+ }, `spacer-${i}`))]
178
+ }, rowIndex))
179
+ });
180
+ }
181
+ function CodeSnippet({ width }) {
182
+ if (!(width >= 60)) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
183
+ paddingX: 1,
184
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
185
+ dim: true,
186
+ italic: true,
187
+ children: "(Widen terminal to see the code that powers this)"
188
+ })
189
+ });
190
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
191
+ flexDirection: "column",
192
+ borderStyle: "single",
193
+ borderColor: "$border",
194
+ paddingX: 1,
195
+ children: [
196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(H1, {
197
+ color: "yellow",
198
+ children: "How it works:"
199
+ }),
200
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
201
+ color: "gray",
202
+ children: [
203
+ " ",
204
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
205
+ color: "magenta",
206
+ children: "const"
207
+ }),
208
+ " ",
209
+ "{",
210
+ " width ",
211
+ "}",
212
+ " = ",
213
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
214
+ color: "cyan",
215
+ children: "useBoxRect"
216
+ }),
217
+ "()"
218
+ ]
219
+ }),
220
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
221
+ color: "gray",
222
+ children: [
223
+ " ",
224
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
225
+ color: "magenta",
226
+ children: "const"
227
+ }),
228
+ " columns = width ",
229
+ ">",
230
+ " 100 ? ",
231
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
232
+ color: "green",
233
+ children: "3"
234
+ }),
235
+ " : width ",
236
+ ">",
237
+ " 60 ?",
238
+ " ",
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
240
+ color: "green",
241
+ children: "2"
242
+ }),
243
+ " : ",
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
245
+ color: "green",
246
+ children: "1"
247
+ })
248
+ ]
249
+ }),
250
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
251
+ dim: true,
252
+ italic: true,
253
+ children: [" ", "// No useEffect, no layout thrashing. Synchronous."]
254
+ })
255
+ ]
256
+ });
257
+ }
258
+ function LiveResize() {
259
+ const { width, height } = useBoxRect();
260
+ const columns = width >= 100 ? 3 : width >= 60 ? 2 : 1;
261
+ const compact = height < 20 || width < 40;
262
+ useInput$1(useCallback((input, key) => {
263
+ if (input === "q" || key.escape || key.ctrl && input === "c") return "exit";
264
+ }, []));
265
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
266
+ flexDirection: "column",
267
+ width: "100%",
268
+ height: "100%",
269
+ padding: 1,
270
+ children: [
271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BreakpointIndicator, {
272
+ width,
273
+ columns
274
+ }),
275
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
276
+ flexGrow: 1,
277
+ flexDirection: "column",
278
+ marginTop: 1,
279
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GridLayout, {
280
+ cards: CARDS,
281
+ columns,
282
+ compact
283
+ })
284
+ }),
285
+ !compact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CodeSnippet, { width }),
286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
287
+ justifyContent: "space-between",
288
+ paddingX: 1,
289
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: "Resize your terminal to see the layout reflow" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "Esc/q" }), " quit"] })]
290
+ })
291
+ ]
292
+ });
293
+ }
294
+ async function main() {
295
+ await (await run(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExampleBanner, {
296
+ meta,
297
+ controls: "Resize terminal to see reflow Esc/q quit",
298
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LiveResize, {})
299
+ }))).waitUntilExit();
300
+ }
301
+ if (import.meta.main) await main();
302
+ //#endregion
303
+ export { main, meta };
@@ -0,0 +1,70 @@
1
+ import { t as _usingCtx } from "../usingCtx-CsEf0xO3.mjs";
2
+ import { t as require_jsx_runtime } from "../jsx-runtime-dMs_8fNu.mjs";
3
+ import { t as ExampleBanner } from "../_banner-DLPxCqVy.mjs";
4
+ import "react";
5
+ import { Box, Text, createTerm, render, useApp, useInput } from "silvery";
6
+ //#region layout/overflow.tsx
7
+ var import_jsx_runtime = require_jsx_runtime();
8
+ const meta = {
9
+ name: "Overflow",
10
+ description: "overflow=\"hidden\" content clipping demonstration",
11
+ features: ["overflow=\"hidden\"", "Box height"]
12
+ };
13
+ function OverflowApp() {
14
+ const { exit } = useApp();
15
+ useInput((input, key) => {
16
+ if (input === "q" || key.escape) exit();
17
+ });
18
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
19
+ flexDirection: "column",
20
+ padding: 1,
21
+ children: [
22
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
23
+ color: "yellow",
24
+ children: "Title"
25
+ }),
26
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
27
+ borderStyle: "single",
28
+ borderColor: "$primary",
29
+ height: 5,
30
+ overflow: "hidden",
31
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
32
+ flexDirection: "column",
33
+ flexGrow: 1,
34
+ children: [
35
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Line 1" }),
36
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Line 2" }),
37
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Line 3" }),
38
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Line 4" }),
39
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Line 5" }),
40
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Line 6 - should NOT appear" }),
41
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Line 7 - should NOT appear" })
42
+ ]
43
+ })
44
+ }),
45
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
46
+ color: "$success",
47
+ children: "This should NOT be corrupted"
48
+ })
49
+ ]
50
+ });
51
+ }
52
+ async function main() {
53
+ try {
54
+ var _usingCtx$1 = _usingCtx();
55
+ const term = _usingCtx$1.u(createTerm());
56
+ const { waitUntilExit } = await render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExampleBanner, {
57
+ meta,
58
+ controls: "Esc/q quit",
59
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OverflowApp, {})
60
+ }), term);
61
+ await waitUntilExit();
62
+ } catch (_) {
63
+ _usingCtx$1.e = _;
64
+ } finally {
65
+ _usingCtx$1.d();
66
+ }
67
+ }
68
+ if (import.meta.main) await main();
69
+ //#endregion
70
+ export { OverflowApp, main, meta };
@@ -0,0 +1,335 @@
1
+ import { t as require_jsx_runtime } from "../jsx-runtime-dMs_8fNu.mjs";
2
+ import { t as ExampleBanner } from "../_banner-DLPxCqVy.mjs";
3
+ import { useCallback, useState } from "react";
4
+ import { Box, Divider, H2, Kbd, Muted, Small, Text } from "silvery";
5
+ import { run, useInput as useInput$1 } from "silvery/runtime";
6
+ //#region layout/text-layout.tsx
7
+ /**
8
+ * Pretext Demo — snug-content bubbles + even wrapping comparison
9
+ *
10
+ * Interactive demo showcasing Silvery's Pretext-inspired text layout:
11
+ * - width="snug-content" — tightest box width for same line count (shrinkwrap)
12
+ * - wrap="even" — minimum-raggedness line breaking (Knuth-Plass)
13
+ *
14
+ * Inspired by https://chenglou.me/pretext/
15
+ *
16
+ * Usage: bun examples/pretext-demo.tsx
17
+ *
18
+ * Controls:
19
+ * j/k - Cycle demo sections
20
+ * Esc/q - Quit
21
+ */
22
+ var import_jsx_runtime = require_jsx_runtime();
23
+ const meta = {
24
+ name: "text layout",
25
+ description: "Snug-content bubbles + even wrapping — inspired by chenglou/pretext",
26
+ demo: true,
27
+ features: [
28
+ "width=\"snug-content\"",
29
+ "wrap=\"even\"",
30
+ "chat bubbles",
31
+ "paragraph layout"
32
+ ]
33
+ };
34
+ const CHAT_MESSAGES = [
35
+ {
36
+ sender: "Alice",
37
+ text: "Hey!"
38
+ },
39
+ {
40
+ sender: "Bob",
41
+ text: "What are you working on?"
42
+ },
43
+ {
44
+ sender: "Alice",
45
+ text: "Building a terminal UI framework with beautiful text layout."
46
+ },
47
+ {
48
+ sender: "Bob",
49
+ text: "That sounds interesting. Does it handle word wrapping well? Most terminal apps have really ugly ragged text."
50
+ },
51
+ {
52
+ sender: "Alice",
53
+ text: "Yes! It uses Pretext-inspired algorithms for snug bubbles and even line breaking."
54
+ }
55
+ ];
56
+ const PARAGRAPH = "The quick brown fox jumps over the lazy dog. Typography in terminal applications has always been limited by the character grid, but modern algorithms can distribute text across lines for minimum raggedness, producing results that rival print-quality typesetting. Silvery brings these techniques to the terminal.";
57
+ /** A single chat bubble with configurable width and wrap mode. */
58
+ function Bubble({ sender, text, width, wrap, align }) {
59
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
60
+ flexDirection: "column",
61
+ alignItems: align ?? "flex-start",
62
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Small, { children: [" ", sender] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
63
+ width,
64
+ borderStyle: "round",
65
+ borderColor: "$border",
66
+ paddingX: 1,
67
+ maxWidth: 48,
68
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
69
+ wrap: wrap ?? "wrap",
70
+ children: text
71
+ })
72
+ })]
73
+ });
74
+ }
75
+ /** Column of chat bubbles with a label. */
76
+ function BubbleColumn({ label, sublabel, width, wrap }) {
77
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
78
+ flexDirection: "column",
79
+ flexGrow: 1,
80
+ flexBasis: 0,
81
+ children: [
82
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
83
+ bold: true,
84
+ color: "$accent",
85
+ children: label
86
+ }),
87
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: sublabel }),
88
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
89
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
90
+ flexDirection: "column",
91
+ gap: 1,
92
+ children: CHAT_MESSAGES.map((msg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Bubble, {
93
+ sender: msg.sender,
94
+ text: msg.text,
95
+ width,
96
+ wrap,
97
+ align: msg.sender === "Bob" ? "flex-end" : "flex-start"
98
+ }, i))
99
+ })
100
+ ]
101
+ });
102
+ }
103
+ /** Side-by-side paragraph comparison. */
104
+ function ParagraphComparison({ label, sublabel, wrap }) {
105
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
106
+ flexDirection: "column",
107
+ flexGrow: 1,
108
+ flexBasis: 0,
109
+ children: [
110
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
111
+ bold: true,
112
+ color: "$accent",
113
+ children: label
114
+ }),
115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: sublabel }),
116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
118
+ width: 52,
119
+ borderStyle: "single",
120
+ borderColor: "$border",
121
+ paddingX: 1,
122
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123
+ wrap,
124
+ children: PARAGRAPH
125
+ })
126
+ })
127
+ ]
128
+ });
129
+ }
130
+ function Demo1Bubbles() {
131
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
132
+ flexDirection: "column",
133
+ children: [
134
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(H2, { children: "Chat Bubbles: fit-content vs snug-content" }),
135
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [" ", "fit-content sizes to the widest wrapped line (dead space on short lines)."] }),
136
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [" ", "snug-content binary-searches for the tightest width with the same line count."] }),
137
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
139
+ flexDirection: "row",
140
+ gap: 3,
141
+ paddingX: 1,
142
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(BubbleColumn, {
143
+ label: "width=\"fit-content\"",
144
+ sublabel: "CSS default — dead space",
145
+ width: "fit-content"
146
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BubbleColumn, {
147
+ label: "width=\"snug-content\"",
148
+ sublabel: "Pretext shrinkwrap — tight",
149
+ width: "snug-content"
150
+ })]
151
+ })
152
+ ]
153
+ });
154
+ }
155
+ function Demo2EvenWrap() {
156
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
157
+ flexDirection: "column",
158
+ children: [
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(H2, { children: "Paragraph Layout: greedy vs even wrapping" }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [" ", "Greedy fills each line left-to-right, leaving a ragged right edge."] }),
161
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [" ", "Even uses minimum-raggedness DP to distribute words across all lines."] }),
162
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
164
+ flexDirection: "row",
165
+ gap: 3,
166
+ paddingX: 1,
167
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ParagraphComparison, {
168
+ label: "wrap=\"wrap\"",
169
+ sublabel: "Greedy — ragged right edge",
170
+ wrap: "wrap"
171
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ParagraphComparison, {
172
+ label: "wrap=\"even\"",
173
+ sublabel: "Min-raggedness — balanced lines",
174
+ wrap: "even"
175
+ })]
176
+ })
177
+ ]
178
+ });
179
+ }
180
+ function Demo3Combined() {
181
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
182
+ flexDirection: "column",
183
+ children: [
184
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(H2, { children: "Combined: snug-content + even wrapping" }),
185
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [" ", "The tightest, most beautiful text layout — both features together."] }),
186
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
187
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
188
+ flexDirection: "row",
189
+ gap: 3,
190
+ paddingX: 1,
191
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
192
+ flexDirection: "column",
193
+ flexGrow: 1,
194
+ flexBasis: 0,
195
+ children: [
196
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
197
+ bold: true,
198
+ color: "$accent",
199
+ children: "Default (fit-content + greedy)"
200
+ }),
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: "Widest line sets width, lines fill greedily" }),
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
204
+ flexDirection: "column",
205
+ gap: 1,
206
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
207
+ width: "fit-content",
208
+ borderStyle: "round",
209
+ borderColor: "$border",
210
+ paddingX: 1,
211
+ maxWidth: 48,
212
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
213
+ wrap: "wrap",
214
+ children: "Typography in terminal applications has always been limited by the character grid, but modern algorithms change that."
215
+ })
216
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
217
+ width: "fit-content",
218
+ borderStyle: "round",
219
+ borderColor: "$border",
220
+ paddingX: 1,
221
+ maxWidth: 48,
222
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
223
+ wrap: "wrap",
224
+ children: "Silvery brings Pretext-inspired layout to the terminal with two simple props."
225
+ })
226
+ })]
227
+ })
228
+ ]
229
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
230
+ flexDirection: "column",
231
+ flexGrow: 1,
232
+ flexBasis: 0,
233
+ children: [
234
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
235
+ bold: true,
236
+ color: "$accent",
237
+ children: "Pretext (snug-content + even)"
238
+ }),
239
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: "Tightest width, balanced line lengths" }),
240
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
241
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
242
+ flexDirection: "column",
243
+ gap: 1,
244
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
245
+ width: "snug-content",
246
+ borderStyle: "round",
247
+ borderColor: "$primary",
248
+ paddingX: 1,
249
+ maxWidth: 48,
250
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
251
+ wrap: "even",
252
+ children: "Typography in terminal applications has always been limited by the character grid, but modern algorithms change that."
253
+ })
254
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
255
+ width: "snug-content",
256
+ borderStyle: "round",
257
+ borderColor: "$primary",
258
+ paddingX: 1,
259
+ maxWidth: 48,
260
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
261
+ wrap: "even",
262
+ children: "Silvery brings Pretext-inspired layout to the terminal with two simple props."
263
+ })
264
+ })]
265
+ })
266
+ ]
267
+ })]
268
+ })
269
+ ]
270
+ });
271
+ }
272
+ const DEMOS = [
273
+ Demo1Bubbles,
274
+ Demo2EvenWrap,
275
+ Demo3Combined
276
+ ];
277
+ const DEMO_LABELS = [
278
+ "Chat Bubbles",
279
+ "Even Wrapping",
280
+ "Combined"
281
+ ];
282
+ function PretextDemo() {
283
+ const [demoIndex, setDemoIndex] = useState(0);
284
+ useInput$1(useCallback((input, key) => {
285
+ if (input === "q" || key.escape) return "exit";
286
+ if (input === "j" || key.downArrow || key.rightArrow) setDemoIndex((i) => Math.min(i + 1, DEMOS.length - 1));
287
+ if (input === "k" || key.upArrow || key.leftArrow) setDemoIndex((i) => Math.max(i - 1, 0));
288
+ if (input === "1") setDemoIndex(0);
289
+ if (input === "2") setDemoIndex(1);
290
+ if (input === "3") setDemoIndex(2);
291
+ }, []));
292
+ const Demo = DEMOS[demoIndex];
293
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
294
+ flexDirection: "column",
295
+ padding: 1,
296
+ gap: 1,
297
+ children: [
298
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
299
+ gap: 2,
300
+ children: DEMO_LABELS.map((label, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
301
+ bold: i === demoIndex,
302
+ color: i === demoIndex ? "$primary" : "$muted",
303
+ children: [
304
+ i === demoIndex ? "▸ " : " ",
305
+ i + 1,
306
+ ". ",
307
+ label
308
+ ]
309
+ }, i))
310
+ }),
311
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Divider, {}),
312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Demo, {}),
313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
314
+ " ",
315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "j/k" }),
316
+ " or ",
317
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "1-3" }),
318
+ " switch demo",
319
+ " ",
320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "Esc/q" }),
321
+ " quit"
322
+ ] }) })
323
+ ]
324
+ });
325
+ }
326
+ async function main() {
327
+ await (await run(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExampleBanner, {
328
+ meta,
329
+ controls: "j/k switch demo 1-3 jump Esc/q quit",
330
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PretextDemo, {})
331
+ }))).waitUntilExit();
332
+ }
333
+ if (import.meta.main) await main();
334
+ //#endregion
335
+ export { main, meta };