@interactive-inc/claude-funnel 0.8.1 → 0.10.1

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 (354) hide show
  1. package/README.md +179 -80
  2. package/dist/bin.js +726 -658
  3. package/dist/connector-adapter-CXB-q_XC.d.ts +11 -0
  4. package/dist/connector-adapter-D5Utumgz.js +4 -0
  5. package/dist/connectors/discord.d.ts +76 -0
  6. package/dist/connectors/discord.js +2 -0
  7. package/dist/connectors/gh.d.ts +38 -0
  8. package/dist/connectors/gh.js +2 -0
  9. package/dist/connectors/schedule.d.ts +53 -0
  10. package/dist/connectors/schedule.js +2 -0
  11. package/dist/connectors/slack.d.ts +34 -0
  12. package/dist/connectors/slack.js +2 -0
  13. package/dist/discord-connector-schema-Dww2I4zH.d.ts +14 -0
  14. package/dist/discord-connector-schema-ygf5Df-2.js +173 -0
  15. package/dist/file-system-Co60LrmR.d.ts +74 -0
  16. package/dist/gateway/daemon.js +233 -183
  17. package/dist/gh-connector-schema-2ml29MBC.js +218 -0
  18. package/dist/gh-connector-schema-BZFAS-p-.d.ts +45 -0
  19. package/dist/index.d.ts +3881 -36
  20. package/dist/index.js +6217 -3483
  21. package/dist/logger-CTlXs7z4.d.ts +33 -0
  22. package/dist/node-logger-DQz_BGOD.js +61 -0
  23. package/dist/schedule-connector-schema-CkuIQ0JQ.js +325 -0
  24. package/dist/slack-connector-schema-Cd22WiHB.js +153 -0
  25. package/dist/slack-event-processor-CS-bAit9.d.ts +43 -0
  26. package/package.json +34 -28
  27. package/dist/cli/factory.d.ts +0 -7
  28. package/dist/cli/router/query-to-cli-args.d.ts +0 -1
  29. package/dist/cli/router/to-request.d.ts +0 -5
  30. package/dist/cli/router/validator.d.ts +0 -5
  31. package/dist/cli/routes/channels.$channel.connectors.$connector.d.ts +0 -42
  32. package/dist/cli/routes/channels.$channel.connectors.$connector.rename.$newName.d.ts +0 -46
  33. package/dist/cli/routes/channels.$channel.connectors.$connector.request.d.ts +0 -54
  34. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.d.ts +0 -66
  35. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.d.ts +0 -42
  36. package/dist/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.d.ts +0 -46
  37. package/dist/cli/routes/channels.$channel.connectors.add.$connector.d.ts +0 -90
  38. package/dist/cli/routes/channels.$channel.connectors.d.ts +0 -38
  39. package/dist/cli/routes/channels.$channel.connectors.remove.$connector.d.ts +0 -42
  40. package/dist/cli/routes/channels.$channel.connectors.set.$connector.d.ts +0 -62
  41. package/dist/cli/routes/channels.$channel.d.ts +0 -38
  42. package/dist/cli/routes/channels.$channel.rename.$newName.d.ts +0 -42
  43. package/dist/cli/routes/channels.$channel.set.delivery.$mode.d.ts +0 -28
  44. package/dist/cli/routes/channels.add.$channel.d.ts +0 -46
  45. package/dist/cli/routes/channels.d.ts +0 -16
  46. package/dist/cli/routes/channels.remove.$channel.d.ts +0 -38
  47. package/dist/cli/routes/claude.d.ts +0 -32
  48. package/dist/cli/routes/gateway.d.ts +0 -20
  49. package/dist/cli/routes/gateway.listeners.d.ts +0 -17
  50. package/dist/cli/routes/gateway.logs.d.ts +0 -24
  51. package/dist/cli/routes/gateway.restart.d.ts +0 -24
  52. package/dist/cli/routes/gateway.run.d.ts +0 -24
  53. package/dist/cli/routes/gateway.start.d.ts +0 -24
  54. package/dist/cli/routes/gateway.status.d.ts +0 -13
  55. package/dist/cli/routes/gateway.stop.d.ts +0 -16
  56. package/dist/cli/routes/index.d.ts +0 -1222
  57. package/dist/cli/routes/profiles.$profile.as-default.d.ts +0 -38
  58. package/dist/cli/routes/profiles.$profile.rename.$newName.d.ts +0 -42
  59. package/dist/cli/routes/profiles.$profile.run.d.ts +0 -46
  60. package/dist/cli/routes/profiles.add.$profile.d.ts +0 -54
  61. package/dist/cli/routes/profiles.d.ts +0 -16
  62. package/dist/cli/routes/profiles.remove.$profile.d.ts +0 -38
  63. package/dist/cli/routes/profiles.set.$profile.d.ts +0 -54
  64. package/dist/cli/routes/status.d.ts +0 -16
  65. package/dist/cli/routes/update.d.ts +0 -16
  66. package/dist/connectors/connector-adapter.d.ts +0 -8
  67. package/dist/connectors/connector-config-schema.d.ts +0 -43
  68. package/dist/connectors/connector-factory.d.ts +0 -32
  69. package/dist/connectors/connector-listener.d.ts +0 -17
  70. package/dist/connectors/discord-adapter.d.ts +0 -14
  71. package/dist/connectors/discord-connector-schema.d.ts +0 -10
  72. package/dist/connectors/discord-event-processor.d.ts +0 -26
  73. package/dist/connectors/discord-listener.d.ts +0 -17
  74. package/dist/connectors/gh-adapter.d.ts +0 -11
  75. package/dist/connectors/gh-connector-schema.d.ts +0 -10
  76. package/dist/connectors/gh-listener.d.ts +0 -26
  77. package/dist/connectors/match-cron.d.ts +0 -1
  78. package/dist/connectors/schedule-connector-schema.d.ts +0 -45
  79. package/dist/connectors/schedule-listener.d.ts +0 -30
  80. package/dist/connectors/schedule-state-store.d.ts +0 -19
  81. package/dist/connectors/slack-adapter.d.ts +0 -15
  82. package/dist/connectors/slack-connector-schema.d.ts +0 -11
  83. package/dist/connectors/slack-event-processor.d.ts +0 -27
  84. package/dist/connectors/slack-listener.d.ts +0 -17
  85. package/dist/engine/channels/channels.d.ts +0 -106
  86. package/dist/engine/claude/claude.d.ts +0 -49
  87. package/dist/engine/claude/gateway-controller.d.ts +0 -6
  88. package/dist/engine/fs/file-system.d.ts +0 -24
  89. package/dist/engine/fs/memory-file-system.d.ts +0 -31
  90. package/dist/engine/fs/node-file-system.d.ts +0 -15
  91. package/dist/engine/http/http-client.d.ts +0 -15
  92. package/dist/engine/http/memory-http-client.d.ts +0 -12
  93. package/dist/engine/http/node-http-client.d.ts +0 -5
  94. package/dist/engine/id/id-generator.d.ts +0 -7
  95. package/dist/engine/id/memory-id-generator.d.ts +0 -11
  96. package/dist/engine/id/node-id-generator.d.ts +0 -4
  97. package/dist/engine/logger/logger.d.ts +0 -11
  98. package/dist/engine/logger/memory-logger.d.ts +0 -14
  99. package/dist/engine/logger/node-logger.d.ts +0 -15
  100. package/dist/engine/logger/noop-logger.d.ts +0 -7
  101. package/dist/engine/mcp/channel-server.d.ts +0 -1
  102. package/dist/engine/mcp/mcp.d.ts +0 -22
  103. package/dist/engine/process/memory-process-runner.d.ts +0 -43
  104. package/dist/engine/process/node-process-runner.d.ts +0 -9
  105. package/dist/engine/process/process-runner.d.ts +0 -29
  106. package/dist/engine/profiles/profile-channel-checker.d.ts +0 -7
  107. package/dist/engine/profiles/profiles.d.ts +0 -31
  108. package/dist/engine/settings/mock-settings-reader.d.ts +0 -9
  109. package/dist/engine/settings/settings-reader.d.ts +0 -5
  110. package/dist/engine/settings/settings-schema.d.ts +0 -132
  111. package/dist/engine/settings/settings-store.d.ts +0 -18
  112. package/dist/engine/time/clock.d.ts +0 -9
  113. package/dist/engine/time/memory-clock.d.ts +0 -12
  114. package/dist/engine/time/node-clock.d.ts +0 -4
  115. package/dist/funnel.d.ts +0 -95
  116. package/dist/gateway/auth-middleware.d.ts +0 -14
  117. package/dist/gateway/broadcaster.d.ts +0 -122
  118. package/dist/gateway/daemon.d.ts +0 -2
  119. package/dist/gateway/factory.d.ts +0 -7
  120. package/dist/gateway/funnel-event-store.d.ts +0 -81
  121. package/dist/gateway/gateway-server.d.ts +0 -94
  122. package/dist/gateway/gateway-token.d.ts +0 -33
  123. package/dist/gateway/gateway.d.ts +0 -58
  124. package/dist/gateway/kill-competing-slack-gateways.d.ts +0 -9
  125. package/dist/gateway/listener-supervisor.d.ts +0 -85
  126. package/dist/gateway/listeners-client.d.ts +0 -53
  127. package/dist/gateway/resolve-daemon-script.d.ts +0 -11
  128. package/dist/gateway/routes/channels.connectors.call.d.ts +0 -41
  129. package/dist/gateway/routes/health.d.ts +0 -17
  130. package/dist/gateway/routes/index.d.ts +0 -209
  131. package/dist/gateway/routes/listeners.list.d.ts +0 -14
  132. package/dist/gateway/routes/listeners.restart.d.ts +0 -34
  133. package/dist/gateway/routes/listeners.start.d.ts +0 -34
  134. package/dist/gateway/routes/listeners.stop.d.ts +0 -34
  135. package/dist/gateway/routes/route-deps.d.ts +0 -10
  136. package/dist/gateway/routes/status.d.ts +0 -30
  137. package/dist/gateway/routes/validator.d.ts +0 -19
  138. package/dist/logger/leuco-human-file-writer.d.ts +0 -33
  139. package/dist/logger/leuco-human-logger.d.ts +0 -46
  140. package/dist/logger/leuco-human-record.d.ts +0 -15
  141. package/dist/logger/leuco-human-stdout-writer.d.ts +0 -20
  142. package/dist/logger/leuco-human-writer.d.ts +0 -13
  143. package/dist/logger/leuco-logger-memory-sink.d.ts +0 -33
  144. package/dist/logger/leuco-logger-record.d.ts +0 -13
  145. package/dist/logger/leuco-logger-sink.d.ts +0 -34
  146. package/dist/logger/leuco-logger-sqlite-sink.d.ts +0 -102
  147. package/dist/logger/leuco-logger.d.ts +0 -56
  148. package/lib/bin.ts +0 -78
  149. package/lib/cli/factory.ts +0 -10
  150. package/lib/cli/router/query-to-cli-args.ts +0 -20
  151. package/lib/cli/router/to-request.ts +0 -112
  152. package/lib/cli/router/validator.ts +0 -27
  153. package/lib/cli/routes/channels.$channel.connectors.$connector.rename.$newName.ts +0 -27
  154. package/lib/cli/routes/channels.$channel.connectors.$connector.request.ts +0 -40
  155. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.add.$id.ts +0 -41
  156. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.remove.$id.ts +0 -22
  157. package/lib/cli/routes/channels.$channel.connectors.$connector.schedules.ts +0 -23
  158. package/lib/cli/routes/channels.$channel.connectors.$connector.ts +0 -26
  159. package/lib/cli/routes/channels.$channel.connectors.add.$connector.ts +0 -92
  160. package/lib/cli/routes/channels.$channel.connectors.remove.$connector.ts +0 -22
  161. package/lib/cli/routes/channels.$channel.connectors.set.$connector.ts +0 -63
  162. package/lib/cli/routes/channels.$channel.connectors.ts +0 -26
  163. package/lib/cli/routes/channels.$channel.rename.$newName.ts +0 -22
  164. package/lib/cli/routes/channels.$channel.set.delivery.$mode.ts +0 -34
  165. package/lib/cli/routes/channels.$channel.ts +0 -34
  166. package/lib/cli/routes/channels.add.$channel.ts +0 -33
  167. package/lib/cli/routes/channels.remove.$channel.ts +0 -20
  168. package/lib/cli/routes/channels.ts +0 -39
  169. package/lib/cli/routes/claude.ts +0 -69
  170. package/lib/cli/routes/gateway.listeners.ts +0 -41
  171. package/lib/cli/routes/gateway.logs.ts +0 -123
  172. package/lib/cli/routes/gateway.restart.ts +0 -50
  173. package/lib/cli/routes/gateway.run.ts +0 -41
  174. package/lib/cli/routes/gateway.start.ts +0 -50
  175. package/lib/cli/routes/gateway.status.ts +0 -19
  176. package/lib/cli/routes/gateway.stop.ts +0 -32
  177. package/lib/cli/routes/gateway.ts +0 -55
  178. package/lib/cli/routes/index.ts +0 -202
  179. package/lib/cli/routes/profiles.$profile.as-default.ts +0 -22
  180. package/lib/cli/routes/profiles.$profile.rename.$newName.ts +0 -22
  181. package/lib/cli/routes/profiles.$profile.run.ts +0 -36
  182. package/lib/cli/routes/profiles.add.$profile.ts +0 -46
  183. package/lib/cli/routes/profiles.remove.$profile.ts +0 -20
  184. package/lib/cli/routes/profiles.set.$profile.ts +0 -46
  185. package/lib/cli/routes/profiles.ts +0 -40
  186. package/lib/cli/routes/status.ts +0 -93
  187. package/lib/cli/routes/update.ts +0 -27
  188. package/lib/connectors/connector-adapter.ts +0 -9
  189. package/lib/connectors/connector-config-schema.ts +0 -16
  190. package/lib/connectors/connector-factory.ts +0 -94
  191. package/lib/connectors/connector-listener.ts +0 -20
  192. package/lib/connectors/discord-adapter.ts +0 -51
  193. package/lib/connectors/discord-connector-schema.ts +0 -12
  194. package/lib/connectors/discord-event-processor.ts +0 -48
  195. package/lib/connectors/discord-listener.ts +0 -111
  196. package/lib/connectors/gh-adapter.ts +0 -48
  197. package/lib/connectors/gh-connector-schema.ts +0 -12
  198. package/lib/connectors/gh-listener.ts +0 -137
  199. package/lib/connectors/match-cron.ts +0 -78
  200. package/lib/connectors/schedule-connector-schema.ts +0 -33
  201. package/lib/connectors/schedule-listener.ts +0 -207
  202. package/lib/connectors/schedule-state-store.ts +0 -54
  203. package/lib/connectors/slack-adapter.ts +0 -36
  204. package/lib/connectors/slack-connector-schema.ts +0 -13
  205. package/lib/connectors/slack-event-processor.ts +0 -97
  206. package/lib/connectors/slack-listener.ts +0 -97
  207. package/lib/engine/channels/channels.ts +0 -520
  208. package/lib/engine/claude/claude.ts +0 -199
  209. package/lib/engine/claude/gateway-controller.ts +0 -4
  210. package/lib/engine/fs/file-system.ts +0 -23
  211. package/lib/engine/fs/memory-file-system.ts +0 -102
  212. package/lib/engine/fs/node-file-system.ts +0 -68
  213. package/lib/engine/http/http-client.ts +0 -17
  214. package/lib/engine/http/memory-http-client.ts +0 -36
  215. package/lib/engine/http/node-http-client.ts +0 -23
  216. package/lib/engine/id/id-generator.ts +0 -7
  217. package/lib/engine/id/memory-id-generator.ts +0 -20
  218. package/lib/engine/id/node-id-generator.ts +0 -7
  219. package/lib/engine/logger/logger.ts +0 -11
  220. package/lib/engine/logger/memory-logger.ts +0 -28
  221. package/lib/engine/logger/node-logger.ts +0 -49
  222. package/lib/engine/logger/noop-logger.ts +0 -9
  223. package/lib/engine/mcp/channel-server.ts +0 -204
  224. package/lib/engine/mcp/mcp.ts +0 -126
  225. package/lib/engine/process/memory-process-runner.ts +0 -88
  226. package/lib/engine/process/node-process-runner.ts +0 -91
  227. package/lib/engine/process/process-runner.ts +0 -33
  228. package/lib/engine/profiles/profile-channel-checker.ts +0 -7
  229. package/lib/engine/profiles/profiles.ts +0 -126
  230. package/lib/engine/settings/mock-settings-reader.ts +0 -27
  231. package/lib/engine/settings/settings-reader.ts +0 -6
  232. package/lib/engine/settings/settings-schema.ts +0 -46
  233. package/lib/engine/settings/settings-store.ts +0 -110
  234. package/lib/engine/time/clock.ts +0 -15
  235. package/lib/engine/time/memory-clock.ts +0 -26
  236. package/lib/engine/time/node-clock.ts +0 -7
  237. package/lib/funnel.ts +0 -187
  238. package/lib/gateway/auth-middleware.ts +0 -44
  239. package/lib/gateway/broadcaster.ts +0 -319
  240. package/lib/gateway/daemon.ts +0 -47
  241. package/lib/gateway/factory.ts +0 -10
  242. package/lib/gateway/funnel-event-store.ts +0 -155
  243. package/lib/gateway/gateway-server.ts +0 -414
  244. package/lib/gateway/gateway-token.ts +0 -79
  245. package/lib/gateway/gateway.ts +0 -209
  246. package/lib/gateway/kill-competing-slack-gateways.ts +0 -56
  247. package/lib/gateway/listener-supervisor.ts +0 -339
  248. package/lib/gateway/listeners-client.ts +0 -128
  249. package/lib/gateway/resolve-daemon-script.ts +0 -26
  250. package/lib/gateway/routes/channels.connectors.call.ts +0 -39
  251. package/lib/gateway/routes/health.ts +0 -13
  252. package/lib/gateway/routes/index.ts +0 -24
  253. package/lib/gateway/routes/listeners.list.ts +0 -6
  254. package/lib/gateway/routes/listeners.restart.ts +0 -15
  255. package/lib/gateway/routes/listeners.start.ts +0 -15
  256. package/lib/gateway/routes/listeners.stop.ts +0 -15
  257. package/lib/gateway/routes/route-deps.ts +0 -11
  258. package/lib/gateway/routes/status.ts +0 -15
  259. package/lib/gateway/routes/validator.ts +0 -17
  260. package/lib/index.ts +0 -52
  261. package/lib/logger/leuco-human-file-writer.ts +0 -65
  262. package/lib/logger/leuco-human-logger.ts +0 -98
  263. package/lib/logger/leuco-human-record.ts +0 -16
  264. package/lib/logger/leuco-human-stdout-writer.ts +0 -26
  265. package/lib/logger/leuco-human-writer.ts +0 -14
  266. package/lib/logger/leuco-logger-memory-sink.ts +0 -67
  267. package/lib/logger/leuco-logger-record.ts +0 -13
  268. package/lib/logger/leuco-logger-sink.ts +0 -33
  269. package/lib/logger/leuco-logger-sqlite-sink.ts +0 -355
  270. package/lib/logger/leuco-logger.ts +0 -135
  271. package/lib/tui/app.tsx +0 -357
  272. package/lib/tui/components/add-row.tsx +0 -18
  273. package/lib/tui/components/brand.tsx +0 -27
  274. package/lib/tui/components/card.tsx +0 -44
  275. package/lib/tui/components/detail-bar.tsx +0 -46
  276. package/lib/tui/components/editable-field.tsx +0 -33
  277. package/lib/tui/components/empty-state.tsx +0 -11
  278. package/lib/tui/components/gateway-status.tsx +0 -66
  279. package/lib/tui/components/keymap.tsx +0 -29
  280. package/lib/tui/components/menu-item.tsx +0 -73
  281. package/lib/tui/components/menu.tsx +0 -26
  282. package/lib/tui/components/panel-header.tsx +0 -22
  283. package/lib/tui/components/readonly-field.tsx +0 -18
  284. package/lib/tui/components/section-header.tsx +0 -25
  285. package/lib/tui/components/selection-accent.tsx +0 -32
  286. package/lib/tui/components/session-item.tsx +0 -33
  287. package/lib/tui/components/session-list.tsx +0 -33
  288. package/lib/tui/components/ui/hascii/accordion-item.tsx +0 -88
  289. package/lib/tui/components/ui/hascii/accordion.tsx +0 -96
  290. package/lib/tui/components/ui/hascii/alert-dialog.tsx +0 -43
  291. package/lib/tui/components/ui/hascii/badge.tsx +0 -51
  292. package/lib/tui/components/ui/hascii/breadcrumb.tsx +0 -58
  293. package/lib/tui/components/ui/hascii/button.tsx +0 -194
  294. package/lib/tui/components/ui/hascii/card-content.tsx +0 -14
  295. package/lib/tui/components/ui/hascii/card-description.tsx +0 -13
  296. package/lib/tui/components/ui/hascii/card-footer.tsx +0 -14
  297. package/lib/tui/components/ui/hascii/card-header.tsx +0 -14
  298. package/lib/tui/components/ui/hascii/card-title.tsx +0 -13
  299. package/lib/tui/components/ui/hascii/card.tsx +0 -27
  300. package/lib/tui/components/ui/hascii/checkbox.tsx +0 -65
  301. package/lib/tui/components/ui/hascii/command.tsx +0 -159
  302. package/lib/tui/components/ui/hascii/dialog-content.tsx +0 -14
  303. package/lib/tui/components/ui/hascii/dialog-description.tsx +0 -13
  304. package/lib/tui/components/ui/hascii/dialog-footer.tsx +0 -14
  305. package/lib/tui/components/ui/hascii/dialog-header.tsx +0 -14
  306. package/lib/tui/components/ui/hascii/dialog-title.tsx +0 -13
  307. package/lib/tui/components/ui/hascii/dialog.tsx +0 -27
  308. package/lib/tui/components/ui/hascii/file-tree.tsx +0 -142
  309. package/lib/tui/components/ui/hascii/focus-group.tsx +0 -62
  310. package/lib/tui/components/ui/hascii/form-item.tsx +0 -43
  311. package/lib/tui/components/ui/hascii/input-otp.tsx +0 -86
  312. package/lib/tui/components/ui/hascii/input.tsx +0 -130
  313. package/lib/tui/components/ui/hascii/pagination.tsx +0 -105
  314. package/lib/tui/components/ui/hascii/progress.tsx +0 -28
  315. package/lib/tui/components/ui/hascii/select.tsx +0 -131
  316. package/lib/tui/components/ui/hascii/separator.tsx +0 -35
  317. package/lib/tui/components/ui/hascii/sidebar-content.tsx +0 -23
  318. package/lib/tui/components/ui/hascii/sidebar-header.tsx +0 -14
  319. package/lib/tui/components/ui/hascii/sidebar-menu-item.tsx +0 -67
  320. package/lib/tui/components/ui/hascii/sidebar.tsx +0 -24
  321. package/lib/tui/components/ui/hascii/skeleton.tsx +0 -60
  322. package/lib/tui/components/ui/hascii/slider.tsx +0 -91
  323. package/lib/tui/components/ui/hascii/snackbar.tsx +0 -75
  324. package/lib/tui/components/ui/hascii/sparkline.tsx +0 -53
  325. package/lib/tui/components/ui/hascii/spinner.tsx +0 -47
  326. package/lib/tui/components/ui/hascii/stepper.tsx +0 -54
  327. package/lib/tui/components/ui/hascii/switch.tsx +0 -66
  328. package/lib/tui/components/ui/hascii/table.tsx +0 -95
  329. package/lib/tui/components/ui/hascii/tabs.tsx +0 -59
  330. package/lib/tui/components/ui/hascii/toggle-group-item.tsx +0 -45
  331. package/lib/tui/components/ui/hascii/toggle-group.tsx +0 -99
  332. package/lib/tui/components/ui/hascii/tree.tsx +0 -104
  333. package/lib/tui/components/view-shell.tsx +0 -44
  334. package/lib/tui/filter-input.tsx +0 -33
  335. package/lib/tui/hooks/hascii/use-pressable.ts +0 -54
  336. package/lib/tui/parse-comma-list.ts +0 -14
  337. package/lib/tui/profile-launcher.tsx +0 -61
  338. package/lib/tui/scrollbar-options.ts +0 -19
  339. package/lib/tui/sidebar.tsx +0 -50
  340. package/lib/tui/theme.ts +0 -40
  341. package/lib/tui/tui.tsx +0 -20
  342. package/lib/tui/types.ts +0 -38
  343. package/lib/tui/unique-name.ts +0 -18
  344. package/lib/tui/use-event-stream.ts +0 -133
  345. package/lib/tui/use-snapshot.ts +0 -99
  346. package/lib/tui/utils/hascii/form-item-context.tsx +0 -23
  347. package/lib/tui/utils/hascii/input-focus-context.tsx +0 -31
  348. package/lib/tui/utils/hascii/theme-context.tsx +0 -26
  349. package/lib/tui/utils/hascii/theme.ts +0 -176
  350. package/lib/tui/views/channels-view.tsx +0 -108
  351. package/lib/tui/views/connectors-view.tsx +0 -164
  352. package/lib/tui/views/events-view.tsx +0 -160
  353. package/lib/tui/views/listeners-view.tsx +0 -80
  354. package/lib/tui/views/profiles-view.tsx +0 -152
