@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,86 @@
1
+ import type { Rect, Size } from './geometry.js';
2
+ import type { Cell, Color } from './cells.js';
3
+ import type { Attrs } from './cells.js';
4
+ import type { Style } from './style.js';
5
+ import type { ResolvedTheme } from './theme.js';
6
+ import type { TerminalCapabilities } from './capabilities.js';
7
+ import type { ComponentNode } from './graph.js';
8
+
9
+ /** What a host component paints onto. Clipped to the node's own rect. */
10
+ export interface PaintSurface {
11
+ readonly rect: Rect;
12
+ /** Write a grapheme at a content-relative cell. Out of bounds is a no-op. */
13
+ put(x: number, y: number, char: string, style?: CellStyle): void;
14
+ /** Write a string, advancing by grapheme width. Returns cells consumed. */
15
+ text(x: number, y: number, text: string, style?: CellStyle): number;
16
+ fill(rect: Rect | undefined, char: string, style?: CellStyle): void;
17
+ /** Direct cell write, for painters that already resolved everything. */
18
+ cell(x: number, y: number, cell: Cell): void;
19
+ /** A sub-surface, clipped and offset. */
20
+ clip(rect: Rect): PaintSurface;
21
+ }
22
+
23
+ export interface CellStyle {
24
+ fg?: Color;
25
+ bg?: Color;
26
+ attrs?: Attrs;
27
+ link?: string;
28
+ }
29
+
30
+ /** Everything a host needs that is not its props. */
31
+ export interface RenderContext {
32
+ theme: ResolvedTheme;
33
+ capabilities: TerminalCapabilities;
34
+ /** Interaction state, already resolved for this instance. */
35
+ focused: boolean;
36
+ hovered: boolean;
37
+ active: boolean;
38
+ selected: boolean;
39
+ disabled: boolean;
40
+ /** Resolve a style token or literal to a concrete colour. */
41
+ color(c: unknown, fallback?: Color): Color;
42
+ /** Glyph for a role, downgraded to ascii when the terminal demands it. */
43
+ glyph(name: string): string;
44
+ /** Measure a string in terminal cells, honouring wide and zero-width. */
45
+ measureText(text: string): number;
46
+ }
47
+
48
+ export interface MeasureConstraints {
49
+ /** Infinity means "as much as you want". */
50
+ maxWidth: number;
51
+ maxHeight: number;
52
+ minWidth: number;
53
+ minHeight: number;
54
+ }
55
+
56
+ /**
57
+ * A host component is a primitive that participates in layout and paint
58
+ * directly. There are deliberately few of them - Box, Text and Canvas - and
59
+ * the entire catalog is function components composing those, so the layout
60
+ * engine only ever reasons about three shapes.
61
+ */
62
+ export interface HostComponent<P = Record<string, unknown>> {
63
+ name: string;
64
+ /** Style contributed by props, merged under the node's own `style` prop. */
65
+ style?(props: P, ctx: RenderContext): Style | undefined;
66
+ /** Intrinsic size when the style does not fix one. */
67
+ measure?(props: P, ctx: RenderContext, constraints: MeasureConstraints): Size;
68
+ /** Paint the content box. Border and background are painted by the runtime. */
69
+ paint?(surface: PaintSurface, props: P, ctx: RenderContext): void;
70
+ /** True when this host lays out children (Box). */
71
+ container?: boolean;
72
+ /** Never receives children; a children prop is a programmer error. */
73
+ leaf?: boolean;
74
+ }
75
+
76
+ export type FunctionComponent<P = any> = (props: P) => RenderOutput;
77
+
78
+ export type RenderOutput =
79
+ | ComponentNode
80
+ | ComponentNode[]
81
+ | string
82
+ | number
83
+ | null
84
+ | undefined
85
+ | false
86
+ | RenderOutput[];
@@ -0,0 +1,185 @@
1
+ import type { Disposable } from './disposable.js';
2
+ import type { ComponentNode } from './graph.js';
3
+ import type { WhenClause } from './when.js';
4
+ import type { CommandHandler } from './command.js';
5
+ import type { SemanticVariant } from './style.js';
6
+
7
+ /**
8
+ * A resource is anything addressable that a viewer, an editor or an action can
9
+ * be registered for - a file, a record, a log stream, a service, a chart
10
+ * source. The filesystem is one provider among several, not the model.
11
+ */
12
+ export type ResourceURI = string;
13
+
14
+ export interface ResourceKind {
15
+ /** Stable id: 'file.markdown', 'service', 'log.stream'. */
16
+ id: string;
17
+ title: string;
18
+ icon?: string;
19
+ /**
20
+ * What colour the icon is, as a role rather than a colour.
21
+ *
22
+ * Named the way every other tone in the library is, so a kind picks
23
+ * "informational" and the theme decides what that looks like - a kind
24
+ * carrying `#d19a66` would be a kind that looks wrong in half the themes.
25
+ */
26
+ tone?: SemanticVariant;
27
+ /** MIME types this kind claims. */
28
+ mimeTypes?: string[];
29
+ /** Filename globs this kind claims. */
30
+ extensions?: string[];
31
+ /** Kinds this one specialises. `file.markdown` extends `file.text`. */
32
+ extends?: string;
33
+ /** Higher wins when several kinds match. */
34
+ priority?: number;
35
+ /** Last word on ambiguous matches. */
36
+ detect?(uri: ResourceURI, meta: ResourceMetadata): boolean;
37
+ }
38
+
39
+ export interface ResourceMetadata {
40
+ name: string;
41
+ size?: number;
42
+ modified?: number;
43
+ created?: number;
44
+ mimeType?: string;
45
+ readonly?: boolean;
46
+ /** Provider-specific extras. */
47
+ [key: string]: unknown;
48
+ }
49
+
50
+ /**
51
+ * What a *resource* can have done to it - not what its provider implements.
52
+ *
53
+ * Per resource on purpose: a read-only mount, a file the user has no
54
+ * permission on, and something inherently immutable all come back the same
55
+ * shape, and a caller deciding whether to offer Delete should never have to
56
+ * know which of the three it is looking at.
57
+ *
58
+ * This is what to ask before calling; the registry method still throws if the
59
+ * answer changed in between. `stream` is the odd one - declarative only, no
60
+ * registry method reads it. It stays for a log provider to claim, and comes
61
+ * out if none does.
62
+ */
63
+ export type ResourceCapability =
64
+ | 'read' | 'write' | 'delete' | 'rename' | 'list' | 'watch' | 'stream';
65
+
66
+ export interface Resource {
67
+ uri: ResourceURI;
68
+ kind: string;
69
+ metadata: ResourceMetadata;
70
+ capabilities: ResourceCapability[];
71
+ }
72
+
73
+ /** Where resources come from. One per URI scheme. */
74
+ export interface ResourceProvider {
75
+ scheme: string;
76
+ stat(uri: ResourceURI): Promise<Resource | null>;
77
+ list?(uri: ResourceURI): Promise<Resource[]>;
78
+ read?(uri: ResourceURI): Promise<string | Uint8Array>;
79
+ write?(uri: ResourceURI, content: string | Uint8Array): Promise<void>;
80
+ delete?(uri: ResourceURI): Promise<void>;
81
+ rename?(from: ResourceURI, to: ResourceURI): Promise<void>;
82
+ watch?(uri: ResourceURI, fn: (event: 'change' | 'create' | 'delete') => void): Disposable;
83
+ }
84
+
85
+ /** A component that can display one kind of resource. */
86
+ export interface ResourceViewerDefinition {
87
+ id: string;
88
+ title: string;
89
+ kinds: string[];
90
+ /** Component name; receives `{ resource, content }`. */
91
+ component: string;
92
+ icon?: string;
93
+ /** As on a kind. A renderer that declares one speaks for what it opens. */
94
+ tone?: SemanticVariant;
95
+ priority?: number;
96
+ when?: WhenClause;
97
+ /** Accept anything no specific viewer claims. */
98
+ fallback?: boolean;
99
+ }
100
+
101
+ export interface ResourceEditorDefinition extends ResourceViewerDefinition {
102
+ /** Editors must be able to hand back new content. */
103
+ saves: true;
104
+ }
105
+
106
+ export interface ResourceActionDefinition {
107
+ id: string;
108
+ title: string;
109
+ kinds: string[];
110
+ icon?: string;
111
+ when?: WhenClause;
112
+ /** Where the action offers itself: 'context', 'toolbar', 'palette'. */
113
+ slots?: string[];
114
+ priority?: number;
115
+ run: CommandHandler;
116
+ }
117
+
118
+ export interface ResourceRegistry {
119
+ registerKind(kind: ResourceKind): Disposable;
120
+ registerProvider(provider: ResourceProvider): Disposable;
121
+ registerViewer(def: ResourceViewerDefinition): Disposable;
122
+ registerEditor(def: ResourceEditorDefinition): Disposable;
123
+ registerAction(def: ResourceActionDefinition): Disposable;
124
+
125
+ kinds(): ResourceKind[];
126
+ /** Classify a resource: extension, then mime, then `detect`, then parent. */
127
+ detectKind(uri: ResourceURI, meta?: ResourceMetadata): string;
128
+ /** True when `kind` is `ancestor` or specialises it. */
129
+ kindMatches(kind: string, ancestor: string): boolean;
130
+
131
+ // The registry forwards every operation a provider can offer, so a caller
132
+ // never needs to reach past it to a provider - there is no public path to
133
+ // one, and a caller that found one would be hardcoding a scheme.
134
+ //
135
+ // `list` and `watch` answer emptily when the provider cannot serve them;
136
+ // `read`, `write`, `delete` and `rename` throw. The split is intent: an
137
+ // absent listing is a fact about the resource, and an absent delete is a
138
+ // request that did not happen and must say so.
139
+ stat(uri: ResourceURI): Promise<Resource | null>;
140
+ list(uri: ResourceURI): Promise<Resource[]>;
141
+ read(uri: ResourceURI): Promise<string | Uint8Array>;
142
+ write(uri: ResourceURI, content: string | Uint8Array): Promise<void>;
143
+ /** Throws when the provider cannot. Ask `Resource.capabilities` first. */
144
+ delete(uri: ResourceURI): Promise<void>;
145
+ /** Within one scheme. Across two it throws - that is a copy and a delete. */
146
+ rename(from: ResourceURI, to: ResourceURI): Promise<void>;
147
+ /** A provider that cannot watch gives back a disposable that does nothing. */
148
+ watch(uri: ResourceURI, fn: (event: 'change' | 'create' | 'delete') => void): Disposable;
149
+
150
+ viewersFor(kind: string): ResourceViewerDefinition[];
151
+ editorsFor(kind: string): ResourceEditorDefinition[];
152
+ /** Every renderer for this kind - editors, viewers and openers - best first. */
153
+ renderersFor(kind: string): ResourceRendererDefinition[];
154
+ /**
155
+ * The icon and tone for a resource: the renderer that opens it, then the
156
+ * kind and whatever it extends. Empty when nobody has said.
157
+ */
158
+ appearanceOf(resource: { kind: string }): { icon?: string; tone?: SemanticVariant };
159
+ actionsFor(kind: string, slot?: string): ResourceActionDefinition[];
160
+
161
+ /** The node that displays this resource, viewer chosen by the registry. */
162
+ nodeFor(resource: Resource, options?: { viewerId?: string; mode?: 'view' | 'edit' }): ComponentNode | null;
163
+ }
164
+
165
+ /**
166
+ * One way of showing a resource.
167
+ *
168
+ * A viewer, an editor and a component that declared `opens` are three ways of
169
+ * registering the same thing: a component that can put this kind on screen.
170
+ * A panel wants the list, not the three lists - which one saves is a property
171
+ * of the renderer, not a different registry.
172
+ */
173
+ export interface ResourceRendererDefinition {
174
+ id: string;
175
+ title: string;
176
+ kinds: string[];
177
+ component: string;
178
+ icon?: string;
179
+ /** As on a kind. A renderer that declares one speaks for what it opens. */
180
+ tone?: SemanticVariant;
181
+ priority?: number;
182
+ when?: WhenClause;
183
+ /** True when this renderer can write the resource back. */
184
+ saves?: boolean;
185
+ }
@@ -0,0 +1,28 @@
1
+ import type { Disposable } from './disposable.js';
2
+
3
+ /**
4
+ * A service key is a typed token. This is a lookup table with types, not a
5
+ * dependency-injection container - there is no lifecycle, no scoping and no
6
+ * auto-wiring, on purpose.
7
+ */
8
+ export interface ServiceKey<T> {
9
+ readonly id: string;
10
+ /** Never present at runtime; carries the type. */
11
+ readonly __type?: T;
12
+ }
13
+
14
+ export function serviceKey<T>(id: string): ServiceKey<T> {
15
+ return { id };
16
+ }
17
+
18
+ export interface ServiceContainer {
19
+ provide<T>(key: ServiceKey<T>, value: T): Disposable;
20
+ /** Created on first use. */
21
+ provideLazy<T>(key: ServiceKey<T>, factory: () => T): Disposable;
22
+ get<T>(key: ServiceKey<T>): T | undefined;
23
+ /** Throws with the key id when missing - a clear programmer error. */
24
+ require<T>(key: ServiceKey<T>): T;
25
+ has<T>(key: ServiceKey<T>): boolean;
26
+ /** A child container that falls back to this one. */
27
+ child(): ServiceContainer;
28
+ }
@@ -0,0 +1,30 @@
1
+ import type { Disposable } from './disposable.js';
2
+ import type { SurfaceName } from './surface.js';
3
+
4
+ /**
5
+ * The shell is the frame around the surfaces. Where it puts them is the whole
6
+ * of what a shell decides - which is why the same application renders as a
7
+ * dense bordered console, an airy borderless report or a full workbench by
8
+ * changing one registration.
9
+ */
10
+ export interface ShellDefinition {
11
+ id: string;
12
+ title: string;
13
+ description?: string;
14
+ /** Component name; receives `{ surfaces, renderSurface }`. */
15
+ component: string;
16
+ /** Surfaces this shell renders. Others stay unmounted. */
17
+ surfaces?: SurfaceName[];
18
+ /** Default theme id this shell was designed against. */
19
+ theme?: string;
20
+ /** Below this width or height the shell is not offered. */
21
+ minSize?: { width?: number; height?: number };
22
+ }
23
+
24
+ export interface ShellRegistry {
25
+ register(def: ShellDefinition): Disposable;
26
+ get(id: string): ShellDefinition | undefined;
27
+ list(): ShellDefinition[];
28
+ /** Shells that fit the current terminal, best first. */
29
+ suitable(width: number, height: number): ShellDefinition[];
30
+ }
@@ -0,0 +1,148 @@
1
+ import type { Disposable } from './disposable.js';
2
+ import type { BindingPath, EventPath } from './graph.js';
3
+
4
+ /**
5
+ * Scopes are lifetimes, not folders. The first segment of an absolute path
6
+ * names one, and `clearScope` is what makes sign-out or screen-teardown a
7
+ * single call rather than a cascade of resets.
8
+ */
9
+ export type ScopeName =
10
+ | 'local' // dies with the mount
11
+ | 'screen' // dies with the screen
12
+ | 'session' // dies with the process
13
+ | 'app' // outlives screens; the application's own state
14
+ | 'global' // outlives everything the app can clear
15
+ | 'summary' // derived counts and rollups
16
+ | 'active' // what is selected, application-wide
17
+ | 'ui' // chrome state: collapsed, expanded, scrolled
18
+ | 'layout' // surfaces, mounts, the active shell
19
+ | 'modus' // the environment: size, capabilities, locale
20
+ | `plugins.${string}`
21
+ | (string & {});
22
+
23
+ export type SubscribeOptions = {
24
+ /** Fire for changes at or below the path, not only exact hits. */
25
+ subtree?: boolean;
26
+ /** Deliver the current value immediately on subscribe. */
27
+ immediate?: boolean;
28
+ };
29
+
30
+ export interface ChangeRecord {
31
+ path: BindingPath;
32
+ value: unknown;
33
+ previous: unknown;
34
+ }
35
+
36
+ export type ComputedDefinition<T = unknown> =
37
+ | { from: BindingPath[]; select: (values: Record<string, unknown>) => T }
38
+ /** A tiny expression over the `from` paths, for computed defined as data. */
39
+ | { from: BindingPath[]; select: string };
40
+
41
+ /**
42
+ * A namespace and the code that fills it. Lazy by default: nothing loads until
43
+ * something reads or subscribes below `namespace`.
44
+ */
45
+ export interface DataProviderDefinition {
46
+ namespace: string;
47
+ load?: 'lazy' | 'eager';
48
+ /** Unload this many ms after the last subscriber leaves. */
49
+ unloadAfter?: number;
50
+ provider: {
51
+ load(store: ReactiveStore): Promise<void> | void;
52
+ unload?(store: ReactiveStore): Promise<void> | void;
53
+ loadOne?(id: string): Promise<unknown>;
54
+ };
55
+ }
56
+
57
+ /** Optional shape checking. Dynamic paths stay legal either way. */
58
+ export interface PathSchema {
59
+ path: BindingPath;
60
+ /** Return null when valid, a message when not. */
61
+ validate(value: unknown): string | null;
62
+ /** Seeded when the path is first read and still empty. */
63
+ initial?: unknown;
64
+ }
65
+
66
+ /** Persist selected paths or subtrees across runs. */
67
+ export interface PersistenceAdapter {
68
+ id: string;
69
+ /** Paths and subtrees this adapter owns. Subtree if it ends in `/`. */
70
+ paths: string[];
71
+ read(): Promise<Record<string, unknown>> | Record<string, unknown>;
72
+ write(entries: Record<string, unknown>): Promise<void> | void;
73
+ /** Coalesce writes by this many ms. */
74
+ debounceMs?: number;
75
+ }
76
+
77
+ /** Collection helpers operate on a path holding an array. */
78
+ export interface CollectionOps<T = unknown> {
79
+ all(): T[];
80
+ at(index: number): T | undefined;
81
+ find(pred: (item: T, i: number) => boolean): T | undefined;
82
+ append(...items: T[]): void;
83
+ prepend(...items: T[]): void;
84
+ insertAt(index: number, ...items: T[]): void;
85
+ removeAt(index: number): void;
86
+ remove(pred: (item: T, i: number) => boolean): number;
87
+ update(pred: (item: T, i: number) => boolean, patch: Partial<T> | ((item: T) => T)): number;
88
+ replace(items: T[]): void;
89
+ filter(pred: (item: T, i: number) => boolean): T[];
90
+ clear(): void;
91
+ /** Keep at most `n` items, dropping from the front. For log tails. */
92
+ cap(n: number): void;
93
+ readonly length: number;
94
+ }
95
+
96
+ export interface ReactiveStore extends Disposable {
97
+ get<T = unknown>(path: BindingPath): T | undefined;
98
+ /** `get` with a fallback, so callers stop writing `?? default` everywhere. */
99
+ read<T>(path: BindingPath, fallback: T): T;
100
+ set(path: BindingPath, value: unknown): void;
101
+ /** Functional update - receives the current value. */
102
+ update<T = unknown>(path: BindingPath, fn: (current: T | undefined) => T): void;
103
+ /** Shallow-merge into an object at `path`. */
104
+ patch(path: BindingPath, partial: Record<string, unknown>): void;
105
+ /** Many writes, one notification pass. */
106
+ patchMany(entries: Record<BindingPath, unknown>): void;
107
+ delete(path: BindingPath): void;
108
+ has(path: BindingPath): boolean;
109
+
110
+ /** Coalesce every write inside `fn` into a single notification pass. */
111
+ batch<T>(fn: () => T): T;
112
+
113
+ subscribe(
114
+ path: BindingPath,
115
+ fn: (value: unknown, change: ChangeRecord) => void,
116
+ options?: SubscribeOptions,
117
+ ): Disposable;
118
+
119
+ clearScope(scope: ScopeName | string): void;
120
+ subscriberCount(scope: ScopeName | string): number;
121
+
122
+ computed<T = unknown>(path: BindingPath, def: ComputedDefinition<T>): Disposable;
123
+ collection<T = unknown>(path: BindingPath): CollectionOps<T>;
124
+
125
+ registerDataProvider(def: DataProviderDefinition): Disposable;
126
+ listDataProviders(): DataProviderDefinition[];
127
+
128
+ registerSchema(schema: PathSchema): Disposable;
129
+ registerPersistence(adapter: PersistenceAdapter): Disposable;
130
+ /** Load every registered persistence adapter. Call once at boot. */
131
+ hydrate(): Promise<void>;
132
+
133
+ /** Everything below `scope`, as a plain object. For snapshots and tests. */
134
+ snapshot(scope?: ScopeName | string): Record<string, unknown>;
135
+ restore(snapshot: Record<string, unknown>): void;
136
+ }
137
+
138
+ /**
139
+ * Transient events. Same path convention as the store, deliberately not the
140
+ * same mechanism - an event has no value to read back, which is the whole
141
+ * difference between `$/dialog/open` and `@/dialog/confirm`.
142
+ */
143
+ export interface EventBus extends Disposable {
144
+ emit(path: EventPath, payload?: unknown): void;
145
+ on(path: EventPath, fn: (payload: unknown, path: EventPath) => void, options?: { subtree?: boolean; once?: boolean }): Disposable;
146
+ /** Resolve on the next emit at this path. */
147
+ next(path: EventPath, timeoutMs?: number): Promise<unknown>;
148
+ }
@@ -0,0 +1,24 @@
1
+ import type { Disposable } from './disposable.js';
2
+
3
+ /**
4
+ * The smallest useful stream. A LogViewer should not care whether its lines
5
+ * come from a Node stream, an async iterable, a child process or a store path,
6
+ * so every source is adapted to this.
7
+ */
8
+ export interface Stream<T> {
9
+ subscribe(observer: StreamObserver<T>): Disposable;
10
+ /** Best-effort stop at the source. */
11
+ cancel?(): void;
12
+ }
13
+
14
+ export interface StreamObserver<T> {
15
+ next(value: T): void;
16
+ error?(err: unknown): void;
17
+ complete?(): void;
18
+ }
19
+
20
+ export type StreamSource<T> =
21
+ | Stream<T>
22
+ | AsyncIterable<T>
23
+ | Iterable<T>
24
+ | (() => AsyncIterable<T>);