@textui/widgets 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 (423) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +69 -0
  3. package/dist/chart/area-chart.d.ts +4 -0
  4. package/dist/chart/area-chart.d.ts.map +1 -0
  5. package/dist/chart/area-chart.js +3 -0
  6. package/dist/chart/bar-chart.d.ts +32 -0
  7. package/dist/chart/bar-chart.d.ts.map +1 -0
  8. package/dist/chart/bar-chart.js +32 -0
  9. package/dist/chart/gauge.d.ts +26 -0
  10. package/dist/chart/gauge.d.ts.map +1 -0
  11. package/dist/chart/gauge.js +26 -0
  12. package/dist/chart/heatmap.d.ts +13 -0
  13. package/dist/chart/heatmap.d.ts.map +1 -0
  14. package/dist/chart/heatmap.js +27 -0
  15. package/dist/chart/histogram.d.ts +12 -0
  16. package/dist/chart/histogram.d.ts.map +1 -0
  17. package/dist/chart/histogram.js +22 -0
  18. package/dist/chart/index.d.ts +20 -0
  19. package/dist/chart/index.d.ts.map +1 -0
  20. package/dist/chart/index.js +32 -0
  21. package/dist/chart/line-chart.d.ts +7 -0
  22. package/dist/chart/line-chart.d.ts.map +1 -0
  23. package/dist/chart/line-chart.js +75 -0
  24. package/dist/chart/shared.d.ts +34 -0
  25. package/dist/chart/shared.d.ts.map +1 -0
  26. package/dist/chart/shared.js +78 -0
  27. package/dist/chart/sparkline.d.ts +15 -0
  28. package/dist/chart/sparkline.d.ts.map +1 -0
  29. package/dist/chart/sparkline.js +22 -0
  30. package/dist/control/button.d.ts +27 -0
  31. package/dist/control/button.d.ts.map +1 -0
  32. package/dist/control/button.js +130 -0
  33. package/dist/control/checkbox.d.ts +10 -0
  34. package/dist/control/checkbox.d.ts.map +1 -0
  35. package/dist/control/checkbox.js +31 -0
  36. package/dist/control/index.d.ts +20 -0
  37. package/dist/control/index.d.ts.map +1 -0
  38. package/dist/control/index.js +36 -0
  39. package/dist/control/radio-group.d.ts +17 -0
  40. package/dist/control/radio-group.d.ts.map +1 -0
  41. package/dist/control/radio-group.js +32 -0
  42. package/dist/control/search-box.d.ts +7 -0
  43. package/dist/control/search-box.d.ts.map +1 -0
  44. package/dist/control/search-box.js +3 -0
  45. package/dist/control/select.d.ts +42 -0
  46. package/dist/control/select.d.ts.map +1 -0
  47. package/dist/control/select.js +166 -0
  48. package/dist/control/shared.d.ts +41 -0
  49. package/dist/control/shared.d.ts.map +1 -0
  50. package/dist/control/shared.js +1 -0
  51. package/dist/control/slider.d.ts +14 -0
  52. package/dist/control/slider.d.ts.map +1 -0
  53. package/dist/control/slider.js +45 -0
  54. package/dist/control/switch.d.ts +10 -0
  55. package/dist/control/switch.d.ts.map +1 -0
  56. package/dist/control/switch.js +32 -0
  57. package/dist/control/text-area.d.ts +87 -0
  58. package/dist/control/text-area.d.ts.map +1 -0
  59. package/dist/control/text-area.js +299 -0
  60. package/dist/control/text-input.d.ts +10 -0
  61. package/dist/control/text-input.d.ts.map +1 -0
  62. package/dist/control/text-input.js +129 -0
  63. package/dist/data/code-viewer.d.ts +71 -0
  64. package/dist/data/code-viewer.d.ts.map +1 -0
  65. package/dist/data/code-viewer.js +232 -0
  66. package/dist/data/feed.d.ts +43 -0
  67. package/dist/data/feed.d.ts.map +1 -0
  68. package/dist/data/feed.js +178 -0
  69. package/dist/data/index.d.ts +54 -0
  70. package/dist/data/index.d.ts.map +1 -0
  71. package/dist/data/index.js +187 -0
  72. package/dist/data/list.d.ts +31 -0
  73. package/dist/data/list.d.ts.map +1 -0
  74. package/dist/data/list.js +104 -0
  75. package/dist/data/log-viewer.d.ts +26 -0
  76. package/dist/data/log-viewer.d.ts.map +1 -0
  77. package/dist/data/log-viewer.js +69 -0
  78. package/dist/data/markdown-view.d.ts +31 -0
  79. package/dist/data/markdown-view.d.ts.map +1 -0
  80. package/dist/data/markdown-view.js +139 -0
  81. package/dist/data/pagination.d.ts +9 -0
  82. package/dist/data/pagination.d.ts.map +1 -0
  83. package/dist/data/pagination.js +18 -0
  84. package/dist/data/scroll-thumb.d.ts +15 -0
  85. package/dist/data/scroll-thumb.d.ts.map +1 -0
  86. package/dist/data/scroll-thumb.js +32 -0
  87. package/dist/data/shared.d.ts +12 -0
  88. package/dist/data/shared.d.ts.map +1 -0
  89. package/dist/data/shared.js +17 -0
  90. package/dist/data/tree.d.ts +35 -0
  91. package/dist/data/tree.d.ts.map +1 -0
  92. package/dist/data/tree.js +158 -0
  93. package/dist/decorations.d.ts +88 -0
  94. package/dist/decorations.d.ts.map +1 -0
  95. package/dist/decorations.js +135 -0
  96. package/dist/display/alert.d.ts +8 -0
  97. package/dist/display/alert.d.ts.map +1 -0
  98. package/dist/display/alert.js +12 -0
  99. package/dist/display/badge.d.ts +10 -0
  100. package/dist/display/badge.d.ts.map +1 -0
  101. package/dist/display/badge.js +21 -0
  102. package/dist/display/card.d.ts +8 -0
  103. package/dist/display/card.d.ts.map +1 -0
  104. package/dist/display/card.js +12 -0
  105. package/dist/display/empty-state.d.ts +10 -0
  106. package/dist/display/empty-state.d.ts.map +1 -0
  107. package/dist/display/empty-state.js +5 -0
  108. package/dist/display/error-state.d.ts +9 -0
  109. package/dist/display/error-state.d.ts.map +1 -0
  110. package/dist/display/error-state.js +7 -0
  111. package/dist/display/heading.d.ts +6 -0
  112. package/dist/display/heading.d.ts.map +1 -0
  113. package/dist/display/heading.js +8 -0
  114. package/dist/display/index.d.ts +25 -0
  115. package/dist/display/index.d.ts.map +1 -0
  116. package/dist/display/index.js +52 -0
  117. package/dist/display/key-value.d.ts +14 -0
  118. package/dist/display/key-value.d.ts.map +1 -0
  119. package/dist/display/key-value.js +11 -0
  120. package/dist/display/label.d.ts +6 -0
  121. package/dist/display/label.d.ts.map +1 -0
  122. package/dist/display/label.js +3 -0
  123. package/dist/display/marquee.d.ts +36 -0
  124. package/dist/display/marquee.d.ts.map +1 -0
  125. package/dist/display/marquee.js +55 -0
  126. package/dist/display/progress.d.ts +39 -0
  127. package/dist/display/progress.d.ts.map +1 -0
  128. package/dist/display/progress.js +46 -0
  129. package/dist/display/skeleton.d.ts +20 -0
  130. package/dist/display/skeleton.d.ts.map +1 -0
  131. package/dist/display/skeleton.js +20 -0
  132. package/dist/display/spinner.d.ts +7 -0
  133. package/dist/display/spinner.d.ts.map +1 -0
  134. package/dist/display/spinner.js +9 -0
  135. package/dist/display/status-dot.d.ts +8 -0
  136. package/dist/display/status-dot.d.ts.map +1 -0
  137. package/dist/display/status-dot.js +13 -0
  138. package/dist/display/timeline.d.ts +12 -0
  139. package/dist/display/timeline.d.ts.map +1 -0
  140. package/dist/display/timeline.js +22 -0
  141. package/dist/find.d.ts +74 -0
  142. package/dist/find.d.ts.map +1 -0
  143. package/dist/find.js +102 -0
  144. package/dist/form/danger-zone.d.ts +11 -0
  145. package/dist/form/danger-zone.d.ts.map +1 -0
  146. package/dist/form/danger-zone.js +15 -0
  147. package/dist/form/field.d.ts +19 -0
  148. package/dist/form/field.d.ts.map +1 -0
  149. package/dist/form/field.js +35 -0
  150. package/dist/form/form-actions.d.ts +11 -0
  151. package/dist/form/form-actions.d.ts.map +1 -0
  152. package/dist/form/form-actions.js +16 -0
  153. package/dist/form/form-section.d.ts +7 -0
  154. package/dist/form/form-section.d.ts.map +1 -0
  155. package/dist/form/form-section.js +5 -0
  156. package/dist/form/form.d.ts +7 -0
  157. package/dist/form/form.d.ts.map +1 -0
  158. package/dist/form/form.js +3 -0
  159. package/dist/form/index.d.ts +42 -0
  160. package/dist/form/index.d.ts.map +1 -0
  161. package/dist/form/index.js +140 -0
  162. package/dist/form/shared.d.ts +25 -0
  163. package/dist/form/shared.d.ts.map +1 -0
  164. package/dist/form/shared.js +9 -0
  165. package/dist/index.d.ts +26 -0
  166. package/dist/index.d.ts.map +1 -0
  167. package/dist/index.js +62 -0
  168. package/dist/layout/center.d.ts +7 -0
  169. package/dist/layout/center.d.ts.map +1 -0
  170. package/dist/layout/center.js +8 -0
  171. package/dist/layout/column.d.ts +3 -0
  172. package/dist/layout/column.d.ts.map +1 -0
  173. package/dist/layout/column.js +2 -0
  174. package/dist/layout/divider.d.ts +11 -0
  175. package/dist/layout/divider.d.ts.map +1 -0
  176. package/dist/layout/divider.js +13 -0
  177. package/dist/layout/grid.d.ts +10 -0
  178. package/dist/layout/grid.d.ts.map +1 -0
  179. package/dist/layout/grid.js +22 -0
  180. package/dist/layout/index.d.ts +19 -0
  181. package/dist/layout/index.d.ts.map +1 -0
  182. package/dist/layout/index.js +36 -0
  183. package/dist/layout/panel.d.ts +37 -0
  184. package/dist/layout/panel.d.ts.map +1 -0
  185. package/dist/layout/panel.js +71 -0
  186. package/dist/layout/row.d.ts +7 -0
  187. package/dist/layout/row.d.ts.map +1 -0
  188. package/dist/layout/row.js +2 -0
  189. package/dist/layout/scroll-view.d.ts +27 -0
  190. package/dist/layout/scroll-view.d.ts.map +1 -0
  191. package/dist/layout/scroll-view.js +93 -0
  192. package/dist/layout/splitter.d.ts +10 -0
  193. package/dist/layout/splitter.d.ts.map +1 -0
  194. package/dist/layout/splitter.js +9 -0
  195. package/dist/layout/stack.d.ts +7 -0
  196. package/dist/layout/stack.d.ts.map +1 -0
  197. package/dist/layout/stack.js +5 -0
  198. package/dist/navigation/breadcrumb.d.ts +14 -0
  199. package/dist/navigation/breadcrumb.d.ts.map +1 -0
  200. package/dist/navigation/breadcrumb.js +24 -0
  201. package/dist/navigation/index.d.ts +17 -0
  202. package/dist/navigation/index.d.ts.map +1 -0
  203. package/dist/navigation/index.js +30 -0
  204. package/dist/navigation/key-hints.d.ts +11 -0
  205. package/dist/navigation/key-hints.d.ts.map +1 -0
  206. package/dist/navigation/key-hints.js +13 -0
  207. package/dist/navigation/menu.d.ts +35 -0
  208. package/dist/navigation/menu.d.ts.map +1 -0
  209. package/dist/navigation/menu.js +88 -0
  210. package/dist/navigation/status-bar.d.ts +18 -0
  211. package/dist/navigation/status-bar.d.ts.map +1 -0
  212. package/dist/navigation/status-bar.js +12 -0
  213. package/dist/navigation/tabs.d.ts +20 -0
  214. package/dist/navigation/tabs.d.ts.map +1 -0
  215. package/dist/navigation/tabs.js +41 -0
  216. package/dist/navigation/toolbar.d.ts +14 -0
  217. package/dist/navigation/toolbar.d.ts.map +1 -0
  218. package/dist/navigation/toolbar.js +10 -0
  219. package/dist/navigation/wizard.d.ts +15 -0
  220. package/dist/navigation/wizard.d.ts.map +1 -0
  221. package/dist/navigation/wizard.js +17 -0
  222. package/dist/overlay/command-palette.d.ts +63 -0
  223. package/dist/overlay/command-palette.d.ts.map +1 -0
  224. package/dist/overlay/command-palette.js +415 -0
  225. package/dist/overlay/dialog.d.ts +16 -0
  226. package/dist/overlay/dialog.d.ts.map +1 -0
  227. package/dist/overlay/dialog.js +44 -0
  228. package/dist/overlay/index.d.ts +84 -0
  229. package/dist/overlay/index.d.ts.map +1 -0
  230. package/dist/overlay/index.js +157 -0
  231. package/dist/overlay/layer-scope.d.ts +21 -0
  232. package/dist/overlay/layer-scope.d.ts.map +1 -0
  233. package/dist/overlay/layer-scope.js +5 -0
  234. package/dist/overlay/path-picker.d.ts +14 -0
  235. package/dist/overlay/path-picker.d.ts.map +1 -0
  236. package/dist/overlay/path-picker.js +200 -0
  237. package/dist/overlay/prompt-dialog.d.ts +12 -0
  238. package/dist/overlay/prompt-dialog.d.ts.map +1 -0
  239. package/dist/overlay/prompt-dialog.js +29 -0
  240. package/dist/overlay/shared.d.ts +17 -0
  241. package/dist/overlay/shared.d.ts.map +1 -0
  242. package/dist/overlay/shared.js +17 -0
  243. package/dist/overlay/toast-host.d.ts +8 -0
  244. package/dist/overlay/toast-host.d.ts.map +1 -0
  245. package/dist/overlay/toast-host.js +19 -0
  246. package/dist/overlay/toast.d.ts +9 -0
  247. package/dist/overlay/toast.d.ts.map +1 -0
  248. package/dist/overlay/toast.js +20 -0
  249. package/dist/overlay/tooltip.d.ts +6 -0
  250. package/dist/overlay/tooltip.d.ts.map +1 -0
  251. package/dist/overlay/tooltip.js +11 -0
  252. package/dist/panel/index.d.ts +79 -0
  253. package/dist/panel/index.d.ts.map +1 -0
  254. package/dist/panel/index.js +256 -0
  255. package/dist/panel/resource-panel.d.ts +93 -0
  256. package/dist/panel/resource-panel.d.ts.map +1 -0
  257. package/dist/panel/resource-panel.js +213 -0
  258. package/dist/panel/shared.d.ts +6 -0
  259. package/dist/panel/shared.d.ts.map +1 -0
  260. package/dist/panel/shared.js +4 -0
  261. package/dist/shells/console-shell.d.ts +8 -0
  262. package/dist/shells/console-shell.d.ts.map +1 -0
  263. package/dist/shells/console-shell.js +30 -0
  264. package/dist/shells/index.d.ts +20 -0
  265. package/dist/shells/index.d.ts.map +1 -0
  266. package/dist/shells/index.js +61 -0
  267. package/dist/shells/paper-shell.d.ts +9 -0
  268. package/dist/shells/paper-shell.d.ts.map +1 -0
  269. package/dist/shells/paper-shell.js +23 -0
  270. package/dist/shells/plain-shell.d.ts +3 -0
  271. package/dist/shells/plain-shell.d.ts.map +1 -0
  272. package/dist/shells/plain-shell.js +11 -0
  273. package/dist/shells/shared.d.ts +12 -0
  274. package/dist/shells/shared.d.ts.map +1 -0
  275. package/dist/shells/shared.js +21 -0
  276. package/dist/shells/workbench-shell.d.ts +9 -0
  277. package/dist/shells/workbench-shell.d.ts.map +1 -0
  278. package/dist/shells/workbench-shell.js +43 -0
  279. package/dist/surface/bar-layout.d.ts +4 -0
  280. package/dist/surface/bar-layout.d.ts.map +1 -0
  281. package/dist/surface/bar-layout.js +7 -0
  282. package/dist/surface/index.d.ts +58 -0
  283. package/dist/surface/index.d.ts.map +1 -0
  284. package/dist/surface/index.js +48 -0
  285. package/dist/surface/inline-layout.d.ts +4 -0
  286. package/dist/surface/inline-layout.d.ts.map +1 -0
  287. package/dist/surface/inline-layout.js +7 -0
  288. package/dist/surface/mount-view.d.ts +7 -0
  289. package/dist/surface/mount-view.d.ts.map +1 -0
  290. package/dist/surface/mount-view.js +19 -0
  291. package/dist/surface/rail-layout.d.ts +4 -0
  292. package/dist/surface/rail-layout.d.ts.map +1 -0
  293. package/dist/surface/rail-layout.js +19 -0
  294. package/dist/surface/shared.d.ts +7 -0
  295. package/dist/surface/shared.d.ts.map +1 -0
  296. package/dist/surface/shared.js +1 -0
  297. package/dist/surface/single-layout.d.ts +4 -0
  298. package/dist/surface/single-layout.d.ts.map +1 -0
  299. package/dist/surface/single-layout.js +18 -0
  300. package/dist/surface/split-layout.d.ts +4 -0
  301. package/dist/surface/split-layout.d.ts.map +1 -0
  302. package/dist/surface/split-layout.js +7 -0
  303. package/dist/surface/stack-layout.d.ts +4 -0
  304. package/dist/surface/stack-layout.d.ts.map +1 -0
  305. package/dist/surface/stack-layout.js +22 -0
  306. package/dist/surface/surface-area.d.ts +16 -0
  307. package/dist/surface/surface-area.d.ts.map +1 -0
  308. package/dist/surface/surface-area.js +32 -0
  309. package/dist/surface/tabs-layout.d.ts +4 -0
  310. package/dist/surface/tabs-layout.d.ts.map +1 -0
  311. package/dist/surface/tabs-layout.js +24 -0
  312. package/dist/tone.d.ts +16 -0
  313. package/dist/tone.d.ts.map +1 -0
  314. package/dist/tone.js +45 -0
  315. package/dist/viewport.d.ts +30 -0
  316. package/dist/viewport.d.ts.map +1 -0
  317. package/dist/viewport.js +17 -0
  318. package/package.json +54 -0
  319. package/src/chart/area-chart.ts +9 -0
  320. package/src/chart/bar-chart.ts +74 -0
  321. package/src/chart/gauge.ts +60 -0
  322. package/src/chart/heatmap.ts +49 -0
  323. package/src/chart/histogram.ts +36 -0
  324. package/src/chart/index.ts +36 -0
  325. package/src/chart/line-chart.ts +100 -0
  326. package/src/chart/shared.ts +112 -0
  327. package/src/chart/sparkline.ts +46 -0
  328. package/src/control/button.ts +184 -0
  329. package/src/control/checkbox.ts +48 -0
  330. package/src/control/index.ts +40 -0
  331. package/src/control/radio-group.ts +60 -0
  332. package/src/control/search-box.ts +15 -0
  333. package/src/control/select.ts +230 -0
  334. package/src/control/shared.ts +41 -0
  335. package/src/control/slider.ts +63 -0
  336. package/src/control/switch.ts +39 -0
  337. package/src/control/text-area.ts +390 -0
  338. package/src/control/text-input.ts +150 -0
  339. package/src/data/code-viewer.ts +337 -0
  340. package/src/data/feed.ts +204 -0
  341. package/src/data/index.ts +243 -0
  342. package/src/data/list.ts +149 -0
  343. package/src/data/log-viewer.ts +105 -0
  344. package/src/data/markdown-view.ts +182 -0
  345. package/src/data/pagination.ts +31 -0
  346. package/src/data/scroll-thumb.ts +40 -0
  347. package/src/data/shared.ts +23 -0
  348. package/src/data/tree.ts +215 -0
  349. package/src/decorations.ts +176 -0
  350. package/src/display/alert.ts +28 -0
  351. package/src/display/badge.ts +40 -0
  352. package/src/display/card.ts +23 -0
  353. package/src/display/empty-state.ts +21 -0
  354. package/src/display/error-state.ts +23 -0
  355. package/src/display/heading.ts +17 -0
  356. package/src/display/index.ts +55 -0
  357. package/src/display/key-value.ts +32 -0
  358. package/src/display/label.ts +11 -0
  359. package/src/display/marquee.ts +88 -0
  360. package/src/display/progress.ts +92 -0
  361. package/src/display/skeleton.ts +34 -0
  362. package/src/display/spinner.ts +20 -0
  363. package/src/display/status-dot.ts +24 -0
  364. package/src/display/timeline.ts +47 -0
  365. package/src/find.ts +138 -0
  366. package/src/form/danger-zone.ts +34 -0
  367. package/src/form/field.ts +61 -0
  368. package/src/form/form-actions.ts +35 -0
  369. package/src/form/form-section.ts +16 -0
  370. package/src/form/form.ts +13 -0
  371. package/src/form/index.ts +191 -0
  372. package/src/form/shared.ts +36 -0
  373. package/src/index.ts +63 -0
  374. package/src/layout/center.ts +17 -0
  375. package/src/layout/column.ts +6 -0
  376. package/src/layout/divider.ts +31 -0
  377. package/src/layout/grid.ts +34 -0
  378. package/src/layout/index.ts +39 -0
  379. package/src/layout/panel.ts +114 -0
  380. package/src/layout/row.ts +11 -0
  381. package/src/layout/scroll-view.ts +115 -0
  382. package/src/layout/splitter.ts +24 -0
  383. package/src/layout/stack.ts +12 -0
  384. package/src/navigation/breadcrumb.ts +43 -0
  385. package/src/navigation/index.ts +33 -0
  386. package/src/navigation/key-hints.ts +25 -0
  387. package/src/navigation/menu.ts +134 -0
  388. package/src/navigation/status-bar.ts +43 -0
  389. package/src/navigation/tabs.ts +73 -0
  390. package/src/navigation/toolbar.ts +25 -0
  391. package/src/navigation/wizard.ts +43 -0
  392. package/src/overlay/command-palette.ts +485 -0
  393. package/src/overlay/dialog.ts +65 -0
  394. package/src/overlay/index.ts +232 -0
  395. package/src/overlay/layer-scope.ts +26 -0
  396. package/src/overlay/path-picker.ts +215 -0
  397. package/src/overlay/prompt-dialog.ts +45 -0
  398. package/src/overlay/shared.ts +19 -0
  399. package/src/overlay/toast-host.ts +30 -0
  400. package/src/overlay/toast.ts +35 -0
  401. package/src/overlay/tooltip.ts +17 -0
  402. package/src/panel/index.ts +317 -0
  403. package/src/panel/resource-panel.ts +304 -0
  404. package/src/panel/shared.ts +8 -0
  405. package/src/shells/console-shell.ts +47 -0
  406. package/src/shells/index.ts +66 -0
  407. package/src/shells/paper-shell.ts +31 -0
  408. package/src/shells/plain-shell.ts +19 -0
  409. package/src/shells/shared.ts +24 -0
  410. package/src/shells/workbench-shell.ts +62 -0
  411. package/src/surface/bar-layout.ts +11 -0
  412. package/src/surface/index.ts +53 -0
  413. package/src/surface/inline-layout.ts +11 -0
  414. package/src/surface/mount-view.ts +30 -0
  415. package/src/surface/rail-layout.ts +27 -0
  416. package/src/surface/shared.ts +7 -0
  417. package/src/surface/single-layout.ts +25 -0
  418. package/src/surface/split-layout.ts +11 -0
  419. package/src/surface/stack-layout.ts +32 -0
  420. package/src/surface/surface-area.ts +46 -0
  421. package/src/surface/tabs-layout.ts +30 -0
  422. package/src/tone.ts +48 -0
  423. package/src/viewport.ts +45 -0
