@textui/core 0.1.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 (323) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +67 -0
  3. package/dist/adapters/index.d.ts +12 -0
  4. package/dist/adapters/index.d.ts.map +1 -0
  5. package/dist/adapters/index.js +11 -0
  6. package/dist/app/app.d.ts +200 -0
  7. package/dist/app/app.d.ts.map +1 -0
  8. package/dist/app/app.js +980 -0
  9. package/dist/core/animation.d.ts +34 -0
  10. package/dist/core/animation.d.ts.map +1 -0
  11. package/dist/core/animation.js +125 -0
  12. package/dist/core/clipboard.d.ts +29 -0
  13. package/dist/core/clipboard.d.ts.map +1 -0
  14. package/dist/core/clipboard.js +30 -0
  15. package/dist/core/commands.d.ts +42 -0
  16. package/dist/core/commands.d.ts.map +1 -0
  17. package/dist/core/commands.js +146 -0
  18. package/dist/core/components.d.ts +27 -0
  19. package/dist/core/components.d.ts.map +1 -0
  20. package/dist/core/components.js +78 -0
  21. package/dist/core/events.d.ts +23 -0
  22. package/dist/core/events.d.ts.map +1 -0
  23. package/dist/core/events.js +77 -0
  24. package/dist/core/focus.d.ts +69 -0
  25. package/dist/core/focus.d.ts.map +1 -0
  26. package/dist/core/focus.js +336 -0
  27. package/dist/core/i18n.d.ts +27 -0
  28. package/dist/core/i18n.d.ts.map +1 -0
  29. package/dist/core/i18n.js +88 -0
  30. package/dist/core/keybindings.d.ts +53 -0
  31. package/dist/core/keybindings.d.ts.map +1 -0
  32. package/dist/core/keybindings.js +163 -0
  33. package/dist/core/layers.d.ts +28 -0
  34. package/dist/core/layers.d.ts.map +1 -0
  35. package/dist/core/layers.js +84 -0
  36. package/dist/core/manifest.d.ts +22 -0
  37. package/dist/core/manifest.d.ts.map +1 -0
  38. package/dist/core/manifest.js +85 -0
  39. package/dist/core/navigation.d.ts +47 -0
  40. package/dist/core/navigation.d.ts.map +1 -0
  41. package/dist/core/navigation.js +110 -0
  42. package/dist/core/resources.d.ts +115 -0
  43. package/dist/core/resources.d.ts.map +1 -0
  44. package/dist/core/resources.js +321 -0
  45. package/dist/core/services.d.ts +21 -0
  46. package/dist/core/services.d.ts.map +1 -0
  47. package/dist/core/services.js +63 -0
  48. package/dist/core/store.d.ts +60 -0
  49. package/dist/core/store.d.ts.map +1 -0
  50. package/dist/core/store.js +593 -0
  51. package/dist/core/surfaces.d.ts +81 -0
  52. package/dist/core/surfaces.d.ts.map +1 -0
  53. package/dist/core/surfaces.js +237 -0
  54. package/dist/core/syntax.d.ts +49 -0
  55. package/dist/core/syntax.d.ts.map +1 -0
  56. package/dist/core/syntax.js +172 -0
  57. package/dist/core/when.d.ts +15 -0
  58. package/dist/core/when.d.ts.map +1 -0
  59. package/dist/core/when.js +224 -0
  60. package/dist/index.d.ts +49 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +54 -0
  63. package/dist/jsx/factory.d.ts +27 -0
  64. package/dist/jsx/factory.d.ts.map +1 -0
  65. package/dist/jsx/factory.js +102 -0
  66. package/dist/jsx/intrinsics.d.ts +96 -0
  67. package/dist/jsx/intrinsics.d.ts.map +1 -0
  68. package/dist/jsx/intrinsics.js +1 -0
  69. package/dist/jsx/jsx-dev-runtime.d.ts +13 -0
  70. package/dist/jsx/jsx-dev-runtime.d.ts.map +1 -0
  71. package/dist/jsx/jsx-dev-runtime.js +12 -0
  72. package/dist/jsx/jsx-runtime.d.ts +42 -0
  73. package/dist/jsx/jsx-runtime.d.ts.map +1 -0
  74. package/dist/jsx/jsx-runtime.js +16 -0
  75. package/dist/render/buffer.d.ts +77 -0
  76. package/dist/render/buffer.d.ts.map +1 -0
  77. package/dist/render/buffer.js +275 -0
  78. package/dist/render/color.d.ts +35 -0
  79. package/dist/render/color.d.ts.map +1 -0
  80. package/dist/render/color.js +160 -0
  81. package/dist/render/diff.d.ts +37 -0
  82. package/dist/render/diff.d.ts.map +1 -0
  83. package/dist/render/diff.js +66 -0
  84. package/dist/render/layout.d.ts +72 -0
  85. package/dist/render/layout.d.ts.map +1 -0
  86. package/dist/render/layout.js +633 -0
  87. package/dist/render/static.d.ts +63 -0
  88. package/dist/render/static.d.ts.map +1 -0
  89. package/dist/render/static.js +209 -0
  90. package/dist/runtime/bindings.d.ts +46 -0
  91. package/dist/runtime/bindings.d.ts.map +1 -0
  92. package/dist/runtime/bindings.js +116 -0
  93. package/dist/runtime/hooks.d.ts +284 -0
  94. package/dist/runtime/hooks.d.ts.map +1 -0
  95. package/dist/runtime/hooks.js +846 -0
  96. package/dist/runtime/instance.d.ts +106 -0
  97. package/dist/runtime/instance.d.ts.map +1 -0
  98. package/dist/runtime/instance.js +181 -0
  99. package/dist/runtime/paint.d.ts +27 -0
  100. package/dist/runtime/paint.d.ts.map +1 -0
  101. package/dist/runtime/paint.js +567 -0
  102. package/dist/runtime/reconcile.d.ts +23 -0
  103. package/dist/runtime/reconcile.d.ts.map +1 -0
  104. package/dist/runtime/reconcile.js +260 -0
  105. package/dist/runtime/runtime.d.ts +42 -0
  106. package/dist/runtime/runtime.d.ts.map +1 -0
  107. package/dist/runtime/runtime.js +1 -0
  108. package/dist/runtime/style.d.ts +55 -0
  109. package/dist/runtime/style.d.ts.map +1 -0
  110. package/dist/runtime/style.js +143 -0
  111. package/dist/themes/borders.d.ts +10 -0
  112. package/dist/themes/borders.d.ts.map +1 -0
  113. package/dist/themes/borders.js +88 -0
  114. package/dist/themes/builtin.d.ts +31 -0
  115. package/dist/themes/builtin.d.ts.map +1 -0
  116. package/dist/themes/builtin.js +291 -0
  117. package/dist/themes/glyphs.d.ts +15 -0
  118. package/dist/themes/glyphs.d.ts.map +1 -0
  119. package/dist/themes/glyphs.js +97 -0
  120. package/dist/themes/index.d.ts +5 -0
  121. package/dist/themes/index.d.ts.map +1 -0
  122. package/dist/themes/index.js +4 -0
  123. package/dist/themes/registry.d.ts +20 -0
  124. package/dist/themes/registry.d.ts.map +1 -0
  125. package/dist/themes/registry.js +204 -0
  126. package/dist/types/adapter.d.ts +38 -0
  127. package/dist/types/adapter.d.ts.map +1 -0
  128. package/dist/types/adapter.js +1 -0
  129. package/dist/types/animation.d.ts +31 -0
  130. package/dist/types/animation.d.ts.map +1 -0
  131. package/dist/types/animation.js +1 -0
  132. package/dist/types/app.d.ts +148 -0
  133. package/dist/types/app.d.ts.map +1 -0
  134. package/dist/types/app.js +1 -0
  135. package/dist/types/async.d.ts +30 -0
  136. package/dist/types/async.d.ts.map +1 -0
  137. package/dist/types/async.js +1 -0
  138. package/dist/types/capabilities.d.ts +44 -0
  139. package/dist/types/capabilities.d.ts.map +1 -0
  140. package/dist/types/capabilities.js +33 -0
  141. package/dist/types/cells.d.ts +72 -0
  142. package/dist/types/cells.d.ts.map +1 -0
  143. package/dist/types/cells.js +10 -0
  144. package/dist/types/command.d.ts +127 -0
  145. package/dist/types/command.d.ts.map +1 -0
  146. package/dist/types/command.js +1 -0
  147. package/dist/types/component-registry.d.ts +77 -0
  148. package/dist/types/component-registry.d.ts.map +1 -0
  149. package/dist/types/component-registry.js +1 -0
  150. package/dist/types/disposable.d.ts +8 -0
  151. package/dist/types/disposable.d.ts.map +1 -0
  152. package/dist/types/disposable.js +1 -0
  153. package/dist/types/focus.d.ts +72 -0
  154. package/dist/types/focus.d.ts.map +1 -0
  155. package/dist/types/focus.js +1 -0
  156. package/dist/types/geometry.d.ts +28 -0
  157. package/dist/types/geometry.d.ts.map +1 -0
  158. package/dist/types/geometry.js +14 -0
  159. package/dist/types/graph.d.ts +129 -0
  160. package/dist/types/graph.d.ts.map +1 -0
  161. package/dist/types/graph.js +31 -0
  162. package/dist/types/i18n.d.ts +23 -0
  163. package/dist/types/i18n.d.ts.map +1 -0
  164. package/dist/types/i18n.js +1 -0
  165. package/dist/types/index.d.ts +32 -0
  166. package/dist/types/index.d.ts.map +1 -0
  167. package/dist/types/index.js +31 -0
  168. package/dist/types/input.d.ts +65 -0
  169. package/dist/types/input.d.ts.map +1 -0
  170. package/dist/types/input.js +1 -0
  171. package/dist/types/keybinding.d.ts +49 -0
  172. package/dist/types/keybinding.d.ts.map +1 -0
  173. package/dist/types/keybinding.js +1 -0
  174. package/dist/types/layer.d.ts +54 -0
  175. package/dist/types/layer.d.ts.map +1 -0
  176. package/dist/types/layer.js +1 -0
  177. package/dist/types/manifest.d.ts +114 -0
  178. package/dist/types/manifest.d.ts.map +1 -0
  179. package/dist/types/manifest.js +1 -0
  180. package/dist/types/markdown.d.ts +58 -0
  181. package/dist/types/markdown.d.ts.map +1 -0
  182. package/dist/types/markdown.js +1 -0
  183. package/dist/types/navigation.d.ts +40 -0
  184. package/dist/types/navigation.d.ts.map +1 -0
  185. package/dist/types/navigation.js +1 -0
  186. package/dist/types/render.d.ts +72 -0
  187. package/dist/types/render.d.ts.map +1 -0
  188. package/dist/types/render.js +1 -0
  189. package/dist/types/resource.d.ts +170 -0
  190. package/dist/types/resource.d.ts.map +1 -0
  191. package/dist/types/resource.js +1 -0
  192. package/dist/types/services.d.ts +24 -0
  193. package/dist/types/services.d.ts.map +1 -0
  194. package/dist/types/services.js +3 -0
  195. package/dist/types/shell.d.ts +32 -0
  196. package/dist/types/shell.d.ts.map +1 -0
  197. package/dist/types/shell.js +1 -0
  198. package/dist/types/store.d.ts +124 -0
  199. package/dist/types/store.d.ts.map +1 -0
  200. package/dist/types/store.js +1 -0
  201. package/dist/types/stream.d.ts +18 -0
  202. package/dist/types/stream.d.ts.map +1 -0
  203. package/dist/types/stream.js +1 -0
  204. package/dist/types/style.d.ts +166 -0
  205. package/dist/types/style.d.ts.map +1 -0
  206. package/dist/types/style.js +1 -0
  207. package/dist/types/surface.d.ts +97 -0
  208. package/dist/types/surface.d.ts.map +1 -0
  209. package/dist/types/surface.js +1 -0
  210. package/dist/types/syntax.d.ts +68 -0
  211. package/dist/types/syntax.d.ts.map +1 -0
  212. package/dist/types/syntax.js +5 -0
  213. package/dist/types/terminal.d.ts +52 -0
  214. package/dist/types/terminal.d.ts.map +1 -0
  215. package/dist/types/terminal.js +1 -0
  216. package/dist/types/theme.d.ts +117 -0
  217. package/dist/types/theme.d.ts.map +1 -0
  218. package/dist/types/theme.js +1 -0
  219. package/dist/types/when.d.ts +19 -0
  220. package/dist/types/when.d.ts.map +1 -0
  221. package/dist/types/when.js +1 -0
  222. package/dist/ui/primitives.d.ts +56 -0
  223. package/dist/ui/primitives.d.ts.map +1 -0
  224. package/dist/ui/primitives.js +108 -0
  225. package/dist/ui/screen.d.ts +23 -0
  226. package/dist/ui/screen.d.ts.map +1 -0
  227. package/dist/ui/screen.js +18 -0
  228. package/dist/util/disposable.d.ts +6 -0
  229. package/dist/util/disposable.d.ts.map +1 -0
  230. package/dist/util/disposable.js +49 -0
  231. package/dist/util/markdown.d.ts +24 -0
  232. package/dist/util/markdown.d.ts.map +1 -0
  233. package/dist/util/markdown.js +220 -0
  234. package/dist/util/paths.d.ts +47 -0
  235. package/dist/util/paths.d.ts.map +1 -0
  236. package/dist/util/paths.js +134 -0
  237. package/dist/util/stream.d.ts +32 -0
  238. package/dist/util/stream.d.ts.map +1 -0
  239. package/dist/util/stream.js +206 -0
  240. package/dist/util/text.d.ts +65 -0
  241. package/dist/util/text.d.ts.map +1 -0
  242. package/dist/util/text.js +419 -0
  243. package/package.json +70 -0
  244. package/src/adapters/index.ts +11 -0
  245. package/src/app/app.ts +1096 -0
  246. package/src/core/animation.ts +144 -0
  247. package/src/core/clipboard.ts +40 -0
  248. package/src/core/commands.ts +169 -0
  249. package/src/core/components.ts +91 -0
  250. package/src/core/events.ts +95 -0
  251. package/src/core/focus.ts +358 -0
  252. package/src/core/i18n.ts +107 -0
  253. package/src/core/keybindings.ts +184 -0
  254. package/src/core/layers.ts +94 -0
  255. package/src/core/manifest.ts +84 -0
  256. package/src/core/navigation.ts +135 -0
  257. package/src/core/resources.ts +362 -0
  258. package/src/core/services.ts +69 -0
  259. package/src/core/store.ts +640 -0
  260. package/src/core/surfaces.ts +292 -0
  261. package/src/core/syntax.ts +200 -0
  262. package/src/core/when.ts +238 -0
  263. package/src/index.ts +76 -0
  264. package/src/jsx/factory.ts +124 -0
  265. package/src/jsx/intrinsics.ts +99 -0
  266. package/src/jsx/jsx-dev-runtime.ts +23 -0
  267. package/src/jsx/jsx-runtime.ts +76 -0
  268. package/src/render/buffer.ts +318 -0
  269. package/src/render/color.ts +180 -0
  270. package/src/render/diff.ts +99 -0
  271. package/src/render/layout.ts +764 -0
  272. package/src/render/static.ts +290 -0
  273. package/src/runtime/bindings.ts +150 -0
  274. package/src/runtime/hooks.ts +1057 -0
  275. package/src/runtime/instance.ts +284 -0
  276. package/src/runtime/paint.ts +737 -0
  277. package/src/runtime/reconcile.ts +335 -0
  278. package/src/runtime/runtime.ts +47 -0
  279. package/src/runtime/style.ts +205 -0
  280. package/src/themes/borders.ts +95 -0
  281. package/src/themes/builtin.ts +301 -0
  282. package/src/themes/glyphs.ts +101 -0
  283. package/src/themes/index.ts +4 -0
  284. package/src/themes/registry.ts +224 -0
  285. package/src/types/adapter.ts +41 -0
  286. package/src/types/animation.ts +34 -0
  287. package/src/types/app.ts +149 -0
  288. package/src/types/async.ts +35 -0
  289. package/src/types/capabilities.ts +79 -0
  290. package/src/types/cells.ts +79 -0
  291. package/src/types/command.ts +139 -0
  292. package/src/types/component-registry.ts +82 -0
  293. package/src/types/disposable.ts +8 -0
  294. package/src/types/focus.ts +77 -0
  295. package/src/types/geometry.ts +50 -0
  296. package/src/types/graph.ts +174 -0
  297. package/src/types/i18n.ts +25 -0
  298. package/src/types/index.ts +31 -0
  299. package/src/types/input.ts +81 -0
  300. package/src/types/keybinding.ts +51 -0
  301. package/src/types/layer.ts +46 -0
  302. package/src/types/manifest.ts +101 -0
  303. package/src/types/markdown.ts +47 -0
  304. package/src/types/navigation.ts +42 -0
  305. package/src/types/render.ts +86 -0
  306. package/src/types/resource.ts +185 -0
  307. package/src/types/services.ts +28 -0
  308. package/src/types/shell.ts +30 -0
  309. package/src/types/store.ts +148 -0
  310. package/src/types/stream.ts +24 -0
  311. package/src/types/style.ts +205 -0
  312. package/src/types/surface.ts +119 -0
  313. package/src/types/syntax.ts +93 -0
  314. package/src/types/terminal.ts +58 -0
  315. package/src/types/theme.ts +121 -0
  316. package/src/types/when.ts +21 -0
  317. package/src/ui/primitives.ts +118 -0
  318. package/src/ui/screen.ts +41 -0
  319. package/src/util/disposable.ts +49 -0
  320. package/src/util/markdown.ts +225 -0
  321. package/src/util/paths.ts +138 -0
  322. package/src/util/stream.ts +213 -0
  323. package/src/util/text.ts +428 -0
