@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.
- package/dist/UPNG-Cy7ViL8f.mjs +5074 -0
- package/dist/__vite-browser-external-2447137e-BML7CYau.mjs +4 -0
- package/dist/_banner-DLPxCqVy.mjs +44 -0
- package/dist/ansi-CCE2pVS0.mjs +16397 -0
- package/dist/apng-HhhBjRGt.mjs +68 -0
- package/dist/apng-mwUQbTTF.mjs +3 -0
- package/dist/apps/aichat/index.mjs +1299 -0
- package/dist/apps/app-todo.mjs +139 -0
- package/dist/apps/async-data.mjs +204 -0
- package/dist/apps/cli-wizard.mjs +339 -0
- package/dist/apps/clipboard.mjs +198 -0
- package/dist/apps/components.mjs +864 -0
- package/dist/apps/data-explorer.mjs +483 -0
- package/dist/apps/dev-tools.mjs +397 -0
- package/dist/apps/explorer.mjs +698 -0
- package/dist/apps/gallery.mjs +766 -0
- package/dist/apps/inline-bench.mjs +115 -0
- package/dist/apps/kanban.mjs +280 -0
- package/dist/apps/layout-ref.mjs +187 -0
- package/dist/apps/outline.mjs +203 -0
- package/dist/apps/paste-demo.mjs +189 -0
- package/dist/apps/scroll.mjs +86 -0
- package/dist/apps/search-filter.mjs +287 -0
- package/dist/apps/selection.mjs +355 -0
- package/dist/apps/spatial-focus-demo.mjs +388 -0
- package/dist/apps/task-list.mjs +258 -0
- package/dist/apps/terminal-caps-demo.mjs +315 -0
- package/dist/apps/terminal.mjs +872 -0
- package/dist/apps/text-selection-demo.mjs +254 -0
- package/dist/apps/textarea.mjs +178 -0
- package/dist/apps/theme.mjs +661 -0
- package/dist/apps/transform.mjs +215 -0
- package/dist/apps/virtual-10k.mjs +422 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/backends-Bahh9mKN.mjs +1179 -0
- package/dist/backends-CCtCDQ94.mjs +3 -0
- package/dist/{cli.mjs → bin/cli.mjs} +21 -25
- package/dist/chunk-BSw8zbkd.mjs +37 -0
- package/dist/components/counter.mjs +48 -0
- package/dist/components/hello.mjs +31 -0
- package/dist/components/progress-bar.mjs +59 -0
- package/dist/components/select-list.mjs +85 -0
- package/dist/components/spinner.mjs +57 -0
- package/dist/components/text-input.mjs +62 -0
- package/dist/components/virtual-list.mjs +51 -0
- package/dist/flexily-zero-adapter-UB-ra8fR.mjs +3374 -0
- package/dist/gif-BZaqPPVX.mjs +3 -0
- package/dist/gif-BtnXuxLF.mjs +71 -0
- package/dist/gifenc-CLRW41dk.mjs +728 -0
- package/dist/jsx-runtime-dMs_8fNu.mjs +241 -0
- package/dist/key-mapping-5oYQdAQE.mjs +3 -0
- package/dist/key-mapping-D4LR1go6.mjs +130 -0
- package/dist/layout/dashboard.mjs +1204 -0
- package/dist/layout/live-resize.mjs +303 -0
- package/dist/layout/overflow.mjs +70 -0
- package/dist/layout/text-layout.mjs +335 -0
- package/dist/node-NuJ94BWl.mjs +1083 -0
- package/dist/plugins-D1KtkT4a.mjs +3057 -0
- package/dist/resvg-js-C_8Wps1F.mjs +201 -0
- package/dist/src-BTEVGpd9.mjs +23538 -0
- package/dist/src-CUUOuRH6.mjs +5322 -0
- package/dist/src-CzfRafCQ.mjs +814 -0
- package/dist/usingCtx-CsEf0xO3.mjs +57 -0
- package/dist/yoga-adapter-BVtQ5OJR.mjs +237 -0
- package/package.json +19 -14
- package/_banner.tsx +0 -60
- package/apps/aichat/components.tsx +0 -469
- package/apps/aichat/index.tsx +0 -220
- package/apps/aichat/script.ts +0 -460
- package/apps/aichat/state.ts +0 -325
- package/apps/aichat/types.ts +0 -19
- package/apps/app-todo.tsx +0 -201
- package/apps/async-data.tsx +0 -196
- package/apps/cli-wizard.tsx +0 -332
- package/apps/clipboard.tsx +0 -183
- package/apps/components.tsx +0 -658
- package/apps/data-explorer.tsx +0 -490
- package/apps/dev-tools.tsx +0 -395
- package/apps/explorer.tsx +0 -731
- package/apps/gallery.tsx +0 -653
- package/apps/inline-bench.tsx +0 -138
- package/apps/kanban.tsx +0 -265
- package/apps/layout-ref.tsx +0 -173
- package/apps/outline.tsx +0 -160
- package/apps/panes/index.tsx +0 -203
- package/apps/paste-demo.tsx +0 -185
- package/apps/scroll.tsx +0 -77
- package/apps/search-filter.tsx +0 -240
- package/apps/selection.tsx +0 -342
- package/apps/spatial-focus-demo.tsx +0 -368
- package/apps/task-list.tsx +0 -271
- package/apps/terminal-caps-demo.tsx +0 -334
- package/apps/terminal.tsx +0 -800
- package/apps/text-selection-demo.tsx +0 -189
- package/apps/textarea.tsx +0 -155
- package/apps/theme.tsx +0 -515
- package/apps/transform.tsx +0 -229
- package/apps/virtual-10k.tsx +0 -405
- package/apps/vterm-demo/index.tsx +0 -216
- package/components/counter.tsx +0 -45
- package/components/hello.tsx +0 -34
- package/components/progress-bar.tsx +0 -48
- package/components/select-list.tsx +0 -50
- package/components/spinner.tsx +0 -40
- package/components/text-input.tsx +0 -57
- package/components/virtual-list.tsx +0 -52
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs.map +0 -1
- package/layout/dashboard.tsx +0 -953
- package/layout/live-resize.tsx +0 -282
- package/layout/overflow.tsx +0 -51
- package/layout/text-layout.tsx +0 -283
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { W as CapabilityRegistryContext } from "../src-BTEVGpd9.mjs";
|
|
2
|
+
import { t as _usingCtx } from "../usingCtx-CsEf0xO3.mjs";
|
|
3
|
+
import { a as createApp, i as pipe, n as withTerminal, r as withReact, t as withDomEvents } from "../plugins-D1KtkT4a.mjs";
|
|
4
|
+
import { t as require_jsx_runtime } from "../jsx-runtime-dMs_8fNu.mjs";
|
|
5
|
+
import { t as ExampleBanner } from "../_banner-DLPxCqVy.mjs";
|
|
6
|
+
import { useContext, useSyncExternalStore } from "react";
|
|
7
|
+
import { Box, H1, H2, HR, Kbd, Muted, Small, Strong, Text } from "silvery";
|
|
8
|
+
//#region ../packages/ag-react/src/hooks/useSelection.ts
|
|
9
|
+
/**
|
|
10
|
+
* useSelection — React hook for accessing the selection feature from the capability registry.
|
|
11
|
+
*
|
|
12
|
+
* Reads SELECTION_CAPABILITY from the app's CapabilityRegistry via context.
|
|
13
|
+
* Returns `undefined` when the selection feature is not installed (e.g., simple
|
|
14
|
+
* run() apps without pipe() composition, or when withDomEvents is not used).
|
|
15
|
+
*
|
|
16
|
+
* When installed, returns the current TerminalSelectionState and re-renders
|
|
17
|
+
* reactively on selection changes via useSyncExternalStore.
|
|
18
|
+
*
|
|
19
|
+
* This is the recommended hook for reading selection state. It replaces the
|
|
20
|
+
* older useTerminalSelection/TerminalSelectionProvider pattern with a simpler
|
|
21
|
+
* capability-based lookup.
|
|
22
|
+
*/
|
|
23
|
+
const SELECTION_CAPABILITY = Symbol.for("silvery.selection");
|
|
24
|
+
const noopSubscribe = (_listener) => () => {};
|
|
25
|
+
const getUndefined = () => void 0;
|
|
26
|
+
/**
|
|
27
|
+
* Access the current selection state from the capability registry.
|
|
28
|
+
*
|
|
29
|
+
* Returns `undefined` when:
|
|
30
|
+
* - No CapabilityRegistryContext is provided (simple run() apps)
|
|
31
|
+
* - SELECTION_CAPABILITY is not registered (withDomEvents not used or selection disabled)
|
|
32
|
+
*
|
|
33
|
+
* Returns `TerminalSelectionState` when selection is installed:
|
|
34
|
+
* - `state.range` — current SelectionRange or null (idle)
|
|
35
|
+
* - `state.selecting` — true while mouse button is held
|
|
36
|
+
* - `state.source` — "mouse" | "keyboard" | null
|
|
37
|
+
*/
|
|
38
|
+
function useSelection() {
|
|
39
|
+
const feature = useContext(CapabilityRegistryContext)?.get(SELECTION_CAPABILITY);
|
|
40
|
+
return useSyncExternalStore(feature ? (listener) => feature.subscribe(listener) : noopSubscribe, feature ? () => feature.state : getUndefined);
|
|
41
|
+
}
|
|
42
|
+
//#endregion
|
|
43
|
+
//#region apps/text-selection-demo.tsx
|
|
44
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
45
|
+
const meta = {
|
|
46
|
+
name: "Text Selection",
|
|
47
|
+
description: "Real selection via useSelection hook, userSelect modes, live state readout",
|
|
48
|
+
demo: true,
|
|
49
|
+
features: [
|
|
50
|
+
"useSelection()",
|
|
51
|
+
"userSelect prop",
|
|
52
|
+
"CapabilityRegistry",
|
|
53
|
+
"mouse drag selection"
|
|
54
|
+
]
|
|
55
|
+
};
|
|
56
|
+
function SelectableTextPanel() {
|
|
57
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
58
|
+
flexDirection: "column",
|
|
59
|
+
borderStyle: "round",
|
|
60
|
+
borderColor: "$border",
|
|
61
|
+
paddingX: 1,
|
|
62
|
+
flexGrow: 1,
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H2, { children: "Selectable Text" }),
|
|
65
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Small, { children: "userSelect=\"text\" (default)" }),
|
|
66
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { height: 1 }),
|
|
67
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Drag your mouse over this text to select it." }),
|
|
68
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Multi-line selections work across paragraphs." }),
|
|
69
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { height: 1 }),
|
|
70
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
71
|
+
color: "$muted",
|
|
72
|
+
children: "The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs."
|
|
73
|
+
})
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function NonSelectablePanel() {
|
|
78
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
79
|
+
flexDirection: "column",
|
|
80
|
+
borderStyle: "round",
|
|
81
|
+
borderColor: "$border",
|
|
82
|
+
paddingX: 1,
|
|
83
|
+
flexGrow: 1,
|
|
84
|
+
userSelect: "none",
|
|
85
|
+
children: [
|
|
86
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H2, { children: "Non-Selectable" }),
|
|
87
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Small, { children: "userSelect=\"none\"" }),
|
|
88
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { height: 1 }),
|
|
89
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "This area cannot be selected by mouse drag." }),
|
|
90
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Click events still work normally here." }),
|
|
91
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { height: 1 }),
|
|
92
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: "Hold Alt and drag to override and select anyway." })
|
|
93
|
+
]
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function ContainedPanel() {
|
|
97
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
98
|
+
flexDirection: "column",
|
|
99
|
+
borderStyle: "round",
|
|
100
|
+
borderColor: "$warning",
|
|
101
|
+
paddingX: 1,
|
|
102
|
+
flexGrow: 1,
|
|
103
|
+
children: [
|
|
104
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H2, {
|
|
105
|
+
color: "$warning",
|
|
106
|
+
children: "Contained Selection"
|
|
107
|
+
}),
|
|
108
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Small, { children: "userSelect=\"contain\"" }),
|
|
109
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { height: 1 }),
|
|
110
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
111
|
+
flexDirection: "column",
|
|
112
|
+
borderStyle: "round",
|
|
113
|
+
borderColor: "$primary",
|
|
114
|
+
paddingX: 1,
|
|
115
|
+
userSelect: "contain",
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
118
|
+
bold: true,
|
|
119
|
+
color: "$primary",
|
|
120
|
+
children: "Selection Boundary"
|
|
121
|
+
}),
|
|
122
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { height: 1 }),
|
|
123
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Selection cannot escape this container." }),
|
|
124
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Try dragging past the border — it clips." }),
|
|
125
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { height: 1 }),
|
|
126
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
127
|
+
color: "$success",
|
|
128
|
+
children: "Useful for modals, side panes, overlays."
|
|
129
|
+
})
|
|
130
|
+
]
|
|
131
|
+
})
|
|
132
|
+
]
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function SelectionStatePanel() {
|
|
136
|
+
const selection = useSelection();
|
|
137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
138
|
+
flexDirection: "column",
|
|
139
|
+
borderStyle: "round",
|
|
140
|
+
borderColor: "$info",
|
|
141
|
+
paddingX: 1,
|
|
142
|
+
flexGrow: 1,
|
|
143
|
+
children: [
|
|
144
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H2, {
|
|
145
|
+
color: "$info",
|
|
146
|
+
children: "Selection State"
|
|
147
|
+
}),
|
|
148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Small, { children: "Live readout from useSelection()" }),
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, { height: 1 }),
|
|
150
|
+
!selection ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
151
|
+
color: "$muted",
|
|
152
|
+
children: "useSelection() returned undefined — feature not installed"
|
|
153
|
+
}) : selection.range ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
154
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
155
|
+
gap: 1,
|
|
156
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Strong, { children: "Status:" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
157
|
+
color: "$success",
|
|
158
|
+
children: selection.selecting ? "Selecting..." : "Selected"
|
|
159
|
+
})]
|
|
160
|
+
}),
|
|
161
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
162
|
+
gap: 1,
|
|
163
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Strong, { children: "Source:" }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: selection.source ?? "unknown" })]
|
|
164
|
+
}),
|
|
165
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
166
|
+
gap: 1,
|
|
167
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Strong, { children: "Anchor:" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
168
|
+
"(",
|
|
169
|
+
selection.range.anchor.col,
|
|
170
|
+
", ",
|
|
171
|
+
selection.range.anchor.row,
|
|
172
|
+
")"
|
|
173
|
+
] })]
|
|
174
|
+
}),
|
|
175
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
176
|
+
gap: 1,
|
|
177
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Strong, { children: "Head:" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
178
|
+
"(",
|
|
179
|
+
selection.range.head.col,
|
|
180
|
+
", ",
|
|
181
|
+
selection.range.head.row,
|
|
182
|
+
")"
|
|
183
|
+
] })]
|
|
184
|
+
})
|
|
185
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
186
|
+
color: "$muted",
|
|
187
|
+
children: "No active selection — drag to select text"
|
|
188
|
+
})
|
|
189
|
+
]
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
function StatusBar() {
|
|
193
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
194
|
+
flexDirection: "row",
|
|
195
|
+
gap: 2,
|
|
196
|
+
paddingX: 1,
|
|
197
|
+
flexShrink: 0,
|
|
198
|
+
userSelect: "none",
|
|
199
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
|
|
200
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "Drag" }),
|
|
201
|
+
" select ",
|
|
202
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "Alt+Drag" }),
|
|
203
|
+
" force select ",
|
|
204
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, { children: "Ctrl+C" }),
|
|
205
|
+
" quit"
|
|
206
|
+
] })
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
function TextSelectionDemo() {
|
|
210
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
211
|
+
flexDirection: "column",
|
|
212
|
+
padding: 1,
|
|
213
|
+
gap: 1,
|
|
214
|
+
height: "100%",
|
|
215
|
+
children: [
|
|
216
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H1, {
|
|
217
|
+
color: "$primary",
|
|
218
|
+
children: "Text Selection Demo"
|
|
219
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: " — real selection via useSelection()" })] }),
|
|
220
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
221
|
+
flexDirection: "row",
|
|
222
|
+
gap: 1,
|
|
223
|
+
flexGrow: 1,
|
|
224
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectableTextPanel, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NonSelectablePanel, {})]
|
|
225
|
+
}),
|
|
226
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
227
|
+
flexDirection: "row",
|
|
228
|
+
gap: 1,
|
|
229
|
+
flexGrow: 1,
|
|
230
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContainedPanel, {}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SelectionStatePanel, {})]
|
|
231
|
+
}),
|
|
232
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HR, {}),
|
|
233
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(StatusBar, {})
|
|
234
|
+
]
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
async function main() {
|
|
238
|
+
try {
|
|
239
|
+
var _usingCtx$1 = _usingCtx();
|
|
240
|
+
const app = pipe(createApp(() => () => ({})), withReact(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExampleBanner, {
|
|
241
|
+
meta,
|
|
242
|
+
controls: "Drag select Alt+Drag force select Ctrl+C quit",
|
|
243
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextSelectionDemo, {})
|
|
244
|
+
})), withTerminal(process, { mouse: true }), withDomEvents());
|
|
245
|
+
await _usingCtx$1.u(await app.run()).waitUntilExit();
|
|
246
|
+
} catch (_) {
|
|
247
|
+
_usingCtx$1.e = _;
|
|
248
|
+
} finally {
|
|
249
|
+
_usingCtx$1.d();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (import.meta.main) await main();
|
|
253
|
+
//#endregion
|
|
254
|
+
export { main, meta };
|
|
@@ -0,0 +1,178 @@
|
|
|
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 { useState } from "react";
|
|
5
|
+
import { Box, H1, Muted, Strong, Text, TextArea, createTerm, render, useApp, useInput } from "silvery";
|
|
6
|
+
//#region apps/textarea.tsx
|
|
7
|
+
/**
|
|
8
|
+
* TextArea Example — Split-Pane Note Editor
|
|
9
|
+
*
|
|
10
|
+
* A note-taking app demonstrating:
|
|
11
|
+
* - Multi-line text input with word wrapping and pre-filled content
|
|
12
|
+
* - Split-pane layout: editor (2/3) + saved notes sidebar (1/3)
|
|
13
|
+
* - Tab focus cycling between panes
|
|
14
|
+
* - Cursor movement (arrow keys, Home/End, Ctrl+A/E)
|
|
15
|
+
* - Kill operations (Ctrl+K, Ctrl+U)
|
|
16
|
+
* - Word/character stats in the header
|
|
17
|
+
* - Submit with Ctrl+Enter to collect notes
|
|
18
|
+
*/
|
|
19
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
20
|
+
const meta = {
|
|
21
|
+
name: "TextArea",
|
|
22
|
+
description: "Split-pane note editor with word wrap, kill ring, and note collection",
|
|
23
|
+
features: [
|
|
24
|
+
"TextArea",
|
|
25
|
+
"Split pane layout",
|
|
26
|
+
"Ctrl+Enter submit",
|
|
27
|
+
"Tab focus cycling"
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
const INITIAL_CONTENT = `# Release Notes — Silvery v0.1
|
|
31
|
+
|
|
32
|
+
## New Features
|
|
33
|
+
|
|
34
|
+
- **Flexbox layout engine** — CSS-compatible sizing,
|
|
35
|
+
wrapping, and gap support via Flexily
|
|
36
|
+
- **38 built-in color palettes** — from Dracula
|
|
37
|
+
to Solarized, Nord to Catppuccin
|
|
38
|
+
- **Incremental rendering** — only changed cells
|
|
39
|
+
are repainted, no full-screen flicker
|
|
40
|
+
|
|
41
|
+
## Breaking Changes
|
|
42
|
+
|
|
43
|
+
- Dropped Node.js 18 support (now requires >=20)
|
|
44
|
+
- Renamed \`useTerminal()\` to \`useTerm()\`
|
|
45
|
+
|
|
46
|
+
## Performance
|
|
47
|
+
|
|
48
|
+
Benchmark results on an M4 MacBook Pro:
|
|
49
|
+
Initial render: 2.1ms (80x24)
|
|
50
|
+
Incremental: 0.3ms (typical diff)
|
|
51
|
+
Layout: 0.8ms (1000 nodes)
|
|
52
|
+
|
|
53
|
+
Thanks to all contributors!`;
|
|
54
|
+
function NoteEditor() {
|
|
55
|
+
const { exit } = useApp();
|
|
56
|
+
const [notes, setNotes] = useState([]);
|
|
57
|
+
const [value, setValue] = useState(INITIAL_CONTENT);
|
|
58
|
+
const [focusIndex, setFocusIndex] = useState(0);
|
|
59
|
+
useInput((input, key) => {
|
|
60
|
+
if (key.escape) exit();
|
|
61
|
+
if (key.tab && !key.shift) setFocusIndex((prev) => (prev + 1) % 2);
|
|
62
|
+
if (key.tab && key.shift) setFocusIndex((prev) => (prev - 1 + 2) % 2);
|
|
63
|
+
});
|
|
64
|
+
function handleSubmit(text) {
|
|
65
|
+
if (text.trim()) {
|
|
66
|
+
setNotes((prev) => [...prev, text.trim()]);
|
|
67
|
+
setValue("");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const lines = value.split("\n").length;
|
|
71
|
+
const chars = value.length;
|
|
72
|
+
const words = value.split(/\s+/).filter(Boolean).length;
|
|
73
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
74
|
+
flexDirection: "column",
|
|
75
|
+
flexGrow: 1,
|
|
76
|
+
padding: 1,
|
|
77
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
78
|
+
flexDirection: "row",
|
|
79
|
+
gap: 1,
|
|
80
|
+
flexGrow: 1,
|
|
81
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
82
|
+
borderStyle: "round",
|
|
83
|
+
borderColor: focusIndex === 0 ? "$primary" : "$border",
|
|
84
|
+
flexDirection: "column",
|
|
85
|
+
flexGrow: 3,
|
|
86
|
+
flexBasis: 0,
|
|
87
|
+
children: [
|
|
88
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
89
|
+
paddingX: 1,
|
|
90
|
+
justifyContent: "space-between",
|
|
91
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H1, { children: "Editor" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
|
|
92
|
+
lines,
|
|
93
|
+
" lines, ",
|
|
94
|
+
words,
|
|
95
|
+
" words, ",
|
|
96
|
+
chars,
|
|
97
|
+
" chars"
|
|
98
|
+
] })]
|
|
99
|
+
}),
|
|
100
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
|
|
101
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
102
|
+
paddingX: 1,
|
|
103
|
+
flexGrow: 1,
|
|
104
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextArea, {
|
|
105
|
+
value,
|
|
106
|
+
onChange: setValue,
|
|
107
|
+
onSubmit: handleSubmit,
|
|
108
|
+
height: 16,
|
|
109
|
+
isActive: focusIndex === 0
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
]
|
|
113
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
114
|
+
borderStyle: "round",
|
|
115
|
+
borderColor: focusIndex === 1 ? "$primary" : "$border",
|
|
116
|
+
flexDirection: "column",
|
|
117
|
+
flexGrow: 2,
|
|
118
|
+
flexBasis: 0,
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
121
|
+
paddingX: 1,
|
|
122
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(H1, { children: "Notes" }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
|
|
123
|
+
" (",
|
|
124
|
+
notes.length,
|
|
125
|
+
")"
|
|
126
|
+
] })]
|
|
127
|
+
}),
|
|
128
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }),
|
|
129
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
130
|
+
flexDirection: "column",
|
|
131
|
+
paddingX: 1,
|
|
132
|
+
overflow: "scroll",
|
|
133
|
+
flexGrow: 1,
|
|
134
|
+
children: notes.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Muted, { children: "No notes yet." }) : notes.map((note, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
135
|
+
flexDirection: "column",
|
|
136
|
+
marginBottom: 1,
|
|
137
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
138
|
+
wrap: "truncate",
|
|
139
|
+
children: [
|
|
140
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Strong, {
|
|
141
|
+
color: "$success",
|
|
142
|
+
children: ["#", i + 1]
|
|
143
|
+
}),
|
|
144
|
+
" ",
|
|
145
|
+
note.split("\n")[0]
|
|
146
|
+
]
|
|
147
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Muted, { children: [
|
|
148
|
+
note.split("\n").length,
|
|
149
|
+
" lines, ",
|
|
150
|
+
note.length,
|
|
151
|
+
" chars"
|
|
152
|
+
] })]
|
|
153
|
+
}, i))
|
|
154
|
+
})
|
|
155
|
+
]
|
|
156
|
+
})]
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
async function main() {
|
|
161
|
+
try {
|
|
162
|
+
var _usingCtx$1 = _usingCtx();
|
|
163
|
+
const term = _usingCtx$1.u(createTerm());
|
|
164
|
+
const { waitUntilExit } = await render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExampleBanner, {
|
|
165
|
+
meta,
|
|
166
|
+
controls: "Tab switch pane Ctrl+Enter submit Esc quit",
|
|
167
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(NoteEditor, {})
|
|
168
|
+
}), term);
|
|
169
|
+
await waitUntilExit();
|
|
170
|
+
} catch (_) {
|
|
171
|
+
_usingCtx$1.e = _;
|
|
172
|
+
} finally {
|
|
173
|
+
_usingCtx$1.d();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (import.meta.main) await main();
|
|
177
|
+
//#endregion
|
|
178
|
+
export { NoteEditor, main, meta };
|