@silvery/examples 0.17.3 → 0.17.5

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 (111) hide show
  1. package/dist/UPNG-ShUlaTDh.mjs +5074 -0
  2. package/dist/__vite-browser-external-2447137e-Bopa5BFR.mjs +4 -0
  3. package/dist/_banner-A70_y2Vi.mjs +43 -0
  4. package/dist/ansi-0VXlUmNn.mjs +16397 -0
  5. package/dist/apng-B0gRaDVT.mjs +3 -0
  6. package/dist/apng-BTRDTfDW.mjs +68 -0
  7. package/dist/apps/aichat/index.mjs +1298 -0
  8. package/dist/apps/app-todo.mjs +138 -0
  9. package/dist/apps/async-data.mjs +203 -0
  10. package/dist/apps/cli-wizard.mjs +338 -0
  11. package/dist/apps/clipboard.mjs +197 -0
  12. package/dist/apps/components.mjs +863 -0
  13. package/dist/apps/data-explorer.mjs +482 -0
  14. package/dist/apps/dev-tools.mjs +396 -0
  15. package/dist/apps/explorer.mjs +697 -0
  16. package/dist/apps/gallery.mjs +765 -0
  17. package/dist/apps/inline-bench.mjs +115 -0
  18. package/dist/apps/kanban.mjs +279 -0
  19. package/dist/apps/layout-ref.mjs +186 -0
  20. package/dist/apps/outline.mjs +202 -0
  21. package/dist/apps/paste-demo.mjs +188 -0
  22. package/dist/apps/scroll.mjs +85 -0
  23. package/dist/apps/search-filter.mjs +286 -0
  24. package/dist/apps/selection.mjs +354 -0
  25. package/dist/apps/spatial-focus-demo.mjs +387 -0
  26. package/dist/apps/task-list.mjs +257 -0
  27. package/dist/apps/terminal-caps-demo.mjs +314 -0
  28. package/dist/apps/terminal.mjs +871 -0
  29. package/dist/apps/text-selection-demo.mjs +253 -0
  30. package/dist/apps/textarea.mjs +177 -0
  31. package/dist/apps/theme.mjs +660 -0
  32. package/dist/apps/transform.mjs +214 -0
  33. package/dist/apps/virtual-10k.mjs +421 -0
  34. package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
  35. package/dist/backends-Dj-11kZF.mjs +1179 -0
  36. package/dist/backends-U3QwStfO.mjs +3 -0
  37. package/dist/{cli.mjs → bin/cli.mjs} +15 -19
  38. package/dist/chunk-BSw8zbkd.mjs +37 -0
  39. package/dist/components/counter.mjs +47 -0
  40. package/dist/components/hello.mjs +30 -0
  41. package/dist/components/progress-bar.mjs +58 -0
  42. package/dist/components/select-list.mjs +84 -0
  43. package/dist/components/spinner.mjs +56 -0
  44. package/dist/components/text-input.mjs +61 -0
  45. package/dist/components/virtual-list.mjs +50 -0
  46. package/dist/flexily-zero-adapter-ByVzLTFP.mjs +3374 -0
  47. package/dist/gif-B6NGH5gs.mjs +3 -0
  48. package/dist/gif-CfkOF-iG.mjs +71 -0
  49. package/dist/gifenc-BI4ihP_T.mjs +728 -0
  50. package/dist/key-mapping-5oYQdAQE.mjs +3 -0
  51. package/dist/key-mapping-D4LR1go6.mjs +130 -0
  52. package/dist/layout/dashboard.mjs +1203 -0
  53. package/dist/layout/live-resize.mjs +302 -0
  54. package/dist/layout/overflow.mjs +69 -0
  55. package/dist/layout/text-layout.mjs +334 -0
  56. package/dist/node-nsrAOjH4.mjs +1083 -0
  57. package/dist/plugins-CT0DdV_E.mjs +3056 -0
  58. package/dist/resvg-js-Cnk2o49d.mjs +201 -0
  59. package/dist/src-9ZhfQyzD.mjs +814 -0
  60. package/dist/src-CUUOuRH6.mjs +5322 -0
  61. package/dist/src-jO3Zuzjj.mjs +23538 -0
  62. package/dist/usingCtx-CsEf0xO3.mjs +57 -0
  63. package/dist/yoga-adapter-BSQHuMV9.mjs +237 -0
  64. package/package.json +21 -14
  65. package/_banner.tsx +0 -60
  66. package/apps/aichat/components.tsx +0 -469
  67. package/apps/aichat/index.tsx +0 -220
  68. package/apps/aichat/script.ts +0 -460
  69. package/apps/aichat/state.ts +0 -325
  70. package/apps/aichat/types.ts +0 -19
  71. package/apps/app-todo.tsx +0 -201
  72. package/apps/async-data.tsx +0 -196
  73. package/apps/cli-wizard.tsx +0 -332
  74. package/apps/clipboard.tsx +0 -183
  75. package/apps/components.tsx +0 -658
  76. package/apps/data-explorer.tsx +0 -490
  77. package/apps/dev-tools.tsx +0 -395
  78. package/apps/explorer.tsx +0 -731
  79. package/apps/gallery.tsx +0 -653
  80. package/apps/inline-bench.tsx +0 -138
  81. package/apps/kanban.tsx +0 -265
  82. package/apps/layout-ref.tsx +0 -173
  83. package/apps/outline.tsx +0 -160
  84. package/apps/panes/index.tsx +0 -203
  85. package/apps/paste-demo.tsx +0 -185
  86. package/apps/scroll.tsx +0 -80
  87. package/apps/search-filter.tsx +0 -240
  88. package/apps/selection.tsx +0 -346
  89. package/apps/spatial-focus-demo.tsx +0 -372
  90. package/apps/task-list.tsx +0 -271
  91. package/apps/terminal-caps-demo.tsx +0 -317
  92. package/apps/terminal.tsx +0 -784
  93. package/apps/text-selection-demo.tsx +0 -193
  94. package/apps/textarea.tsx +0 -155
  95. package/apps/theme.tsx +0 -515
  96. package/apps/transform.tsx +0 -229
  97. package/apps/virtual-10k.tsx +0 -405
  98. package/apps/vterm-demo/index.tsx +0 -216
  99. package/components/counter.tsx +0 -49
  100. package/components/hello.tsx +0 -38
  101. package/components/progress-bar.tsx +0 -52
  102. package/components/select-list.tsx +0 -54
  103. package/components/spinner.tsx +0 -44
  104. package/components/text-input.tsx +0 -61
  105. package/components/virtual-list.tsx +0 -56
  106. package/dist/cli.d.mts +0 -1
  107. package/dist/cli.mjs.map +0 -1
  108. package/layout/dashboard.tsx +0 -953
  109. package/layout/live-resize.tsx +0 -282
  110. package/layout/overflow.tsx +0 -51
  111. package/layout/text-layout.tsx +0 -283