@@ -0,0 +1,79 @@
1
+ import type { BindingPath, CommandDefinition, ReactiveStore, TextUIApp } from '@textui/core';
2
+ import type { PanelHandle, PanelView } from './resource-panel.js';
3
+ /**
4
+ * Panels.
5
+ *
6
+ * A resource is a file, a record, a log; a panel is a place one is being
7
+ * shown, and the renderer is a late-bound choice between the components
8
+ * registered for that kind. Those are three separate things, and every gain
9
+ * here comes from keeping them separate:
10
+ *
11
+ * - the same resource can be open in two panels at once, as source in one and
12
+ * rendered in the other, over one buffer - so an edit in either shows in
13
+ * both;
14
+ * - "open with" is one list from one registry, so a new renderer arriving
15
+ * from an extension is offered everywhere without anything being told;
16
+ * - and where a panel is looking - scroll, caret, which node was expanded -
17
+ * belongs to the panel rather than to whichever component happens to be
18
+ * drawing it, so every renderer remembers its place without implementing
19
+ * anything.
20
+ *
21
+ * That last one is what `usePanelState` is. A renderer asks for the state it
22
+ * wants, in whatever shape suits it, and gets back what this panel last had
23
+ * for this resource. The panel never knows what is in the record - a markdown
24
+ * view keeps a row, an editor keeps a caret and two offsets, a tree keeps a
25
+ * set of open nodes - which is precisely why it works for renderers that do
26
+ * not exist yet.
27
+ */
28
+ export * from './resource-panel.js';
29
+ export declare function panelStatusPath(id: string): BindingPath;
30
+ /** Ask a panel to take the keyboard as soon as it has something that can. */
31
+ export declare function claimPanel(store: ReactiveStore, id: string): void;
32
+ export interface PanelRenderer {
33
+ id: string;
34
+ title: string;
35
+ /** Whether what is on screen can write the resource back. */
36
+ saves: boolean;
37
+ }
38
+ export declare function panelRenderer(store: ReactiveStore, id: string): PanelRenderer | null;
39
+ export declare function activePanel(store: ReactiveStore): string | null;
40
+ export declare function panelUri(store: ReactiveStore, id: string): string | null;
41
+ export declare function panelView(store: ReactiveStore, id: string, uri: string): PanelView;
42
+ export declare function kindRenderer(store: ReactiveStore, kind: string): string | null;
43
+ export declare function setKindRenderer(store: ReactiveStore, kind: string, renderer: string): void;
44
+ /** Remember which renderer this panel uses for this resource. */
45
+ export declare function setPanelRenderer(store: ReactiveStore, id: string, uri: string, renderer: string | null): void;
46
+ /** The panel a renderer is drawing in, or null when it is mounted bare. */
47
+ export declare function usePanel(): PanelHandle | null;
48
+ /**
49
+ * State that belongs to this panel showing this resource.
50
+ *
51
+ * `useState`, except that it survives the renderer being swapped, the tab
52
+ * being switched away and back, and the component unmounting - and does not
53
+ * survive being carried to another panel, because where the other panel is
54
+ * looking is its own business.
55
+ *
56
+ * A renderer mounted outside a panel gets ordinary component state, so nothing
57
+ * has to check whether it is in one.
58
+ */
59
+ export declare function usePanelState<T extends Record<string, unknown>>(defaults: T): [T, (patch: Partial<T>) => void];
60
+ /**
61
+ * One line about what this panel is doing, for whoever draws a status bar.
62
+ *
63
+ * A renderer knows things nothing outside it can compute - how much is
64
+ * selected, which hunk is under the caret, how many matches are left - and a
65
+ * status bar wants them without knowing which renderer is mounted. Publishing
66
+ * it here means the bar reads one path and every renderer, present and future,
67
+ * can fill it.
68
+ */
69
+ export declare function usePanelStatus(text: string | null): void;
70
+ /**
71
+ * The panel commands every application gets.
72
+ *
73
+ * They are here rather than in each host because the list they act on comes
74
+ * from the registry, not from the host: an application that mounts a panel has
75
+ * already said everything it needs to say for "open with" to work. What a host
76
+ * still chooses is the keys.
77
+ */
78
+ export declare function panelCommands(app: TextUIApp): CommandDefinition[];
79
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/panel/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAEX,iBAAiB,EACjB,aAAa,EAEb,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAalE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,cAAc,qBAAqB,CAAC;AAEpC,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAEvD;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAEjE;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAEpF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAE/D;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAExE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAElF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE9E;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1F;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EACpB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,IAAI,CAMN;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,IAAI,WAAW,GAAG,IAAI,CAE7C;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7D,QAAQ,EAAE,CAAC,GACV,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CA0BlC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAUxD;AAgDD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,GAAG,iBAAiB,EAAE,CAsFjE"}
@@ -0,0 +1,256 @@
1
+ import { useContext, useEffect, useRuntime, useState, useStoreValue } from '@textui/core';
2
+ import { PANEL_PATH, defaultRenderer, kindRendererPath, panelClaimPath, panelPath, panelRendererPath, panelUriPath, panelViewPath, } from './resource-panel.js';
3
+ import { LOOSE, PanelContext } from './shared.js';
4
+ /**
5
+ * Panels.
6
+ *
7
+ * A resource is a file, a record, a log; a panel is a place one is being
8
+ * shown, and the renderer is a late-bound choice between the components
9
+ * registered for that kind. Those are three separate things, and every gain
10
+ * here comes from keeping them separate:
11
+ *
12
+ * - the same resource can be open in two panels at once, as source in one and
13
+ * rendered in the other, over one buffer - so an edit in either shows in
14
+ * both;
15
+ * - "open with" is one list from one registry, so a new renderer arriving
16
+ * from an extension is offered everywhere without anything being told;
17
+ * - and where a panel is looking - scroll, caret, which node was expanded -
18
+ * belongs to the panel rather than to whichever component happens to be
19
+ * drawing it, so every renderer remembers its place without implementing
20
+ * anything.
21
+ *
22
+ * That last one is what `usePanelState` is. A renderer asks for the state it
23
+ * wants, in whatever shape suits it, and gets back what this panel last had
24
+ * for this resource. The panel never knows what is in the record - a markdown
25
+ * view keeps a row, an editor keeps a caret and two offsets, a tree keeps a
26
+ * set of open nodes - which is precisely why it works for renderers that do
27
+ * not exist yet.
28
+ */
29
+ export * from './resource-panel.js';
30
+ export function panelStatusPath(id) {
31
+ return `${panelPath(id)}/status`;
32
+ }
33
+ /** Ask a panel to take the keyboard as soon as it has something that can. */
34
+ export function claimPanel(store, id) {
35
+ store.set(panelClaimPath(id), (store.get(panelClaimPath(id)) ?? 0) + 1);
36
+ }
37
+ export function panelRenderer(store, id) {
38
+ return store.get(panelRendererPath(id)) ?? null;
39
+ }
40
+ export function activePanel(store) {
41
+ return store.get(PANEL_PATH) ?? null;
42
+ }
43
+ export function panelUri(store, id) {
44
+ return store.get(panelUriPath(id)) ?? null;
45
+ }
46
+ export function panelView(store, id, uri) {
47
+ return store.get(panelViewPath(id, uri)) ?? {};
48
+ }
49
+ export function kindRenderer(store, kind) {
50
+ return store.get(kindRendererPath(kind)) ?? null;
51
+ }
52
+ export function setKindRenderer(store, kind, renderer) {
53
+ store.set(kindRendererPath(kind), renderer);
54
+ }
55
+ /** Remember which renderer this panel uses for this resource. */
56
+ export function setPanelRenderer(store, id, uri, renderer) {
57
+ const view = panelView(store, id, uri);
58
+ const next = { ...view };
59
+ if (renderer === null)
60
+ delete next.renderer;
61
+ else
62
+ next.renderer = renderer;
63
+ store.set(panelViewPath(id, uri), next);
64
+ }
65
+ /** The panel a renderer is drawing in, or null when it is mounted bare. */
66
+ export function usePanel() {
67
+ return useContext(PanelContext);
68
+ }
69
+ function same(a, b) {
70
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
71
+ for (const key of keys)
72
+ if (!Object.is(a[key], b[key]))
73
+ return false;
74
+ return true;
75
+ }
76
+ /**
77
+ * State that belongs to this panel showing this resource.
78
+ *
79
+ * `useState`, except that it survives the renderer being swapped, the tab
80
+ * being switched away and back, and the component unmounting - and does not
81
+ * survive being carried to another panel, because where the other panel is
82
+ * looking is its own business.
83
+ *
84
+ * A renderer mounted outside a panel gets ordinary component state, so nothing
85
+ * has to check whether it is in one.
86
+ */
87
+ export function usePanelState(defaults) {
88
+ const runtime = useRuntime();
89
+ const panel = usePanel();
90
+ const path = panel ? panelViewPath(panel.id, panel.uri) : LOOSE;
91
+ const stored = useStoreValue(path);
92
+ const [local, setLocal] = useState({});
93
+ const remembered = panel ? (stored?.state ?? {}) : local;
94
+ const value = { ...defaults, ...remembered };
95
+ const set = (patch) => {
96
+ if (panel) {
97
+ // Read the store rather than the render's snapshot: two sets in one
98
+ // frame - a caret move that also scrolls - would otherwise have the
99
+ // second overwrite the first with stale neighbours.
100
+ const view = runtime.store.get(path) ?? {};
101
+ const base = view.state ?? {};
102
+ const next = { ...base, ...patch };
103
+ if (same(base, next))
104
+ return;
105
+ runtime.store.set(path, { ...view, state: next });
106
+ return;
107
+ }
108
+ setLocal((prev) => (same(prev, { ...prev, ...patch }) ? prev : { ...prev, ...patch }));
109
+ };
110
+ return [value, set];
111
+ }
112
+ /**
113
+ * One line about what this panel is doing, for whoever draws a status bar.
114
+ *
115
+ * A renderer knows things nothing outside it can compute - how much is
116
+ * selected, which hunk is under the caret, how many matches are left - and a
117
+ * status bar wants them without knowing which renderer is mounted. Publishing
118
+ * it here means the bar reads one path and every renderer, present and future,
119
+ * can fill it.
120
+ */
121
+ export function usePanelStatus(text) {
122
+ const runtime = useRuntime();
123
+ const panel = usePanel();
124
+ const id = panel?.id ?? null;
125
+ useEffect(() => {
126
+ if (id === null)
127
+ return;
128
+ runtime.store.set(panelStatusPath(id), text);
129
+ return () => { runtime.store.set(panelStatusPath(id), null); };
130
+ }, [id, text]);
131
+ }
132
+ /**
133
+ * The renderers on offer for whatever the focused panel is showing.
134
+ *
135
+ * Titles are what a person picks from, so a title that two renderers share is
136
+ * disambiguated by the component behind it rather than being offered twice.
137
+ */
138
+ async function choicesFor(app, uri) {
139
+ if (uri === null)
140
+ return [];
141
+ const resource = await app.resources.stat(uri);
142
+ if (!resource)
143
+ return [];
144
+ const renderers = app.resources.renderersFor(resource.kind);
145
+ const counts = new Map();
146
+ for (const r of renderers)
147
+ counts.set(r.title, (counts.get(r.title) ?? 0) + 1);
148
+ return renderers.map((def) => ({
149
+ def,
150
+ kind: resource.kind,
151
+ label: (counts.get(def.title) ?? 0) > 1 ? `${def.title} (${def.component})` : def.title,
152
+ }));
153
+ }
154
+ function apply(app, id, uri, choice) {
155
+ if (id === null || uri === null || !choice)
156
+ return;
157
+ setPanelRenderer(app.store, id, uri, choice.def.id);
158
+ // And against the kind, because a person who opens one markdown as source is
159
+ // usually saying something about markdown. The file's own answer still wins
160
+ // wherever there is one.
161
+ setKindRenderer(app.store, choice.kind, choice.def.id);
162
+ // Asking for a different view of something is asking to look at it.
163
+ claimPanel(app.store, id);
164
+ }
165
+ /**
166
+ * The panel commands every application gets.
167
+ *
168
+ * They are here rather than in each host because the list they act on comes
169
+ * from the registry, not from the host: an application that mounts a panel has
170
+ * already said everything it needs to say for "open with" to work. What a host
171
+ * still chooses is the keys.
172
+ */
173
+ export function panelCommands(app) {
174
+ const current = (args, ctx) => args.panel ?? activePanel(ctx.store);
175
+ /*
176
+ * Which resource to act on.
177
+ *
178
+ * The panel publishes what it is showing from an effect, so a caller that
179
+ * opens a file and asks to edit it in the same tick would be acting on
180
+ * whatever the panel had a frame ago. A caller that knows the URI says so
181
+ * and the choice is remembered against it, ready for the panel that is about
182
+ * to draw it.
183
+ */
184
+ const targetUri = (args, id) => args.uri ?? (id === null ? null : panelUri(app.store, id));
185
+ return [
186
+ {
187
+ id: 'panel.openWith',
188
+ title: 'Open With…',
189
+ category: 'View',
190
+ slots: ['palette'],
191
+ when: PANEL_PATH,
192
+ args: [{
193
+ name: 'renderer',
194
+ type: 'string',
195
+ required: true,
196
+ description: 'How to show this resource',
197
+ choices: async () => {
198
+ const id = activePanel(app.store);
199
+ return (await choicesFor(app, id === null ? null : panelUri(app.store, id)))
200
+ .map((c) => c.label);
201
+ },
202
+ }],
203
+ run: async (args, ctx) => {
204
+ const id = current(args, ctx);
205
+ const uri = targetUri(args, id);
206
+ const label = String(args.renderer ?? '');
207
+ const choices = await choicesFor(app, uri);
208
+ apply(app, id, uri, choices.find((c) => c.label === label || c.def.id === label));
209
+ },
210
+ },
211
+ {
212
+ id: 'panel.nextRenderer',
213
+ title: 'Next View',
214
+ category: 'View',
215
+ slots: ['palette'],
216
+ when: PANEL_PATH,
217
+ run: async (args, ctx) => {
218
+ const id = current(args, ctx);
219
+ const uri = targetUri(args, id);
220
+ if (id === null || uri === null)
221
+ return;
222
+ const choices = await choicesFor(app, uri);
223
+ if (choices.length < 2)
224
+ return;
225
+ const at = panelView(app.store, id, uri).renderer
226
+ ?? defaultRenderer(choices.map((c) => c.def))?.id;
227
+ const index = choices.findIndex((c) => c.def.id === at);
228
+ apply(app, id, uri, choices[(index + 1) % choices.length]);
229
+ },
230
+ },
231
+ {
232
+ id: 'panel.toggleEdit',
233
+ title: 'Edit / View',
234
+ category: 'View',
235
+ slots: ['palette'],
236
+ when: PANEL_PATH,
237
+ run: async (args, ctx) => {
238
+ const id = current(args, ctx);
239
+ const uri = targetUri(args, id);
240
+ if (id === null || uri === null)
241
+ return;
242
+ const choices = await choicesFor(app, uri);
243
+ const defs = choices.map((c) => c.def);
244
+ const at = panelView(app.store, id, uri).renderer ?? defaultRenderer(defs)?.id;
245
+ const showing = defs.find((d) => d.id === at);
246
+ // Back to whatever this kind opens as, or forward to whatever writes
247
+ // it. Not a cycle: the footer says "edit / view", and a key that says
248
+ // one thing and does another is worse than a key that does less.
249
+ const next = showing?.saves === true
250
+ ? defaultRenderer(defs.filter((d) => d.saves !== true))
251
+ : defs.find((d) => d.saves === true);
252
+ apply(app, id, uri, choices.find((c) => c.def.id === next?.id));
253
+ },
254
+ },
255
+ ];
256
+ }
@@ -0,0 +1,93 @@
1
+ import type { BindingPath, BoxProps, ComponentDefinition, Resource, ResourceRendererDefinition } from '@textui/core';
2
+ /** Every panel's remembered state. */
3
+ export declare const PANELS_ROOT = "$/ui/panels";
4
+ /** Which panel the keyboard is in. */
5
+ export declare const PANEL_PATH: BindingPath;
6
+ export declare function panelPath(id: string): BindingPath;
7
+ /** What one panel remembers about one resource. */
8
+ export declare function panelViewPath(id: string, uri: string): BindingPath;
9
+ export declare function panelUriPath(id: string): BindingPath;
10
+ /** Which renderer a panel settled on, published for whoever draws chrome. */
11
+ export declare function panelRendererPath(id: string): BindingPath;
12
+ /**
13
+ * A pending "put the keyboard in here", counted rather than flagged.
14
+ *
15
+ * A command can ask before the panel showing that resource has even rendered -
16
+ * "open this file and edit it" is two calls in one tick - so the ask has to
17
+ * survive until there is something to focus. A counter, because asking twice
18
+ * in a row for the same panel is two asks.
19
+ */
20
+ export declare function panelClaimPath(id: string): BindingPath;
21
+ export interface PanelView {
22
+ /** Which renderer this panel uses for this resource. */
23
+ renderer?: string;
24
+ /** Opaque, and owned by whichever renderer is showing. */
25
+ state?: Record<string, unknown>;
26
+ }
27
+ /**
28
+ * Which renderer a *kind* opens with, once somebody has said.
29
+ *
30
+ * The per-resource memory answers "this file", and a person who opens one
31
+ * markdown as source is usually telling you something about markdown rather
32
+ * than about that file. So a choice is remembered twice: against the resource,
33
+ * where it wins, and against its kind, where it becomes the answer for the
34
+ * next file of that kind nobody has an opinion about yet.
35
+ */
36
+ export declare const RENDERERS_ROOT = "$/ui/renderers";
37
+ export declare function kindRendererPath(kind: string): BindingPath;
38
+ /**
39
+ * Which renderer a panel opens a resource with when it has no opinion.
40
+ *
41
+ * The highest-priority renderer that does not save. Opening a file is looking
42
+ * at it: an editor is something you ask for, and a kind whose only renderer
43
+ * writes back is the one case where asking is not required.
44
+ */
45
+ export declare function defaultRenderer(renderers: ResourceRendererDefinition[]): ResourceRendererDefinition | null;
46
+ export interface PanelHandle {
47
+ id: string;
48
+ uri: string;
49
+ /** The renderer currently drawing, which may not be the remembered one. */
50
+ renderer: string | null;
51
+ resource: Resource;
52
+ }
53
+ export interface ResourcePanelProps extends BoxProps {
54
+ /**
55
+ * This panel's identity, and the focus scope it makes.
56
+ *
57
+ * A panel with no id still works and still remembers - it takes the scope id
58
+ * the runtime generates - but that id dies with the mount, so it forgets
59
+ * when it is unmounted. Naming a panel is what makes it a place.
60
+ */
61
+ id?: string;
62
+ uri: string | null;
63
+ /** Force a renderer, ignoring what the panel remembers. */
64
+ renderer?: string;
65
+ /**
66
+ * What the caller wants, when it has no renderer in mind: `edit` asks for
67
+ * one that writes back. An intent, resolved against whatever is registered -
68
+ * not a second way of naming a component.
69
+ */
70
+ mode?: 'view' | 'edit';
71
+ /** Props for whichever renderer is chosen. The caller does not know which. */
72
+ rendererProps?: Record<string, unknown>;
73
+ /** Component to mount when nothing is registered for the kind. */
74
+ fallbackComponent?: string;
75
+ /** Claim the keyboard when nothing else holds it. */
76
+ autoFocus?: boolean;
77
+ emptyTitle?: string;
78
+ }
79
+ /**
80
+ * A place a resource is shown.
81
+ *
82
+ * `ResourcePanel` rather than `Panel` only because `Panel` is already a titled
83
+ * box in this catalog. The vocabulary everywhere else - `usePanelState`,
84
+ * `panelViewPath`, `$/ui/panels` - is the short word.
85
+ *
86
+ * It resolves the resource, picks a renderer, mounts it, and owns the focus
87
+ * scope and the remembered state around it. What it deliberately does not own
88
+ * is a tab strip, a title or a border: a panel is one slot, and how a host
89
+ * arranges its slots is the host's business.
90
+ */
91
+ export declare const ResourcePanel: (props: ResourcePanelProps) => import("@textui/core").RenderOutput;
92
+ export declare const PANEL_COMPONENTS: ComponentDefinition[];
93
+ //# sourceMappingURL=resource-panel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-panel.d.ts","sourceRoot":"","sources":["../../src/panel/resource-panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,mBAAmB,EAEnB,QAAQ,EACR,0BAA0B,EAC3B,MAAM,cAAc,CAAC;AAetB,sCAAsC;AACtC,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,sCAAsC;AACtC,eAAO,MAAM,UAAU,EAAmB,WAAW,CAAC;AAEtD,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAEjD;AAED,mDAAmD;AACnD,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,WAAW,CAElE;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAEpD;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAEzD;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAEtD;AAED,MAAM,WAAW,SAAS;IACxB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,mBAAmB,CAAC;AAE/C,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,0BAA0B,EAAE,GACtC,0BAA0B,GAAG,IAAI,CAEnC;AAID,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAID,MAAM,WAAW,kBAAmB,SAAQ,QAAQ;IAClD;;;;;;OAMG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,kEAAkE;IAClE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,oEAsJxB,CAAC;AAEH,eAAO,MAAM,gBAAgB,EAAE,mBAAmB,EAOjD,CAAC"}
@@ -0,0 +1,213 @@
1
+ import { defineComponent, escapeSegment, h, useEffect, useFocusScope, useRef, useRuntime, useStoreValue, useTask, } from '@textui/core';
2
+ import { EmptyState, ErrorState, Spinner } from '../display/index.js';
3
+ import { LOOSE, PanelContext } from './shared.js';
4
+ /** Every panel's remembered state. */
5
+ export const PANELS_ROOT = '$/ui/panels';
6
+ /** Which panel the keyboard is in. */
7
+ export const PANEL_PATH = '$/ui/panel';
8
+ export function panelPath(id) {
9
+ return `${PANELS_ROOT}/${escapeSegment(id)}`;
10
+ }
11
+ /** What one panel remembers about one resource. */
12
+ export function panelViewPath(id, uri) {
13
+ return `${panelPath(id)}/views/${escapeSegment(uri)}`;
14
+ }
15
+ export function panelUriPath(id) {
16
+ return `${panelPath(id)}/uri`;
17
+ }
18
+ /** Which renderer a panel settled on, published for whoever draws chrome. */
19
+ export function panelRendererPath(id) {
20
+ return `${panelPath(id)}/renderer`;
21
+ }
22
+ /**
23
+ * A pending "put the keyboard in here", counted rather than flagged.
24
+ *
25
+ * A command can ask before the panel showing that resource has even rendered -
26
+ * "open this file and edit it" is two calls in one tick - so the ask has to
27
+ * survive until there is something to focus. A counter, because asking twice
28
+ * in a row for the same panel is two asks.
29
+ */
30
+ export function panelClaimPath(id) {
31
+ return `${panelPath(id)}/claim`;
32
+ }
33
+ /**
34
+ * Which renderer a *kind* opens with, once somebody has said.
35
+ *
36
+ * The per-resource memory answers "this file", and a person who opens one
37
+ * markdown as source is usually telling you something about markdown rather
38
+ * than about that file. So a choice is remembered twice: against the resource,
39
+ * where it wins, and against its kind, where it becomes the answer for the
40
+ * next file of that kind nobody has an opinion about yet.
41
+ */
42
+ export const RENDERERS_ROOT = '$/ui/renderers';
43
+ export function kindRendererPath(kind) {
44
+ return `${RENDERERS_ROOT}/${escapeSegment(kind)}`;
45
+ }
46
+ /**
47
+ * Which renderer a panel opens a resource with when it has no opinion.
48
+ *
49
+ * The highest-priority renderer that does not save. Opening a file is looking
50
+ * at it: an editor is something you ask for, and a kind whose only renderer
51
+ * writes back is the one case where asking is not required.
52
+ */
53
+ export function defaultRenderer(renderers) {
54
+ return renderers.find((r) => r.saves !== true) ?? renderers[0] ?? null;
55
+ }
56
+ /**
57
+ * A place a resource is shown.
58
+ *
59
+ * `ResourcePanel` rather than `Panel` only because `Panel` is already a titled
60
+ * box in this catalog. The vocabulary everywhere else - `usePanelState`,
61
+ * `panelViewPath`, `$/ui/panels` - is the short word.
62
+ *
63
+ * It resolves the resource, picks a renderer, mounts it, and owns the focus
64
+ * scope and the remembered state around it. What it deliberately does not own
65
+ * is a tab strip, a title or a border: a panel is one slot, and how a host
66
+ * arranges its slots is the host's business.
67
+ */
68
+ export const ResourcePanel = defineComponent('ResourcePanel', (props) => {
69
+ const runtime = useRuntime();
70
+ const { id: named, uri, renderer, mode, rendererProps, fallbackComponent, autoFocus, emptyTitle, ...rest } = props;
71
+ const app = runtime.app();
72
+ const scope = useFocusScope({
73
+ ...(named !== undefined ? { id: named } : {}),
74
+ ...(autoFocus === true ? { autoFocus: true } : {}),
75
+ });
76
+ const id = scope;
77
+ const focused = useStoreValue('$/focus/scope', null);
78
+ const active = useStoreValue(PANEL_PATH, null);
79
+ // What this panel is showing, published for commands that act on it and for
80
+ // a host that would rather ask the store than thread props back up.
81
+ useEffect(() => {
82
+ runtime.store.set(panelUriPath(id), uri);
83
+ return () => {
84
+ // An unnamed panel's id was invented for this mount, so what it
85
+ // remembered can never be found again - it goes with it rather than
86
+ // accumulating in the store one screen at a time.
87
+ if (named === undefined)
88
+ runtime.store.set(panelPath(id), null);
89
+ else
90
+ runtime.store.set(panelUriPath(id), null);
91
+ };
92
+ }, [id, uri]);
93
+ // The panel the keyboard is in is wherever the keyboard actually is - and
94
+ // the first panel to exist, before anything has been focused at all, so a
95
+ // command run from the palette on a fresh screen still has a target.
96
+ useEffect(() => {
97
+ if (focused === scope || active == null)
98
+ runtime.store.set(PANEL_PATH, id);
99
+ }, [focused, scope, id, active]);
100
+ // And no panel at all once the last one goes: the panel commands exist only
101
+ // while there is somewhere for them to act, so an application with no panels
102
+ // does not carry "Open With" in its palette. Whichever panel is still
103
+ // mounted takes the mark back on the next frame.
104
+ useEffect(() => () => {
105
+ if (runtime.store.get(PANEL_PATH) === id)
106
+ runtime.store.set(PANEL_PATH, null);
107
+ }, [id]);
108
+ const task = useTask(async () => {
109
+ if (!uri || !app)
110
+ return null;
111
+ return app.resources.stat(uri);
112
+ }, [uri]);
113
+ useEffect(() => { void task.run(); }, [uri]);
114
+ // Subscribing here, not only in the renderer: picking "open with" writes the
115
+ // choice into this record, and the panel is what has to redraw.
116
+ const view = useStoreValue(uri ? panelViewPath(id, uri) : LOOSE);
117
+ const resourceKind = task.status === 'success' ? (task.data?.kind ?? null) : null;
118
+ // Subscribed, not just read: choosing how markdown opens in one panel is an
119
+ // answer every other panel showing markdown should pick up.
120
+ const forKind = useStoreValue(kindRendererPath(resourceKind ?? '\u0000'));
121
+ /*
122
+ * What the caller named, then what the caller asked for, then what this
123
+ * panel last used for this resource, then what the kind opens as. A caller
124
+ * that says nothing gets the remembered choice, which is what makes "open
125
+ * this json as a tree" stick.
126
+ */
127
+ const resource = task.status === 'success' ? task.data : null;
128
+ const renderers = resource && app ? app.resources.renderersFor(resource.kind) : [];
129
+ const asked = mode === 'edit'
130
+ ? renderers.find((r) => r.saves === true)
131
+ : mode === 'view'
132
+ ? defaultRenderer(renderers.filter((r) => r.saves !== true))
133
+ : undefined;
134
+ const chosen = (renderer !== undefined ? renderers.find((r) => r.id === renderer) : undefined) ??
135
+ asked ??
136
+ renderers.find((r) => r.id === view?.renderer) ??
137
+ renderers.find((r) => r.id === forKind) ??
138
+ defaultRenderer(renderers);
139
+ // Published rather than returned, because what is on screen is a fact a
140
+ // status bar, a menu and a key hint all want and none of them is this
141
+ // panel's parent. Resolved here, since the choice is made here.
142
+ useEffect(() => {
143
+ runtime.store.set(panelRendererPath(id), chosen ? { id: chosen.id, title: chosen.title, saves: chosen.saves === true } : null);
144
+ return () => { runtime.store.set(panelRendererPath(id), null); };
145
+ }, [id, chosen?.id ?? null]);
146
+ /*
147
+ * Swapping the renderer puts the keyboard in the panel.
148
+ *
149
+ * Asking for a different view of a thing is asking to look at it, and the
150
+ * swap unmounts whatever had focus - so without this the new renderer draws
151
+ * perfectly and reads no keys, which looks like a broken viewer rather than
152
+ * an unfocused one. It is handed to the renderer as `autoFocus` rather than
153
+ * pushed from here afterwards, because the mounting render is the first
154
+ * moment the thing that should have focus exists.
155
+ *
156
+ * Only on a *change*: a panel appearing on screen is not a reason to take
157
+ * the keyboard off whatever the person was already using.
158
+ *
159
+ * And only for the *same resource*. This used to compare renderer ids and
160
+ * nothing else, so walking a tree from a markdown file to a JSON one was a
161
+ * swap - the renderer changed because the file did - and the keyboard left
162
+ * the tree in the middle of browsing it. A different view of one thing is a
163
+ * request; a different thing is just the next thing.
164
+ */
165
+ const previous = useRef(null);
166
+ const before = previous.current;
167
+ const swapped = before !== null
168
+ && before.uri === uri
169
+ && before.renderer !== (chosen?.id ?? null);
170
+ if (chosen)
171
+ previous.current = { uri, renderer: chosen.id };
172
+ // Or because a command asked outright, which is the same thing arriving a
173
+ // frame earlier: "open this and edit it" writes the choice before this panel
174
+ // has drawn once, so there is no swap to notice.
175
+ const claim = useStoreValue(panelClaimPath(id), 0) ?? 0;
176
+ useEffect(() => {
177
+ if (claim > 0 && chosen)
178
+ runtime.store.set(panelClaimPath(id), 0);
179
+ }, [id, claim, chosen?.id ?? null]);
180
+ if (!uri)
181
+ return h(EmptyState, { title: emptyTitle ?? 'Nothing selected', ...rest });
182
+ if (!app)
183
+ return null;
184
+ if (task.status === 'running' || task.status === 'idle') {
185
+ return h(Spinner, { label: 'Loading…', ...rest });
186
+ }
187
+ if (task.status === 'error')
188
+ return h(ErrorState, { error: task.error, ...rest });
189
+ if (!resource)
190
+ return h(EmptyState, { title: 'Not found', message: uri, ...rest });
191
+ if (!chosen) {
192
+ return h('box', { direction: 'column', flex: 1, id: scope, ...rest }, fallbackComponent
193
+ ? { component: fallbackComponent, resource, uri }
194
+ : h(EmptyState, { title: 'No renderer', message: resource.kind }));
195
+ }
196
+ const node = {
197
+ component: chosen.component,
198
+ resource,
199
+ uri,
200
+ ...(swapped || claim > 0 ? { autoFocus: true } : {}),
201
+ ...(rendererProps ?? {}),
202
+ };
203
+ const handle = { id, uri, renderer: chosen.id, resource };
204
+ return h('box', { direction: 'column', flex: 1, id: scope, ...rest }, h(PanelContext.Provider, { value: handle }, node));
205
+ });
206
+ export const PANEL_COMPONENTS = [
207
+ {
208
+ component: 'ResourcePanel',
209
+ category: 'resource',
210
+ renderer: { kind: 'function', render: ResourcePanel },
211
+ description: 'A place a resource is shown, by whichever renderer is registered for it.',
212
+ },
213
+ ];
@@ -0,0 +1,6 @@
1
+ import type { BindingPath } from '@textui/core';
2
+ import type { PanelHandle } from './resource-panel.js';
3
+ export declare const PanelContext: import("@textui/core").Context<PanelHandle | null>;
4
+ /** Where a renderer with no panel keeps state, so the hook is always safe. */
5
+ export declare const LOOSE: BindingPath;
6
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/panel/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,eAAO,MAAM,YAAY,oDAAmD,CAAC;AAE7E,8EAA8E;AAC9E,eAAO,MAAM,KAAK,EAA4B,WAAW,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { createContext } from '@textui/core';
2
+ export const PanelContext = createContext('Panel', null);
3
+ /** Where a renderer with no panel keeps state, so the hook is always safe. */
4
+ export const LOOSE = '$/local/panel/loose';