@@ -1,108 +0,0 @@
1
- /** @jsxImportSource @opentui/react */
2
- import { AddRow } from "@/tui/components/add-row"
3
- import { Card } from "@/tui/components/card"
4
- import { EditableField } from "@/tui/components/editable-field"
5
- import { EmptyState } from "@/tui/components/empty-state"
6
- import { PanelHeader } from "@/tui/components/panel-header"
7
- import { ReadonlyField } from "@/tui/components/readonly-field"
8
- import { ViewShell } from "@/tui/components/view-shell"
9
- import type { Snapshot } from "@/tui/types"
10
- import { uniqueName } from "@/tui/unique-name"
11
- import type { Funnel } from "@/funnel"
12
-
13
- type Props = {
14
- snapshot: Snapshot
15
- funnel: Funnel
16
- refresh: () => void
17
- focusedKey: string | null
18
- setFocusedKey: (key: string | null) => void
19
- }
20
-
21
- type Channel = Snapshot["channels"][number]
22
-
23
- const fieldKey = (name: string, field: string): string => `channels::${name}::${field}`
24
-
25
- /**
26
- * Channel inspector — one Card per channel. Connectors live nested inside the
27
- * channel and are managed in the connectors view; here only the channel's
28
- * name and id (read-only) are shown along with a count of nested connectors.
29
- */
30
- export function ChannelsView(props: Props) {
31
- const channels = props.snapshot.channels
32
-
33
- const commit = (channel: Channel, field: string, raw: string): void => {
34
- try {
35
- if (field === "name") {
36
- const next = raw.trim()
37
-
38
- if (next && next !== channel.name) props.funnel.channels.rename(channel.name, next)
39
- }
40
- } catch (error) {
41
- props.funnel.logger.error(error instanceof Error ? error.message : String(error))
42
- }
43
-
44
- props.setFocusedKey(null)
45
- props.refresh()
46
- }
47
-
48
- const removeChannel = (name: string): void => {
49
- try {
50
- props.funnel.channels.remove(name)
51
- } catch (error) {
52
- props.funnel.logger.error(error instanceof Error ? error.message : String(error))
53
- }
54
-
55
- props.setFocusedKey(null)
56
- props.refresh()
57
- }
58
-
59
- const addChannel = (): void => {
60
- const name = uniqueName(
61
- channels.map((c) => c.name),
62
- "channel",
63
- )
64
-
65
- try {
66
- const created = props.funnel.channels.add({ name })
67
- props.setFocusedKey(fieldKey(created.name, "name"))
68
- } catch (error) {
69
- props.funnel.logger.error(error instanceof Error ? error.message : String(error))
70
- }
71
-
72
- props.refresh()
73
- }
74
-
75
- return (
76
- <ViewShell>
77
- <PanelHeader label="channels" count={channels.length} />
78
-
79
- {channels.length === 0 ? (
80
- <EmptyState message="(none — use the button below to add one)" />
81
- ) : (
82
- channels.map((channel) => (
83
- <Card key={channel.id} title={channel.name} onDelete={() => removeChannel(channel.name)}>
84
- <EditableField
85
- label="name"
86
- initialValue={channel.name}
87
- focused={props.focusedKey === fieldKey(channel.name, "name")}
88
- onFocus={() => props.setFocusedKey(fieldKey(channel.name, "name"))}
89
- onCommit={(raw) => commit(channel, "name", raw)}
90
- />
91
- <ReadonlyField label="id" value={channel.id} />
92
- <ReadonlyField label="delivery" value={channel.delivery} />
93
- <ReadonlyField
94
- label="connectors"
95
- value={
96
- channel.connectors.length > 0
97
- ? channel.connectors.map((c) => `${c.name}:${c.type}`).join(", ")
98
- : "(none)"
99
- }
100
- />
101
- </Card>
102
- ))
103
- )}
104
-
105
- <AddRow label="add channel" onClick={addChannel} />
106
- </ViewShell>
107
- )
108
- }
@@ -1,164 +0,0 @@
1
- import { AddRow } from "@/tui/components/add-row"
2
- import { Card } from "@/tui/components/card"
3
- import { EmptyState } from "@/tui/components/empty-state"
4
- import { PanelHeader } from "@/tui/components/panel-header"
5
- import { ReadonlyField } from "@/tui/components/readonly-field"
6
- import { HasciiButton } from "@/tui/components/ui/hascii/button"
7
- import { ViewShell } from "@/tui/components/view-shell"
8
- import { funnel } from "@/tui/theme"
9
- import type { Snapshot } from "@/tui/types"
10
- import { uniqueName } from "@/tui/unique-name"
11
- import type { Funnel } from "@/funnel"
12
-
13
- type Props = {
14
- snapshot: Snapshot
15
- funnel: Funnel
16
- refresh: () => void
17
- focusedKey: string | null
18
- setFocusedKey: (key: string | null) => void
19
- }
20
-
21
- type Connector = Snapshot["connectors"][number]
22
- type ConnectorType = Connector["type"]
23
-
24
- const formatTimestamp = (iso: string | undefined): string => {
25
- if (!iso) return "—"
26
-
27
- const d = new Date(iso)
28
-
29
- if (Number.isNaN(d.getTime())) return "—"
30
-
31
- const yyyy = d.getFullYear()
32
- const mm = String(d.getMonth() + 1).padStart(2, "0")
33
- const dd = String(d.getDate()).padStart(2, "0")
34
- const hh = String(d.getHours()).padStart(2, "0")
35
- const mn = String(d.getMinutes()).padStart(2, "0")
36
-
37
- return `${yyyy}-${mm}-${dd} ${hh}:${mn}`
38
- }
39
-
40
- /**
41
- * Channel-scoped connector inspector. Reads `funnel.channels.listAllConnectors()`
42
- * (already flattened with channelName / channelId tags) and lets the user delete
43
- * each connector or quickly add a new one to the first available channel via the
44
- * AddRow buttons. Editing values is intentionally read-only — token / pollInterval
45
- * mutation belongs to `fnl channels <ch> connectors set <conn> ...` because the
46
- * same connector name can exist in multiple channels and inline edits would have
47
- * to disambiguate.
48
- */
49
- export function ConnectorsView(props: Props) {
50
- const connectors = props.snapshot.connectors
51
- const channels = props.snapshot.channels
52
- const targetChannel = channels[0] ?? null
53
-
54
- const logError = (error: unknown): void => {
55
- props.funnel.logger.error(error instanceof Error ? error.message : String(error))
56
- }
57
-
58
- const removeConnector = (connector: Connector): void => {
59
- props.funnel.listeners.stop(connector.channelName, connector.name).catch(logError)
60
-
61
- try {
62
- props.funnel.channels.removeConnector(connector.channelName, connector.name)
63
- } catch (error) {
64
- logError(error)
65
- }
66
-
67
- props.refresh()
68
- }
69
-
70
- const addConnector = (type: ConnectorType): void => {
71
- if (!targetChannel) {
72
- logError(new Error("add a channel first before creating a connector"))
73
-
74
- return
75
- }
76
-
77
- const existingNames = connectors
78
- .filter((c) => c.channelId === targetChannel.id)
79
- .map((c) => c.name)
80
- const name = uniqueName(existingNames, type)
81
-
82
- try {
83
- if (type === "slack") {
84
- props.funnel.channels.addConnector(targetChannel.name, {
85
- type: "slack",
86
- name,
87
- botToken: "xoxb-PLACEHOLDER",
88
- appToken: "xapp-PLACEHOLDER",
89
- })
90
- } else if (type === "gh") {
91
- props.funnel.channels.addConnector(targetChannel.name, { type: "gh", name })
92
- } else if (type === "discord") {
93
- props.funnel.channels.addConnector(targetChannel.name, {
94
- type: "discord",
95
- name,
96
- botToken: "PLACEHOLDER-PLACEHOLDER",
97
- })
98
- } else {
99
- props.funnel.channels.addConnector(targetChannel.name, { type: "schedule", name })
100
- }
101
-
102
- props.funnel.listeners.start(targetChannel.name, name).catch(logError)
103
- } catch (error) {
104
- logError(error)
105
- }
106
-
107
- props.refresh()
108
- }
109
-
110
- return (
111
- <ViewShell>
112
- <PanelHeader label="connectors" count={connectors.length} />
113
-
114
- {connectors.length === 0 ? (
115
- <EmptyState message="(none — add via the buttons below or `fnl channels <ch> connectors add ...`)" />
116
- ) : (
117
- connectors.map((connector) => (
118
- <Card key={`${connector.channelId}::${connector.id}`} title={connector.name}>
119
- <ReadonlyField label="channel" value={connector.channelName} />
120
- <ReadonlyField label="type" value={connector.type} />
121
- <ReadonlyField label="id" value={connector.id} />
122
- {connector.type === "slack" ? (
123
- <>
124
- <ReadonlyField label="bot-token" value={connector.botToken} />
125
- <ReadonlyField label="app-token" value={connector.appToken} />
126
- </>
127
- ) : null}
128
- {connector.type === "gh" ? (
129
- <ReadonlyField label="poll" value={String(connector.pollInterval ?? 60)} />
130
- ) : null}
131
- {connector.type === "discord" ? (
132
- <ReadonlyField label="bot-token" value={connector.botToken} />
133
- ) : null}
134
- {connector.type === "schedule" ? (
135
- <ReadonlyField label="entries" value={String(connector.entries.length)} />
136
- ) : null}
137
- <text fg={funnel.faint}>{`created ${formatTimestamp(connector.createdAt)}`}</text>
138
- <box style={{ flexDirection: "row", justifyContent: "space-between" }}>
139
- <text fg={funnel.faint}>{`updated ${formatTimestamp(connector.updatedAt)}`}</text>
140
- <HasciiButton
141
- variant="destructive"
142
- size="sm"
143
- onPress={() => removeConnector(connector)}
144
- >
145
- delete
146
- </HasciiButton>
147
- </box>
148
- </Card>
149
- ))
150
- )}
151
-
152
- {targetChannel ? (
153
- <text fg={funnel.faint}>{`add target channel: ${targetChannel.name}`}</text>
154
- ) : (
155
- <text fg={funnel.warn}>add a channel first to enable the buttons below</text>
156
- )}
157
-
158
- <AddRow label="add slack" onClick={() => addConnector("slack")} />
159
- <AddRow label="add gh" onClick={() => addConnector("gh")} />
160
- <AddRow label="add discord" onClick={() => addConnector("discord")} />
161
- <AddRow label="add schedule" onClick={() => addConnector("schedule")} />
162
- </ViewShell>
163
- )
164
- }
@@ -1,160 +0,0 @@
1
- /** @jsxImportSource @opentui/react */
2
- import { DetailBar } from "@/tui/components/detail-bar"
3
- import { HasciiSeparator } from "@/tui/components/ui/hascii/separator"
4
- import { EmptyState } from "@/tui/components/empty-state"
5
- import { Keymap } from "@/tui/components/keymap"
6
- import { PanelHeader } from "@/tui/components/panel-header"
7
- import { ViewShell } from "@/tui/components/view-shell"
8
- import { funnel } from "@/tui/theme"
9
- import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
10
- import type { StreamEvent, StreamStatus } from "@/tui/types"
11
-
12
- type Props = {
13
- events: StreamEvent[]
14
- filter: string
15
- selectedIndex: number
16
- streamStatus: StreamStatus
17
- }
18
-
19
- const streamLabel = (status: StreamStatus): string => {
20
- if (status === "open") return "live"
21
- if (status === "connecting") return "connecting…"
22
- if (status === "closed") return "reconnecting…"
23
-
24
- return "offline"
25
- }
26
-
27
- const formatTime = (ms: number): string => {
28
- const date = new Date(ms)
29
- const hh = String(date.getHours()).padStart(2, "0")
30
- const mm = String(date.getMinutes()).padStart(2, "0")
31
- const ss = String(date.getSeconds()).padStart(2, "0")
32
-
33
- return `${hh}:${mm}:${ss}`
34
- }
35
-
36
- const truncate = (value: string, max: number): string => {
37
- const flat = value.replace(/\s+/g, " ").trim()
38
-
39
- if (flat.length <= max) return flat
40
-
41
- return `${flat.slice(0, max - 1)}…`
42
- }
43
-
44
- const matches = (event: StreamEvent, filter: string): boolean => {
45
- if (!filter) return true
46
-
47
- const needle = filter.toLowerCase()
48
- const haystack = [
49
- event.content,
50
- event.meta.connector ?? "",
51
- event.meta.event_type ?? "",
52
- event.meta.channel ?? "",
53
- ]
54
- .join(" ")
55
- .toLowerCase()
56
-
57
- return haystack.includes(needle)
58
- }
59
-
60
- const tryParseJson = (value: string): unknown => {
61
- try {
62
- return JSON.parse(value)
63
- } catch {
64
- return value
65
- }
66
- }
67
-
68
- const formatJson = (value: unknown): string => {
69
- try {
70
- return JSON.stringify(value, null, 2)
71
- } catch {
72
- return String(value)
73
- }
74
- }
75
-
76
- /**
77
- * Live event stream + detail of the selected event.
78
- *
79
- * The events list lives inside `ViewShell` (padded canvas) while the
80
- * detail strip is a sibling `DetailBar` so its background spans the
81
- * full main column edge-to-edge and reads as a distinct elevated
82
- * stratum below the list.
83
- */
84
- export function EventsView(props: Props) {
85
- const theme = useHasciiTheme()
86
- const visible = props.events.filter((event) => matches(event, props.filter))
87
- const selected = visible[props.selectedIndex] ?? null
88
-
89
- return (
90
- <box style={{ flexDirection: "column", flexGrow: 1 }}>
91
- <ViewShell>
92
- <PanelHeader
93
- label="events"
94
- count={visible.length}
95
- hint={[
96
- streamLabel(props.streamStatus),
97
- `${props.events.length} total`,
98
- props.filter ? `/${props.filter}/` : null,
99
- ]
100
- .filter((part): part is string => part !== null)
101
- .join(" · ")}
102
- />
103
-
104
- {visible.length === 0 ? (
105
- <EmptyState message="(no events yet — waiting for the first one)" />
106
- ) : (
107
- visible.map((event, index) => {
108
- const isSelected = index === props.selectedIndex
109
- const connector = event.meta.connector ?? "system"
110
- const eventType = event.meta.event_type ?? "?"
111
-
112
- return (
113
- <text key={event.id} bg={isSelected ? theme.color.muted : undefined}>
114
- <span fg={theme.color.mutedForeground}>{formatTime(event.receivedAt)}</span>
115
- <span fg={funnel.faint}> </span>
116
- <span fg={theme.color.mutedForeground}>{eventType.padEnd(8)}</span>
117
- <span fg={funnel.faint}>{" · "}</span>
118
- <span fg={isSelected ? theme.color.foreground : theme.color.foreground}>
119
- {connector.padEnd(14)}
120
- </span>
121
- <span fg={funnel.faint}> </span>
122
- <span fg={isSelected ? theme.color.foreground : theme.color.mutedForeground}>
123
- {truncate(event.content, 80)}
124
- </span>
125
- </text>
126
- )
127
- })
128
- )}
129
-
130
- <Keymap
131
- hints={[
132
- { key: "j/k", label: "select" },
133
- { key: "/", label: "filter" },
134
- ]}
135
- />
136
- </ViewShell>
137
-
138
- <DetailBar>
139
- <PanelHeader label="detail" />
140
-
141
- {!selected ? (
142
- <EmptyState message="(select an event with j/k to inspect)" />
143
- ) : (
144
- <>
145
- <text>
146
- <span fg={theme.color.mutedForeground}>meta: </span>
147
- <span fg={theme.color.foreground}>
148
- {Object.entries(selected.meta)
149
- .map(([key, value]) => `${key}=${value}`)
150
- .join(" ")}
151
- </span>
152
- </text>
153
- <HasciiSeparator />
154
- <text fg={theme.color.foreground}>{formatJson(tryParseJson(selected.content))}</text>
155
- </>
156
- )}
157
- </DetailBar>
158
- </box>
159
- )
160
- }
@@ -1,80 +0,0 @@
1
- /** @jsxImportSource @opentui/react */
2
- import { Card } from "@/tui/components/card"
3
- import { EmptyState } from "@/tui/components/empty-state"
4
- import { Keymap } from "@/tui/components/keymap"
5
- import { PanelHeader } from "@/tui/components/panel-header"
6
- import { ViewShell } from "@/tui/components/view-shell"
7
- import { funnel } from "@/tui/theme"
8
- import { useHasciiTheme } from "@/tui/utils/hascii/theme-context"
9
- import type { Snapshot, StreamEvent } from "@/tui/types"
10
-
11
- type Props = {
12
- snapshot: Snapshot
13
- events: StreamEvent[]
14
- selectedIndex: number
15
- busy: boolean
16
- }
17
-
18
- const eventCountBy = (events: StreamEvent[], connectorName: string): number => {
19
- let count = 0
20
-
21
- for (const event of events) {
22
- if (event.meta.connector === connectorName) count += 1
23
- }
24
-
25
- return count
26
- }
27
-
28
- /**
29
- * Listener registry — one Card per listener. The Card title shows the
30
- * listener's name; inside, a single status line carries the alive
31
- * dot, the connector type, and the event count. Cursor selection is
32
- * shown via the Card's `selected` accent. Listeners are runtime
33
- * entities derived from connectors, so there is no add path here —
34
- * register / remove a connector instead.
35
- */
36
- export function ListenersView(props: Props) {
37
- const theme = useHasciiTheme()
38
- const listeners = props.snapshot.listeners
39
-
40
- return (
41
- <ViewShell>
42
- <PanelHeader
43
- label="listeners"
44
- count={listeners.length}
45
- hint={props.busy ? "working…" : undefined}
46
- />
47
-
48
- {!props.snapshot.daemonReachable ? (
49
- <EmptyState message="(gateway daemon offline — press G to start it)" />
50
- ) : listeners.length === 0 ? (
51
- <EmptyState message="(no listeners — register a connector first)" />
52
- ) : (
53
- listeners.map((entry, index) => {
54
- const aliveColor = entry.alive ? funnel.alive : funnel.dead
55
- const count = eventCountBy(props.events, entry.name)
56
-
57
- return (
58
- <Card key={entry.name} title={entry.name} selected={index === props.selectedIndex}>
59
- <text>
60
- <span fg={aliveColor}>{entry.alive ? "●" : "○"}</span>
61
- <span fg={funnel.faint}> </span>
62
- <span fg={theme.color.mutedForeground}>{entry.type}</span>
63
- {count > 0 ? <span fg={theme.color.mutedForeground}>{` ${count}↓`}</span> : null}
64
- </text>
65
- </Card>
66
- )
67
- })
68
- )}
69
-
70
- <Keymap
71
- hints={[
72
- { key: "j/k", label: "select" },
73
- { key: "s", label: "start" },
74
- { key: "x", label: "stop" },
75
- { key: "R", label: "restart" },
76
- ]}
77
- />
78
- </ViewShell>
79
- )
80
- }
@@ -1,152 +0,0 @@
1
- /** @jsxImportSource @opentui/react */
2
- import { AddRow } from "@/tui/components/add-row"
3
- import { Card } from "@/tui/components/card"
4
- import { EditableField } from "@/tui/components/editable-field"
5
- import { EmptyState } from "@/tui/components/empty-state"
6
- import { Keymap } from "@/tui/components/keymap"
7
- import { PanelHeader } from "@/tui/components/panel-header"
8
- import { ViewShell } from "@/tui/components/view-shell"
9
- import type { Snapshot } from "@/tui/types"
10
- import { uniqueName } from "@/tui/unique-name"
11
- import type { Funnel } from "@/funnel"
12
-
13
- type Props = {
14
- snapshot: Snapshot
15
- selectedIndex: number
16
- funnel: Funnel
17
- refresh: () => void
18
- focusedKey: string | null
19
- setFocusedKey: (key: string | null) => void
20
- }
21
-
22
- type Profile = Snapshot["profiles"][number]
23
-
24
- const fieldKey = (name: string, field: string): string => `profiles::${name}::${field}`
25
-
26
- /**
27
- * Profile list — one Card per profile. Selection (j/k cursor) shows the
28
- * `▏` primary rule via the Card's `selected` prop; pressing `c`
29
- * launches Claude Code with the selected profile.
30
- *
31
- * `+ add profile` at the foot creates a new profile pointed at the
32
- * first existing channel (or an empty string if there are none, which
33
- * the user must then edit before launching).
34
- */
35
- export function ProfilesView(props: Props) {
36
- const profiles = props.snapshot.profiles
37
- const channels = props.snapshot.channels
38
-
39
- const commit = (profile: Profile, field: string, raw: string): void => {
40
- try {
41
- if (field === "name") {
42
- const next = raw.trim()
43
-
44
- if (next && next !== profile.name) props.funnel.profiles.rename(profile.name, next)
45
- } else if (field === "channel") {
46
- const next = raw.trim()
47
-
48
- if (next) props.funnel.profiles.update(profile.name, { channelId: next })
49
- } else if (field === "path") {
50
- const next = raw.trim()
51
-
52
- if (next) props.funnel.profiles.update(profile.name, { path: next })
53
- } else if (field === "sub-agent") {
54
- const next = raw.trim()
55
-
56
- if (next) props.funnel.profiles.update(profile.name, { subAgent: next })
57
- }
58
- } catch (error) {
59
- props.funnel.logger.error(error instanceof Error ? error.message : String(error))
60
- }
61
-
62
- props.setFocusedKey(null)
63
- props.refresh()
64
- }
65
-
66
- const removeProfile = (name: string): void => {
67
- try {
68
- props.funnel.profiles.remove(name)
69
- } catch (error) {
70
- props.funnel.logger.error(error instanceof Error ? error.message : String(error))
71
- }
72
-
73
- props.setFocusedKey(null)
74
- props.refresh()
75
- }
76
-
77
- const addProfile = (): void => {
78
- const name = uniqueName(
79
- profiles.map((p) => p.name),
80
- "profile",
81
- )
82
- const channelId = channels[0]?.name ?? ""
83
-
84
- try {
85
- props.funnel.profiles.add({ name, path: "", subAgent: "", channelId })
86
- props.setFocusedKey(fieldKey(name, "name"))
87
- } catch (error) {
88
- props.funnel.logger.error(error instanceof Error ? error.message : String(error))
89
- }
90
-
91
- props.refresh()
92
- }
93
-
94
- return (
95
- <ViewShell>
96
- <PanelHeader label="profiles" count={profiles.length} />
97
-
98
- {profiles.length === 0 ? (
99
- <EmptyState message="(none — use the button below to add one)" />
100
- ) : (
101
- profiles.map((profile, index) => (
102
- <Card
103
- key={profile.name}
104
- title={profile.name}
105
- selected={index === props.selectedIndex}
106
- onDelete={() => removeProfile(profile.name)}
107
- >
108
- <EditableField
109
- label="name"
110
- initialValue={profile.name}
111
- focused={props.focusedKey === fieldKey(profile.name, "name")}
112
- onFocus={() => props.setFocusedKey(fieldKey(profile.name, "name"))}
113
- onCommit={(raw) => commit(profile, "name", raw)}
114
- />
115
- <EditableField
116
- label="path"
117
- initialValue={profile.path}
118
- focused={props.focusedKey === fieldKey(profile.name, "path")}
119
- onFocus={() => props.setFocusedKey(fieldKey(profile.name, "path"))}
120
- onCommit={(raw) => commit(profile, "path", raw)}
121
- placeholder="repository path"
122
- />
123
- <EditableField
124
- label="sub-agent"
125
- initialValue={profile.subAgent}
126
- focused={props.focusedKey === fieldKey(profile.name, "sub-agent")}
127
- onFocus={() => props.setFocusedKey(fieldKey(profile.name, "sub-agent"))}
128
- onCommit={(raw) => commit(profile, "sub-agent", raw)}
129
- placeholder="claude --agent value"
130
- />
131
- <EditableField
132
- label="channel"
133
- initialValue={profile.channelId}
134
- focused={props.focusedKey === fieldKey(profile.name, "channel")}
135
- onFocus={() => props.setFocusedKey(fieldKey(profile.name, "channel"))}
136
- onCommit={(raw) => commit(profile, "channel", raw)}
137
- />
138
- </Card>
139
- ))
140
- )}
141
-
142
- <AddRow label="add profile" onClick={addProfile} />
143
-
144
- <Keymap
145
- hints={[
146
- { key: "j/k", label: "select" },
147
- { key: "c", label: "launch" },
148
- ]}
149
- />
150
- </ViewShell>
151
- )
152
- }