@@ -0,0 +1,286 @@
1
+ import { t as _usingCtx } from "../usingCtx-CsEf0xO3.mjs";
2
+ import { t as ExampleBanner } from "../_banner-A70_y2Vi.mjs";
3
+ import { useDeferredValue, useState, useTransition } from "react";
4
+ import { Box, Kbd, Lead, Muted, Strong, Text, createTerm, render, useApp, useInput } from "silvery";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region apps/search-filter.tsx
7
+ /**
8
+ * Search Filter Example
9
+ *
10
+ * Demonstrates React concurrent features for responsive typing:
11
+ * - useTransition for low-priority state updates
12
+ * - useDeferredValue for deferred filtering
13
+ * - Typing remains responsive even with heavy filtering
14
+ */
15
+ const meta = {
16
+ name: "Search Filter",
17
+ description: "useTransition + useDeferredValue for responsive concurrent search",
18
+ features: [
19
+ "useDeferredValue",
20
+ "useTransition",
21
+ "useInput"
22
+ ]
23
+ };
24
+ const items = [
25
+ {
26
+ id: 1,
27
+ name: "React Hooks Guide",
28
+ category: "docs",
29
+ tags: [
30
+ "react",
31
+ "hooks",
32
+ "tutorial"
33
+ ]
34
+ },
35
+ {
36
+ id: 2,
37
+ name: "TypeScript Patterns",
38
+ category: "docs",
39
+ tags: ["typescript", "patterns"]
40
+ },
41
+ {
42
+ id: 3,
43
+ name: "Build Configuration",
44
+ category: "config",
45
+ tags: [
46
+ "webpack",
47
+ "vite",
48
+ "build"
49
+ ]
50
+ },
51
+ {
52
+ id: 4,
53
+ name: "Testing Best Practices",
54
+ category: "docs",
55
+ tags: [
56
+ "testing",
57
+ "jest",
58
+ "vitest"
59
+ ]
60
+ },
61
+ {
62
+ id: 5,
63
+ name: "API Documentation",
64
+ category: "docs",
65
+ tags: [
66
+ "api",
67
+ "rest",
68
+ "graphql"
69
+ ]
70
+ },
71
+ {
72
+ id: 6,
73
+ name: "Database Schema",
74
+ category: "config",
75
+ tags: [
76
+ "database",
77
+ "sql",
78
+ "migration"
79
+ ]
80
+ },
81
+ {
82
+ id: 7,
83
+ name: "Authentication Flow",
84
+ category: "docs",
85
+ tags: [
86
+ "auth",
87
+ "security",
88
+ "jwt"
89
+ ]
90
+ },
91
+ {
92
+ id: 8,
93
+ name: "CI/CD Pipeline",
94
+ category: "config",
95
+ tags: [
96
+ "ci",
97
+ "deployment",
98
+ "github"
99
+ ]
100
+ },
101
+ {
102
+ id: 9,
103
+ name: "Performance Tuning",
104
+ category: "docs",
105
+ tags: ["performance", "optimization"]
106
+ },
107
+ {
108
+ id: 10,
109
+ name: "Error Handling",
110
+ category: "docs",
111
+ tags: [
112
+ "errors",
113
+ "debugging",
114
+ "logging"
115
+ ]
116
+ },
117
+ {
118
+ id: 11,
119
+ name: "State Management",
120
+ category: "docs",
121
+ tags: [
122
+ "state",
123
+ "redux",
124
+ "zustand"
125
+ ]
126
+ },
127
+ {
128
+ id: 12,
129
+ name: "CSS Architecture",
130
+ category: "docs",
131
+ tags: [
132
+ "css",
133
+ "tailwind",
134
+ "styled"
135
+ ]
136
+ },
137
+ {
138
+ id: 13,
139
+ name: "Security Guidelines",
140
+ category: "docs",
141
+ tags: [
142
+ "security",
143
+ "owasp",
144
+ "audit"
145
+ ]
146
+ },
147
+ {
148
+ id: 14,
149
+ name: "Deployment Scripts",
150
+ category: "config",
151
+ tags: [
152
+ "deploy",
153
+ "docker",
154
+ "k8s"
155
+ ]
156
+ },
157
+ {
158
+ id: 15,
159
+ name: "Monitoring Setup",
160
+ category: "config",
161
+ tags: [
162
+ "monitoring",
163
+ "metrics",
164
+ "logs"
165
+ ]
166
+ }
167
+ ];
168
+ function SearchInput({ value, onChange }) {
169
+ return /* @__PURE__ */ jsxs(Box, { children: [
170
+ /* @__PURE__ */ jsx(Strong, {
171
+ color: "$primary",
172
+ children: "Search: "
173
+ }),
174
+ /* @__PURE__ */ jsx(Text, { children: value }),
175
+ /* @__PURE__ */ jsx(Text, {
176
+ dim: true,
177
+ children: "|"
178
+ })
179
+ ] });
180
+ }
181
+ function FilteredList({ query, isPending }) {
182
+ const filtered = items.filter((item) => {
183
+ const searchLower = query.toLowerCase();
184
+ return item.name.toLowerCase().includes(searchLower) || item.category.toLowerCase().includes(searchLower) || item.tags.some((tag) => tag.toLowerCase().includes(searchLower));
185
+ });
186
+ return /* @__PURE__ */ jsxs(Box, {
187
+ flexDirection: "column",
188
+ marginTop: 1,
189
+ children: [
190
+ /* @__PURE__ */ jsx(Box, {
191
+ marginBottom: 1,
192
+ children: /* @__PURE__ */ jsxs(Muted, { children: [
193
+ filtered.length,
194
+ " results",
195
+ isPending && " (filtering...)"
196
+ ] })
197
+ }),
198
+ filtered.map((item) => /* @__PURE__ */ jsxs(Box, {
199
+ marginBottom: 1,
200
+ children: [
201
+ /* @__PURE__ */ jsx(Text, {
202
+ bold: true,
203
+ children: item.name
204
+ }),
205
+ /* @__PURE__ */ jsxs(Text, {
206
+ dim: true,
207
+ children: [
208
+ " [",
209
+ item.category,
210
+ "]"
211
+ ]
212
+ }),
213
+ /* @__PURE__ */ jsxs(Text, {
214
+ color: "$muted",
215
+ children: [" ", item.tags.join(", ")]
216
+ })
217
+ ]
218
+ }, item.id)),
219
+ filtered.length === 0 && /* @__PURE__ */ jsx(Lead, { children: "No matches found" })
220
+ ]
221
+ });
222
+ }
223
+ function SearchApp() {
224
+ const { exit } = useApp();
225
+ const [query, setQuery] = useState("");
226
+ const deferredQuery = useDeferredValue(query);
227
+ const [isPending, startTransition] = useTransition();
228
+ useInput((input, key) => {
229
+ if (key.escape) {
230
+ exit();
231
+ return;
232
+ }
233
+ if (key.backspace || key.delete) {
234
+ startTransition(() => {
235
+ setQuery((prev) => prev.slice(0, -1));
236
+ });
237
+ return;
238
+ }
239
+ if (input && !key.ctrl && !key.meta) startTransition(() => {
240
+ setQuery((prev) => prev + input);
241
+ });
242
+ });
243
+ return /* @__PURE__ */ jsxs(Box, {
244
+ flexDirection: "column",
245
+ padding: 1,
246
+ children: [
247
+ /* @__PURE__ */ jsx(SearchInput, {
248
+ value: query,
249
+ onChange: setQuery
250
+ }),
251
+ /* @__PURE__ */ jsx(Box, {
252
+ flexGrow: 1,
253
+ children: /* @__PURE__ */ jsx(FilteredList, {
254
+ query: deferredQuery,
255
+ isPending
256
+ })
257
+ }),
258
+ /* @__PURE__ */ jsxs(Muted, { children: [
259
+ " ",
260
+ /* @__PURE__ */ jsx(Kbd, { children: "type" }),
261
+ " to search ",
262
+ /* @__PURE__ */ jsx(Kbd, { children: "Esc/q" }),
263
+ " quit"
264
+ ] })
265
+ ]
266
+ });
267
+ }
268
+ async function main() {
269
+ try {
270
+ var _usingCtx$1 = _usingCtx();
271
+ const term = _usingCtx$1.u(createTerm());
272
+ const { waitUntilExit } = await render(/* @__PURE__ */ jsx(ExampleBanner, {
273
+ meta,
274
+ controls: "type to search Esc quit",
275
+ children: /* @__PURE__ */ jsx(SearchApp, {})
276
+ }), term);
277
+ await waitUntilExit();
278
+ } catch (_) {
279
+ _usingCtx$1.e = _;
280
+ } finally {
281
+ _usingCtx$1.d();
282
+ }
283
+ }
284
+ if (import.meta.main) await main();
285
+ //#endregion
286
+ export { SearchApp, main, meta };
@@ -0,0 +1,354 @@
1
+ import { t as _usingCtx } from "../usingCtx-CsEf0xO3.mjs";
2
+ import { useCallback, useState } from "react";
3
+ import { Box, Text } from "silvery";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { run, useInput as useInput$1 } from "silvery/runtime";
6
+ //#region apps/selection.tsx
7
+ /**
8
+ * Selection Model Demo
9
+ *
10
+ * Demonstrates the silvery selection model from docs/design/selection-model.md:
11
+ * - Node selection (click, j/k)
12
+ * - Multi-select (Cmd+click toggle, Shift+j/k extend)
13
+ * - Text editing (Enter → edit, Escape → node mode)
14
+ * - Mode ladder: text ──Esc──► node ──Esc──► board
15
+ * - Live status bar showing Selection state
16
+ *
17
+ * Run: bun examples/apps/selection.tsx
18
+ */
19
+ const S = {
20
+ cursor: (sel) => sel.nodes[0],
21
+ anchor: (sel) => sel.nodes.at(-1),
22
+ ids: (sel) => new Set(sel.nodes),
23
+ includes: (sel, id) => sel.nodes.includes(id),
24
+ isEditing: (sel) => !!sel.text,
25
+ inputMode: (sel) => !sel ? "board" : sel.text ? "text" : "node",
26
+ select: (id) => ({ nodes: [id] }),
27
+ toggle(sel, id) {
28
+ if (sel.nodes.includes(id)) {
29
+ const rest = sel.nodes.filter((n) => n !== id);
30
+ return rest.length > 0 ? { nodes: rest } : void 0;
31
+ }
32
+ return { nodes: [id, ...sel.nodes] };
33
+ },
34
+ extend(sel, id, allIds) {
35
+ const anchorIdx = allIds.indexOf(S.anchor(sel));
36
+ const targetIdx = allIds.indexOf(id);
37
+ if (anchorIdx < 0 || targetIdx < 0) return sel;
38
+ const lo = Math.min(anchorIdx, targetIdx);
39
+ const hi = Math.max(anchorIdx, targetIdx);
40
+ const range = allIds.slice(lo, hi + 1);
41
+ return { nodes: targetIdx <= anchorIdx ? range : [...range].reverse() };
42
+ },
43
+ areaSelect(_sel, hitIds, mode) {
44
+ if (mode === "replace") return hitIds.length > 0 ? { nodes: hitIds } : void 0;
45
+ let result = _sel;
46
+ for (const id of hitIds) result = result ? S.toggle(result, id) : S.select(id);
47
+ return result;
48
+ },
49
+ clear: () => void 0,
50
+ collapseToCursor(sel) {
51
+ return { nodes: [sel.nodes[0]] };
52
+ },
53
+ edit(sel, offset) {
54
+ return {
55
+ ...sel,
56
+ text: [{
57
+ nodeId: sel.nodes[0],
58
+ offset
59
+ }]
60
+ };
61
+ },
62
+ stopEditing(sel) {
63
+ const { text: _, ...rest } = sel;
64
+ return rest;
65
+ }
66
+ };
67
+ const ITEMS = [
68
+ {
69
+ id: "inbox",
70
+ label: "Inbox"
71
+ },
72
+ {
73
+ id: "today",
74
+ label: "Today"
75
+ },
76
+ {
77
+ id: "next",
78
+ label: "Next Actions"
79
+ },
80
+ {
81
+ id: "projects",
82
+ label: "Projects"
83
+ },
84
+ {
85
+ id: "waiting",
86
+ label: "Waiting For"
87
+ },
88
+ {
89
+ id: "someday",
90
+ label: "Someday / Maybe"
91
+ },
92
+ {
93
+ id: "reference",
94
+ label: "Reference"
95
+ },
96
+ {
97
+ id: "calendar",
98
+ label: "Calendar"
99
+ },
100
+ {
101
+ id: "review",
102
+ label: "Weekly Review"
103
+ },
104
+ {
105
+ id: "done",
106
+ label: "Done"
107
+ }
108
+ ];
109
+ const ALL_IDS = ITEMS.map((i) => i.id);
110
+ function ItemRow({ item, sel, onSelect }) {
111
+ const isCursor = sel ? S.cursor(sel) === item.id : false;
112
+ const isAnchor = sel ? S.anchor(sel) === item.id : false;
113
+ const isSelected = sel ? S.includes(sel, item.id) : false;
114
+ const isEditing = isCursor && sel ? S.isEditing(sel) : false;
115
+ const marker = isCursor ? "►" : isSelected ? "●" : " ";
116
+ const anchorMark = isAnchor && !isCursor ? " ⚓" : "";
117
+ return /* @__PURE__ */ jsxs(Box, {
118
+ onClick: (e) => onSelect(item.id, e.metaKey, e.shiftKey),
119
+ onDoubleClick: () => onSelect(item.id, false, false),
120
+ children: [
121
+ /* @__PURE__ */ jsxs(Text, {
122
+ color: isSelected ? "$primary" : "$muted",
123
+ children: [marker, " "]
124
+ }),
125
+ isEditing ? /* @__PURE__ */ jsxs(Text, {
126
+ backgroundColor: "$surface",
127
+ color: "$text",
128
+ bold: true,
129
+ children: [
130
+ " ",
131
+ item.label,
132
+ /* @__PURE__ */ jsx(Text, {
133
+ color: "$primary",
134
+ children: "│"
135
+ }),
136
+ " "
137
+ ]
138
+ }) : /* @__PURE__ */ jsx(Text, {
139
+ bold: isCursor,
140
+ color: isCursor ? "$primary" : isSelected ? "$primary" : "$text",
141
+ dim: !isSelected && !isCursor,
142
+ children: item.label
143
+ }),
144
+ /* @__PURE__ */ jsx(Text, {
145
+ color: "$muted",
146
+ dim: true,
147
+ children: anchorMark
148
+ })
149
+ ]
150
+ });
151
+ }
152
+ function StatusBar({ sel }) {
153
+ const mode = S.inputMode(sel);
154
+ return /* @__PURE__ */ jsxs(Box, {
155
+ flexDirection: "column",
156
+ borderStyle: "single",
157
+ borderColor: "$border",
158
+ paddingX: 1,
159
+ children: [/* @__PURE__ */ jsxs(Box, {
160
+ gap: 2,
161
+ children: [/* @__PURE__ */ jsx(Text, {
162
+ color: mode === "text" ? "$success" : mode === "node" ? "$primary" : "$muted",
163
+ bold: true,
164
+ children: mode.toUpperCase()
165
+ }), sel && /* @__PURE__ */ jsxs(Fragment, { children: [
166
+ /* @__PURE__ */ jsxs(Text, {
167
+ color: "$muted",
168
+ children: ["cursor=", /* @__PURE__ */ jsx(Text, {
169
+ color: "$primary",
170
+ children: S.cursor(sel)
171
+ })]
172
+ }),
173
+ /* @__PURE__ */ jsxs(Text, {
174
+ color: "$muted",
175
+ children: ["anchor=", /* @__PURE__ */ jsx(Text, {
176
+ color: "$text",
177
+ children: S.anchor(sel)
178
+ })]
179
+ }),
180
+ /* @__PURE__ */ jsxs(Text, {
181
+ color: "$muted",
182
+ children: ["selected=", /* @__PURE__ */ jsx(Text, {
183
+ color: "$text",
184
+ children: sel.nodes.length
185
+ })]
186
+ }),
187
+ sel.text && /* @__PURE__ */ jsxs(Text, {
188
+ color: "$muted",
189
+ children: ["text=", /* @__PURE__ */ jsxs(Text, {
190
+ color: "$success",
191
+ children: ["offset ", sel.text[0].offset]
192
+ })]
193
+ })
194
+ ] })]
195
+ }), /* @__PURE__ */ jsx(Text, {
196
+ color: "$muted",
197
+ dim: true,
198
+ children: "j/k nav · Enter edit · Esc back · Shift+j/k extend · Cmd+click toggle · q quit"
199
+ })]
200
+ });
201
+ }
202
+ function SelectionDemo() {
203
+ const [sel, setSel] = useState(void 0);
204
+ const [editTexts, setEditTexts] = useState(() => Object.fromEntries(ITEMS.map((i) => [i.id, i.label])));
205
+ const cursorIndex = sel ? ALL_IDS.indexOf(S.cursor(sel)) : -1;
206
+ const handleSelect = useCallback((id, meta, shift) => {
207
+ setSel((prev) => {
208
+ if (meta && prev) return S.toggle(prev, id);
209
+ if (shift && prev) return S.extend(prev, id, ALL_IDS);
210
+ return S.select(id);
211
+ });
212
+ }, []);
213
+ useInput$1((input, key) => {
214
+ if (input === "q" || key.escape && !sel) return "exit";
215
+ const mode = S.inputMode(sel);
216
+ if (mode === "text" && sel) {
217
+ if (key.escape) {
218
+ setSel(S.stopEditing(sel));
219
+ return;
220
+ }
221
+ if (key.backspace && sel.text) {
222
+ const nodeId = S.cursor(sel);
223
+ const offset = sel.text[0].offset;
224
+ if (offset > 0) {
225
+ setEditTexts((prev) => ({
226
+ ...prev,
227
+ [nodeId]: prev[nodeId].slice(0, offset - 1) + prev[nodeId].slice(offset)
228
+ }));
229
+ setSel(S.edit(sel, offset - 1));
230
+ }
231
+ return;
232
+ }
233
+ if (key.leftArrow && sel.text) {
234
+ setSel(S.edit(sel, Math.max(0, sel.text[0].offset - 1)));
235
+ return;
236
+ }
237
+ if (key.rightArrow && sel.text) {
238
+ const maxLen = editTexts[S.cursor(sel)]?.length ?? 0;
239
+ setSel(S.edit(sel, Math.min(maxLen, sel.text[0].offset + 1)));
240
+ return;
241
+ }
242
+ if (input && !key.ctrl && !key.meta && sel.text) {
243
+ const nodeId = S.cursor(sel);
244
+ const offset = sel.text[0].offset;
245
+ setEditTexts((prev) => ({
246
+ ...prev,
247
+ [nodeId]: prev[nodeId].slice(0, offset) + input + prev[nodeId].slice(offset)
248
+ }));
249
+ setSel(S.edit(sel, offset + input.length));
250
+ return;
251
+ }
252
+ return;
253
+ }
254
+ if (mode === "node" && sel) {
255
+ if (key.escape) {
256
+ if (sel.nodes.length > 1) setSel(S.collapseToCursor(sel));
257
+ else setSel(S.clear());
258
+ return;
259
+ }
260
+ if (key.return) {
261
+ const text = editTexts[S.cursor(sel)] ?? "";
262
+ setSel(S.edit(sel, text.length));
263
+ return;
264
+ }
265
+ if (input === "j" || key.downArrow) {
266
+ const next = Math.min(ALL_IDS.length - 1, cursorIndex + 1);
267
+ if (key.shift) setSel(S.extend(sel, ALL_IDS[next], ALL_IDS));
268
+ else setSel(S.select(ALL_IDS[next]));
269
+ return;
270
+ }
271
+ if (input === "k" || key.upArrow) {
272
+ const next = Math.max(0, cursorIndex - 1);
273
+ if (key.shift) setSel(S.extend(sel, ALL_IDS[next], ALL_IDS));
274
+ else setSel(S.select(ALL_IDS[next]));
275
+ return;
276
+ }
277
+ return;
278
+ }
279
+ if (input === "j" || key.downArrow) setSel(S.select(ALL_IDS[0]));
280
+ if (input === "k" || key.upArrow) setSel(S.select(ALL_IDS.at(-1)));
281
+ });
282
+ const displayItems = ITEMS.map((item) => ({
283
+ ...item,
284
+ label: editTexts[item.id] ?? item.label
285
+ }));
286
+ return /* @__PURE__ */ jsxs(Box, {
287
+ flexDirection: "column",
288
+ padding: 1,
289
+ height: "100%",
290
+ children: [
291
+ /* @__PURE__ */ jsxs(Box, {
292
+ marginBottom: 1,
293
+ children: [/* @__PURE__ */ jsx(Text, {
294
+ bold: true,
295
+ color: "$primary",
296
+ children: "Selection Model Demo"
297
+ }), /* @__PURE__ */ jsx(Text, {
298
+ color: "$muted",
299
+ children: " — silvery reactive selection"
300
+ })]
301
+ }),
302
+ /* @__PURE__ */ jsx(Box, {
303
+ flexDirection: "column",
304
+ flexGrow: 1,
305
+ borderStyle: "round",
306
+ borderColor: "$border",
307
+ overflow: "hidden",
308
+ children: displayItems.map((item) => /* @__PURE__ */ jsx(ItemRow, {
309
+ item,
310
+ sel,
311
+ onSelect: handleSelect
312
+ }, item.id))
313
+ }),
314
+ /* @__PURE__ */ jsx(StatusBar, { sel }),
315
+ /* @__PURE__ */ jsx(Box, {
316
+ marginTop: 1,
317
+ flexDirection: "column",
318
+ children: /* @__PURE__ */ jsxs(Text, {
319
+ color: "$muted",
320
+ dim: true,
321
+ children: [
322
+ "nodes=[",
323
+ sel?.nodes.join(", ") ?? "",
324
+ "]"
325
+ ]
326
+ })
327
+ })
328
+ ]
329
+ });
330
+ }
331
+ const meta = {
332
+ name: "Selection",
333
+ description: "Reactive selection model — node/text modes, multi-select, mode ladder",
334
+ demo: true,
335
+ features: [
336
+ "Selection model",
337
+ "mode ladder",
338
+ "multi-select",
339
+ "text editing"
340
+ ]
341
+ };
342
+ async function main() {
343
+ try {
344
+ var _usingCtx$1 = _usingCtx();
345
+ await _usingCtx$1.u(await run(/* @__PURE__ */ jsx(SelectionDemo, {}), { mode: "fullscreen" })).waitUntilExit();
346
+ } catch (_) {
347
+ _usingCtx$1.e = _;
348
+ } finally {
349
+ _usingCtx$1.d();
350
+ }
351
+ }
352
+ if (import.meta.main) await main();
353
+ //#endregion
354
+ export { main, meta };