@@ -0,0 +1,163 @@
1
+ import { toDisposable } from '../util/disposable.js';
2
+ /** `ctrl+shift+p` or a sequence: `ctrl+k ctrl+s`, `g g`. */
3
+ function parseChord(chord) {
4
+ return chord.trim().split(/\s+/).map(normalizeStroke);
5
+ }
6
+ const MODIFIER = /^(ctrl|control|alt|option|shift|meta|cmd|super)\+/i;
7
+ /**
8
+ * Split one stroke into its modifiers and its key.
9
+ *
10
+ * Modifiers are consumed off the front by name and whatever survives is the
11
+ * key, verbatim. Splitting on `+` cannot work here: `+` is the separator and
12
+ * a key, so `'+'.split('+')` loses the only thing it was asked about. Parsing
13
+ * forwards means `+` is `+`, and `ctrl++` is ctrl and `+`.
14
+ *
15
+ * One parser, exported, because a chord that the test harness presses and a
16
+ * chord that the registry stored have to agree - and two implementations of
17
+ * one grammar disagree eventually.
18
+ */
19
+ export function splitStroke(stroke) {
20
+ const mods = [];
21
+ let rest = stroke.trim();
22
+ for (let match = MODIFIER.exec(rest); match !== null; match = MODIFIER.exec(rest)) {
23
+ const [whole, name = ''] = match;
24
+ mods.push(name.toLowerCase());
25
+ rest = rest.slice(whole.length);
26
+ }
27
+ return { mods, key: rest };
28
+ }
29
+ /** The canonical stroke a chord is filed under. `strokeOf` must agree with it. */
30
+ export function normalizeStroke(stroke) {
31
+ const { mods: names, key } = splitStroke(stroke);
32
+ const mods = new Set();
33
+ for (const name of names) {
34
+ if (name === 'ctrl' || name === 'control')
35
+ mods.add('ctrl');
36
+ else if (name === 'alt' || name === 'option')
37
+ mods.add('alt');
38
+ else if (name === 'shift')
39
+ mods.add('shift');
40
+ else
41
+ mods.add('meta');
42
+ }
43
+ // Shift is not part of a stroke for a single character it *shifted*. A
44
+ // terminal reports a bare shift+p as the character `P`, and `strokeOf` reads
45
+ // it back that way, so `shift+p` would wait for a stroke no keypress makes.
46
+ //
47
+ // Held with another modifier it is the opposite. `ctrl+shift+p` cannot
48
+ // arrive as an uppercase letter, because a control code carries no case:
49
+ // a terminal speaking a keyboard protocol reports the *unshifted* key and a
50
+ // shift bit beside it. Dropping shift here filed `ctrl+shift+f` under
51
+ // `ctrl+f`, where it sat behind whichever binding registered first - so the
52
+ // workspace search had a documented key that ran the in-file search.
53
+ if ([...key].length === 1 && mods.size === 1 && mods.has('shift'))
54
+ mods.delete('shift');
55
+ const order = ['ctrl', 'alt', 'shift', 'meta'].filter((m) => mods.has(m));
56
+ return [...order, key.toLowerCase()].join('+');
57
+ }
58
+ export function strokeOf(event) {
59
+ const mods = [];
60
+ if (event.ctrl)
61
+ mods.push('ctrl');
62
+ if (event.alt)
63
+ mods.push('alt');
64
+ // Shift is implied by an uppercase character, so it is not named for a
65
+ // single character on its own. Beside another modifier nothing is implied -
66
+ // the name is the unshifted key - so there it has to be said. Must agree
67
+ // with `normalizeStroke`.
68
+ const modified = event.ctrl || event.alt || event.meta;
69
+ if (event.shift && (event.name.length > 1 || modified))
70
+ mods.push('shift');
71
+ if (event.meta)
72
+ mods.push('meta');
73
+ return [...mods, event.name.toLowerCase()].join('+');
74
+ }
75
+ /**
76
+ * Keybindings resolve to commands, never to handlers, so the same action is
77
+ * reachable from a chord, a menu row and the palette without three copies.
78
+ * A partially typed chord is remembered, which is what makes `ctrl+k ctrl+s`
79
+ * possible without blocking `ctrl+k` on its own.
80
+ */
81
+ export class Keybindings {
82
+ deps;
83
+ bindings = [];
84
+ pending = [];
85
+ pendingTimer = null;
86
+ constructor(deps) {
87
+ this.deps = deps;
88
+ }
89
+ register(def) {
90
+ const entry = { ...def, strokes: parseChord(def.keys) };
91
+ this.bindings.push(entry);
92
+ this.bindings.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
93
+ return toDisposable(() => {
94
+ const i = this.bindings.indexOf(entry);
95
+ if (i >= 0)
96
+ this.bindings.splice(i, 1);
97
+ });
98
+ }
99
+ unregister(keys, commandId) {
100
+ const strokes = parseChord(keys).join(' ');
101
+ this.bindings = this.bindings.filter((b) => !(b.strokes.join(' ') === strokes && (commandId === undefined || b.commandId === commandId)));
102
+ }
103
+ list() {
104
+ return this.bindings.map(({ strokes: _strokes, ...def }) => def);
105
+ }
106
+ forCommand(commandId) {
107
+ return this.bindings
108
+ // An argument-bearing binding is a key for one invocation, not for the
109
+ // command. `ctrl+shift+b` opens the palette at the sidebar panels; it is
110
+ // not the key for "Command Palette", and offering it as one put a wrong
111
+ // - and wider - hint beside that menu row.
112
+ .filter((b) => b.commandId === commandId && b.args === undefined)
113
+ .map((b) => b.keys);
114
+ }
115
+ applicable(binding) {
116
+ if (!this.deps.when.evaluate(binding.when))
117
+ return false;
118
+ if (!binding.scopeId)
119
+ return true;
120
+ return this.deps.activeScopes().includes(binding.scopeId);
121
+ }
122
+ handle(event) {
123
+ const stroke = strokeOf(event);
124
+ const sequence = [...this.pending, stroke];
125
+ let sawPrefix = false;
126
+ for (const binding of this.bindings) {
127
+ if (!this.applicable(binding))
128
+ continue;
129
+ const { strokes } = binding;
130
+ if (strokes.length === sequence.length && strokes.every((s, i) => s === sequence[i])) {
131
+ this.reset();
132
+ void this.deps.commands
133
+ .execute(binding.commandId, binding.args ?? {}, 'keybinding')
134
+ .catch((err) => this.deps.onError(err, `keybinding ${binding.keys}`));
135
+ return 'handled';
136
+ }
137
+ if (strokes.length > sequence.length &&
138
+ sequence.every((s, i) => strokes[i] === s)) {
139
+ sawPrefix = true;
140
+ }
141
+ }
142
+ if (sawPrefix) {
143
+ this.pending = sequence;
144
+ if (this.pendingTimer)
145
+ clearTimeout(this.pendingTimer);
146
+ this.pendingTimer = setTimeout(() => this.reset(), this.deps.chordTimeoutMs ?? 1200);
147
+ this.pendingTimer.unref?.();
148
+ return 'pending';
149
+ }
150
+ this.reset();
151
+ return 'unhandled';
152
+ }
153
+ reset() {
154
+ this.pending = [];
155
+ if (this.pendingTimer) {
156
+ clearTimeout(this.pendingTimer);
157
+ this.pendingTimer = null;
158
+ }
159
+ }
160
+ }
161
+ export function createKeybindings(deps) {
162
+ return new Keybindings(deps);
163
+ }
@@ -0,0 +1,28 @@
1
+ import type { LayerEntry, LayerManager, LayerName } from '../types/layer.js';
2
+ import type { Disposable } from '../types/disposable.js';
3
+ /**
4
+ * Layers are planes, not components.
5
+ *
6
+ * Dialogs, dropdowns, context menus, tooltips, palettes and toasts all sit on
7
+ * one of five, which is what lets focus trapping, dismissal and paint order be
8
+ * decided once here rather than five times in five components.
9
+ */
10
+ export declare class Layers implements LayerManager {
11
+ private onChange;
12
+ private byId;
13
+ private timers;
14
+ private counter;
15
+ constructor(onChange?: () => void);
16
+ open(entry: LayerEntry): Disposable;
17
+ close(id: string, reason?: 'escape' | 'outside' | 'timeout' | 'api'): void;
18
+ closeLayer(layer: LayerName): void;
19
+ /** Sorted by plane, then by open order - which is paint order. */
20
+ entries(layer?: LayerName): LayerEntry[];
21
+ topmostTrap(): LayerEntry | null;
22
+ /** The entry Escape should close: the last dismissible one. */
23
+ topmostDismissible(): LayerEntry | null;
24
+ update(id: string, patch: Partial<LayerEntry>): void;
25
+ dispose(): void;
26
+ }
27
+ export declare function createLayers(onChange?: () => void): Layers;
28
+ //# sourceMappingURL=layers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layers.d.ts","sourceRoot":"","sources":["../../src/core/layers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAKzD;;;;;;GAMG;AACH,qBAAa,MAAO,YAAW,YAAY;IAK7B,OAAO,CAAC,QAAQ;IAJ5B,OAAO,CAAC,IAAI,CAAiC;IAC7C,OAAO,CAAC,MAAM,CAAoD;IAClE,OAAO,CAAC,OAAO,CAAK;gBAEA,QAAQ,GAAE,MAAM,IAAe;IAEnD,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU;IAcnC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,GAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,KAAa,GAAG,IAAI;IAkBjF,UAAU,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAIlC,kEAAkE;IAClE,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,UAAU,EAAE;IASxC,WAAW,IAAI,UAAU,GAAG,IAAI;IAKhC,+DAA+D;IAC/D,kBAAkB,IAAI,UAAU,GAAG,IAAI;IAKvC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI;IAOpD,OAAO,IAAI,IAAI;CAKhB;AAED,wBAAgB,YAAY,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,MAAM,CAE1D"}
@@ -0,0 +1,84 @@
1
+ import { toDisposable } from '../util/disposable.js';
2
+ const LAYER_ORDER = ['base', 'floating', 'modal', 'notification', 'debug'];
3
+ /**
4
+ * Layers are planes, not components.
5
+ *
6
+ * Dialogs, dropdowns, context menus, tooltips, palettes and toasts all sit on
7
+ * one of five, which is what lets focus trapping, dismissal and paint order be
8
+ * decided once here rather than five times in five components.
9
+ */
10
+ export class Layers {
11
+ onChange;
12
+ byId = new Map();
13
+ timers = new Map();
14
+ counter = 0;
15
+ constructor(onChange = () => { }) {
16
+ this.onChange = onChange;
17
+ }
18
+ open(entry) {
19
+ const order = entry.order ?? ++this.counter;
20
+ this.byId.set(entry.id, { ...entry, order });
21
+ if (entry.timeoutMs && entry.timeoutMs > 0) {
22
+ const timer = setTimeout(() => this.close(entry.id, 'timeout'), entry.timeoutMs);
23
+ timer.unref?.();
24
+ this.timers.set(entry.id, timer);
25
+ }
26
+ this.onChange();
27
+ return toDisposable(() => this.close(entry.id, 'api'));
28
+ }
29
+ close(id, reason = 'api') {
30
+ const entry = this.byId.get(id);
31
+ if (!entry)
32
+ return;
33
+ const timer = this.timers.get(id);
34
+ if (timer) {
35
+ clearTimeout(timer);
36
+ this.timers.delete(id);
37
+ }
38
+ this.byId.delete(id);
39
+ try {
40
+ entry.onClose?.(reason);
41
+ }
42
+ finally {
43
+ this.onChange();
44
+ }
45
+ }
46
+ closeLayer(layer) {
47
+ for (const entry of this.entries(layer))
48
+ this.close(entry.id, 'api');
49
+ }
50
+ /** Sorted by plane, then by open order - which is paint order. */
51
+ entries(layer) {
52
+ const all = [...this.byId.values()];
53
+ const filtered = layer ? all.filter((e) => e.layer === layer) : all;
54
+ return filtered.sort((a, b) => {
55
+ const byLayer = LAYER_ORDER.indexOf(a.layer) - LAYER_ORDER.indexOf(b.layer);
56
+ return byLayer !== 0 ? byLayer : (a.order ?? 0) - (b.order ?? 0);
57
+ });
58
+ }
59
+ topmostTrap() {
60
+ const trapping = this.entries().filter((e) => e.trapFocus);
61
+ return trapping[trapping.length - 1] ?? null;
62
+ }
63
+ /** The entry Escape should close: the last dismissible one. */
64
+ topmostDismissible() {
65
+ const list = this.entries().filter((e) => e.dismissOnEscape !== false);
66
+ return list[list.length - 1] ?? null;
67
+ }
68
+ update(id, patch) {
69
+ const entry = this.byId.get(id);
70
+ if (!entry)
71
+ return;
72
+ this.byId.set(id, { ...entry, ...patch });
73
+ this.onChange();
74
+ }
75
+ dispose() {
76
+ for (const timer of this.timers.values())
77
+ clearTimeout(timer);
78
+ this.timers.clear();
79
+ this.byId.clear();
80
+ }
81
+ }
82
+ export function createLayers(onChange) {
83
+ return new Layers(onChange);
84
+ }
@@ -0,0 +1,22 @@
1
+ import type { Manifest, ManifestAPI, ManifestSource } from '../types/manifest.js';
2
+ import type { TextUIApp } from '../types/app.js';
3
+ import type { Disposable } from '../types/disposable.js';
4
+ /**
5
+ * Manifests.
6
+ *
7
+ * Separate typed lists rather than one generic plugin bag: every category has
8
+ * its own contract, so a bad contribution fails where it is written instead of
9
+ * at some later lookup. Loading returns one disposable that unwinds everything
10
+ * the source contributed, which is what makes unloading exact.
11
+ */
12
+ export declare class Manifests implements ManifestAPI {
13
+ private app;
14
+ private loaded_;
15
+ constructor(app: TextUIApp);
16
+ load(manifest: Manifest): Promise<Disposable>;
17
+ unload(sourceId: string): void;
18
+ sources(): ManifestSource[];
19
+ loaded(sourceId: string): boolean;
20
+ }
21
+ export declare function createManifests(app: TextUIApp): Manifests;
22
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/core/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD;;;;;;;GAOG;AACH,qBAAa,SAAU,YAAW,WAAW;IAG/B,OAAO,CAAC,GAAG;IAFvB,OAAO,CAAC,OAAO,CAAyE;gBAEpE,GAAG,EAAE,SAAS;IAE5B,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC;IAkDnD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAI9B,OAAO,IAAI,cAAc,EAAE;IAI3B,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGlC;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,GAAG,SAAS,CAEzD"}
@@ -0,0 +1,85 @@
1
+ import { createBag } from '../util/disposable.js';
2
+ /**
3
+ * Manifests.
4
+ *
5
+ * Separate typed lists rather than one generic plugin bag: every category has
6
+ * its own contract, so a bad contribution fails where it is written instead of
7
+ * at some later lookup. Loading returns one disposable that unwinds everything
8
+ * the source contributed, which is what makes unloading exact.
9
+ */
10
+ export class Manifests {
11
+ app;
12
+ loaded_ = new Map();
13
+ constructor(app) {
14
+ this.app = app;
15
+ }
16
+ async load(manifest) {
17
+ const { id } = manifest.source;
18
+ if (this.loaded_.has(id)) {
19
+ throw new Error(`[textui] manifest "${id}" is already loaded`);
20
+ }
21
+ for (const required of manifest.requires ?? []) {
22
+ if (!this.loaded_.has(required)) {
23
+ throw new Error(`[textui] manifest "${id}" requires "${required}", which is not loaded`);
24
+ }
25
+ }
26
+ const caps = this.app.capabilities;
27
+ const missing = (manifest.requiresCapabilities ?? []).filter((c) => !caps[c]);
28
+ if (missing.length > 0) {
29
+ throw new Error(`[textui] manifest "${id}" needs terminal capabilities this session lacks: ${missing.join(', ')}`);
30
+ }
31
+ const bag = createBag();
32
+ const c = manifest.contributes ?? {};
33
+ for (const def of c.components ?? [])
34
+ bag.add(this.app.components.register(def));
35
+ for (const def of c.commands ?? [])
36
+ bag.add(this.app.commands.register(def));
37
+ for (const def of c.keybindings ?? [])
38
+ bag.add(this.app.keybindings.register(def));
39
+ for (const def of c.themes ?? [])
40
+ bag.add(this.app.themes.register(def));
41
+ for (const def of c.shells ?? [])
42
+ bag.add(this.app.shells.register(def));
43
+ for (const def of c.layouts ?? [])
44
+ bag.add(this.app.layouts.register(def));
45
+ for (const def of c.screens ?? [])
46
+ bag.add(this.app.screens.register(def));
47
+ for (const def of c.resourceKinds ?? [])
48
+ bag.add(this.app.resources.registerKind(def));
49
+ for (const def of c.resourceProviders ?? [])
50
+ bag.add(this.app.resources.registerProvider(def));
51
+ for (const def of c.resourceViewers ?? [])
52
+ bag.add(this.app.resources.registerViewer(def));
53
+ for (const def of c.resourceEditors ?? [])
54
+ bag.add(this.app.resources.registerEditor(def));
55
+ for (const def of c.resourceActions ?? [])
56
+ bag.add(this.app.resources.registerAction(def));
57
+ for (const def of c.dataProviders ?? [])
58
+ bag.add(this.app.store.registerDataProvider(def));
59
+ for (const entry of c.computed ?? [])
60
+ bag.add(this.app.store.computed(entry.path, entry.def));
61
+ // Views mount last: they may name a component this manifest just added.
62
+ for (const mount of c.views ?? [])
63
+ bag.add(this.app.open(mount));
64
+ const disposable = {
65
+ dispose: () => {
66
+ bag.dispose();
67
+ this.loaded_.delete(id);
68
+ },
69
+ };
70
+ this.loaded_.set(id, { source: manifest.source, disposable });
71
+ return disposable;
72
+ }
73
+ unload(sourceId) {
74
+ this.loaded_.get(sourceId)?.disposable.dispose();
75
+ }
76
+ sources() {
77
+ return [...this.loaded_.values()].map((e) => e.source);
78
+ }
79
+ loaded(sourceId) {
80
+ return this.loaded_.has(sourceId);
81
+ }
82
+ }
83
+ export function createManifests(app) {
84
+ return new Manifests(app);
85
+ }
@@ -0,0 +1,47 @@
1
+ import type { Navigator, ScreenDefinition, ScreenEntry } from '../types/navigation.js';
2
+ import type { ReactiveStore } from '../types/store.js';
3
+ import type { FocusManager } from '../types/focus.js';
4
+ import type { Disposable } from '../types/disposable.js';
5
+ /**
6
+ * Screens and a stack, not a router.
7
+ *
8
+ * The whole navigation model is "what is on top", plus enough memory to put
9
+ * focus back where it was when you return. An application that wants URLs maps
10
+ * them onto screens itself; nothing here assumes a browser exists.
11
+ */
12
+ export declare class Navigation implements Navigator {
13
+ private deps;
14
+ private defs;
15
+ private entries;
16
+ constructor(deps: {
17
+ store: ReactiveStore;
18
+ focus: FocusManager;
19
+ onChange(): void;
20
+ /**
21
+ * Put the current screen on screen.
22
+ *
23
+ * A callback rather than a surface registry, because what a screen *is*
24
+ * - a stack entry and a definition - has nothing to do with where the
25
+ * application decided to draw it.
26
+ */
27
+ mount(entry: ScreenEntry | null): void;
28
+ });
29
+ register(screen: ScreenDefinition): Disposable;
30
+ screens(): ScreenDefinition[];
31
+ get(id: string): ScreenDefinition | undefined;
32
+ private publish;
33
+ /** Remember where focus was, so `pop` can put it back. */
34
+ private snapshotFocus;
35
+ push(id: string, params?: Record<string, unknown>): void;
36
+ replace(id: string, params?: Record<string, unknown>): void;
37
+ pop(): boolean;
38
+ popTo(id: string): boolean;
39
+ reset(id: string, params?: Record<string, unknown>): void;
40
+ current(): ScreenEntry | null;
41
+ stack(): ScreenEntry[];
42
+ canGoBack(): boolean;
43
+ /** A screen's own scope dies with it, unless it asked to be kept alive. */
44
+ private clearScreenScope;
45
+ }
46
+ export declare function createNavigation(deps: ConstructorParameters<typeof Navigation>[0]): Navigation;
47
+ //# sourceMappingURL=navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/core/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD;;;;;;GAMG;AACH,qBAAa,UAAW,YAAW,SAAS;IAKxC,OAAO,CAAC,IAAI;IAJd,OAAO,CAAC,IAAI,CAAuC;IACnD,OAAO,CAAC,OAAO,CAAqB;gBAG1B,IAAI,EAAE;QACZ,KAAK,EAAE,aAAa,CAAC;QACrB,KAAK,EAAE,YAAY,CAAC;QACpB,QAAQ,IAAI,IAAI,CAAC;QACjB;;;;;;WAMG;QACH,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;KACxC;IAGH,QAAQ,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IAK9C,OAAO,IAAI,gBAAgB,EAAE;IAI7B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IAI7C,OAAO,CAAC,OAAO;IAYf,0DAA0D;IAC1D,OAAO,CAAC,aAAa;IAKrB,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IASxD,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAU3D,GAAG,IAAI,OAAO;IAWd,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAY1B,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAMzD,OAAO,IAAI,WAAW,GAAG,IAAI;IAI7B,KAAK,IAAI,WAAW,EAAE;IAItB,SAAS,IAAI,OAAO;IAIpB,2EAA2E;IAC3E,OAAO,CAAC,gBAAgB;CAKzB;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,qBAAqB,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,UAAU,CAE9F"}
@@ -0,0 +1,110 @@
1
+ import { toDisposable } from '../util/disposable.js';
2
+ /**
3
+ * Screens and a stack, not a router.
4
+ *
5
+ * The whole navigation model is "what is on top", plus enough memory to put
6
+ * focus back where it was when you return. An application that wants URLs maps
7
+ * them onto screens itself; nothing here assumes a browser exists.
8
+ */
9
+ export class Navigation {
10
+ deps;
11
+ defs = new Map();
12
+ entries = [];
13
+ constructor(deps) {
14
+ this.deps = deps;
15
+ }
16
+ register(screen) {
17
+ this.defs.set(screen.id, screen);
18
+ return toDisposable(() => this.defs.delete(screen.id));
19
+ }
20
+ screens() {
21
+ return [...this.defs.values()];
22
+ }
23
+ get(id) {
24
+ return this.defs.get(id);
25
+ }
26
+ publish() {
27
+ const current = this.current();
28
+ this.deps.store.set('$/layout/screen/stack', this.entries.map((e) => e.id));
29
+ this.deps.store.set('$/layout/screen/current', current?.id ?? null);
30
+ // Published as well as passed as props, so a component deep inside a
31
+ // screen can read the parameters without every layer between forwarding
32
+ // them.
33
+ this.deps.store.set('$/layout/screen/params', current?.params ?? {});
34
+ this.deps.mount(current);
35
+ this.deps.onChange();
36
+ }
37
+ /** Remember where focus was, so `pop` can put it back. */
38
+ snapshotFocus() {
39
+ const top = this.entries[this.entries.length - 1];
40
+ if (top)
41
+ top.restoreFocus = this.deps.focus.focused();
42
+ }
43
+ push(id, params) {
44
+ if (!this.defs.has(id)) {
45
+ throw new Error(`[textui] no screen registered as "${id}"`);
46
+ }
47
+ this.snapshotFocus();
48
+ this.entries.push({ id, params });
49
+ this.publish();
50
+ }
51
+ replace(id, params) {
52
+ if (!this.defs.has(id)) {
53
+ throw new Error(`[textui] no screen registered as "${id}"`);
54
+ }
55
+ const previous = this.entries.pop();
56
+ this.clearScreenScope(previous);
57
+ this.entries.push({ id, params });
58
+ this.publish();
59
+ }
60
+ pop() {
61
+ if (this.entries.length <= 1)
62
+ return false;
63
+ const previous = this.entries.pop();
64
+ this.clearScreenScope(previous);
65
+ const top = this.entries[this.entries.length - 1];
66
+ if (top?.restoreFocus)
67
+ this.deps.focus.focus(top.restoreFocus);
68
+ this.publish();
69
+ return true;
70
+ }
71
+ popTo(id) {
72
+ const index = this.entries.findIndex((e) => e.id === id);
73
+ if (index === -1)
74
+ return false;
75
+ for (const entry of this.entries.slice(index + 1))
76
+ this.clearScreenScope(entry);
77
+ this.entries = this.entries.slice(0, index + 1);
78
+ const top = this.entries[this.entries.length - 1];
79
+ if (top?.restoreFocus)
80
+ this.deps.focus.focus(top.restoreFocus);
81
+ this.publish();
82
+ return true;
83
+ }
84
+ reset(id, params) {
85
+ for (const entry of this.entries)
86
+ this.clearScreenScope(entry);
87
+ this.entries = [{ id, params }];
88
+ this.publish();
89
+ }
90
+ current() {
91
+ return this.entries[this.entries.length - 1] ?? null;
92
+ }
93
+ stack() {
94
+ return [...this.entries];
95
+ }
96
+ canGoBack() {
97
+ return this.entries.length > 1;
98
+ }
99
+ /** A screen's own scope dies with it, unless it asked to be kept alive. */
100
+ clearScreenScope(entry) {
101
+ if (!entry)
102
+ return;
103
+ if (this.defs.get(entry.id)?.keepAlive)
104
+ return;
105
+ this.deps.store.clearScope(`screen.${entry.id}`);
106
+ }
107
+ }
108
+ export function createNavigation(deps) {
109
+ return new Navigation(deps);
110
+ }
@@ -0,0 +1,115 @@
1
+ import type { Resource, ResourceActionDefinition, ResourceEditorDefinition, ResourceKind, ResourceMetadata, ResourceProvider, ResourceRegistry, ResourceURI, ResourceRendererDefinition, ResourceViewerDefinition } from '../types/resource.js';
2
+ import type { ComponentNode } from '../types/graph.js';
3
+ import type { ComponentRegistry } from '../types/component-registry.js';
4
+ import type { WhenEngine } from '../types/when.js';
5
+ import type { Disposable } from '../types/disposable.js';
6
+ import type { SemanticVariant } from '../types/style.js';
7
+ /**
8
+ * The resource model.
9
+ *
10
+ * A resource is anything addressable that something can be registered to
11
+ * display, edit or act on - a file, a record, a log stream, a service. The
12
+ * filesystem is one provider among several rather than the model itself, which
13
+ * is what lets an explorer browse services and files with the same component.
14
+ *
15
+ * Kinds form a hierarchy by dotted name: `file.markdown` specialises
16
+ * `file.text`, so a viewer registered for `file.text` still opens a markdown
17
+ * file when no more specific one exists.
18
+ */
19
+ export declare class Resources implements ResourceRegistry {
20
+ private deps;
21
+ private kindDefs;
22
+ private providers;
23
+ private viewers;
24
+ private editors;
25
+ private actions;
26
+ constructor(deps: {
27
+ components: ComponentRegistry;
28
+ when: WhenEngine;
29
+ });
30
+ registerKind(kind: ResourceKind): Disposable;
31
+ registerProvider(provider: ResourceProvider): Disposable;
32
+ registerViewer(def: ResourceViewerDefinition): Disposable;
33
+ registerEditor(def: ResourceEditorDefinition): Disposable;
34
+ registerAction(def: ResourceActionDefinition): Disposable;
35
+ kinds(): ResourceKind[];
36
+ /** Extension, then mime type, then an explicit `detect`. Best match wins. */
37
+ detectKind(uri: ResourceURI, meta?: ResourceMetadata): string;
38
+ kindMatches(kind: string, ancestor: string): boolean;
39
+ private provider;
40
+ stat(uri: ResourceURI): Promise<Resource | null>;
41
+ list(uri: ResourceURI): Promise<Resource[]>;
42
+ read(uri: ResourceURI): Promise<string | Uint8Array>;
43
+ write(uri: ResourceURI, content: string | Uint8Array): Promise<void>;
44
+ /**
45
+ * Destructive calls throw when the provider cannot serve them, the same way
46
+ * `write` does. A silent no-op is the failure mode worth designing against:
47
+ * a Delete that appears in the menu and removes nothing is worse than one
48
+ * that says it cannot.
49
+ *
50
+ * Whether to *offer* the action is a different question, answered before
51
+ * the call by `Resource.capabilities` - which is per resource rather than
52
+ * per provider, so a read-only mount, a file without permission and an
53
+ * inherently immutable resource are all the same shape. The throw is for
54
+ * the race that asking first cannot close: the remote goes away, the
55
+ * permission changes, between the offer and the call.
56
+ */
57
+ delete(uri: ResourceURI): Promise<void>;
58
+ /**
59
+ * A rename is within one provider. Two schemes is a move between providers,
60
+ * which is a copy and a delete and belongs above a registry that dispatches
61
+ * on a single scheme by construction.
62
+ */
63
+ rename(from: ResourceURI, to: ResourceURI): Promise<void>;
64
+ /**
65
+ * Watching follows `list` rather than `write`: a provider that cannot watch
66
+ * gets a disposable that does nothing, because "I will never tell you about
67
+ * a change" is a truthful answer to an optional question. Making every
68
+ * caller guard a subscription would buy nothing.
69
+ */
70
+ watch(uri: ResourceURI, fn: (event: 'change' | 'create' | 'delete') => void): Disposable;
71
+ viewersFor(kind: string): ResourceViewerDefinition[];
72
+ editorsFor(kind: string): ResourceEditorDefinition[];
73
+ /**
74
+ * Every way of showing this kind, best first.
75
+ *
76
+ * Three registries answer the same question - an editor, a viewer and a
77
+ * component that declared `opens` are all "a component that can put this on
78
+ * screen" - and a panel offering "open with" wants one list. Saving is a
79
+ * property of the renderer here rather than the registry it came from, which
80
+ * is what lets `file.markdown` list an editor, a rendered view and plain
81
+ * text together and lets the panel pick between them.
82
+ */
83
+ /**
84
+ * What a resource looks like in a list.
85
+ *
86
+ * Asked of the *renderer* first, because the thing that knows what a
87
+ * markdown file is is the thing that opens markdown files - so a viewer an
88
+ * extension brought names and colours its own kind without anything here
89
+ * learning what it is. Then the kind, walking up `extends`, since
90
+ * `file.markdown` inheriting Text's icon is better than inheriting nothing.
91
+ *
92
+ * Answers nothing rather than a default: what a nameless file looks like is
93
+ * the caller's vocabulary, because which glyphs a terminal can draw is known
94
+ * where the terminal is and not here.
95
+ */
96
+ appearanceOf(resource: {
97
+ kind: string;
98
+ }): {
99
+ icon?: string;
100
+ tone?: SemanticVariant;
101
+ };
102
+ renderersFor(kind: string): ResourceRendererDefinition[];
103
+ actionsFor(kind: string, slot?: string): ResourceActionDefinition[];
104
+ /**
105
+ * The node that displays this resource. An explicit `viewerId` wins; then a
106
+ * registered editor when edit was asked for; then the best viewer; then a
107
+ * component that declared `opens` for this kind; then nothing.
108
+ */
109
+ nodeFor(resource: Resource, options?: {
110
+ viewerId?: string;
111
+ mode?: 'view' | 'edit';
112
+ }): ComponentNode | null;
113
+ }
114
+ export declare function createResources(deps: ConstructorParameters<typeof Resources>[0]): Resources;
115
+ //# sourceMappingURL=resources.d.ts.map