@crouton-kit/humanloop 0.4.9 → 0.4.11

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 (125) hide show
  1. package/dist/api.d.ts +1 -1
  2. package/dist/api.js +4 -4
  3. package/dist/cli.js +5 -3
  4. package/dist/editor/terminal-review.d.ts +21 -1
  5. package/dist/editor/terminal-review.js +88 -19
  6. package/dist/inbox/controller.js +34 -25
  7. package/dist/inbox/deck-factories.d.ts +3 -1
  8. package/dist/inbox/deck-factories.js +2 -1
  9. package/dist/inbox/deck-schema.d.ts +2 -1
  10. package/dist/inbox/deck-schema.js +3 -2
  11. package/dist/inbox/maintenance.js +1 -10
  12. package/dist/inbox/scan.js +1 -1
  13. package/dist/inbox/tickets.js +1 -1
  14. package/dist/inbox/tui.js +6 -16
  15. package/dist/inbox/visual.d.ts +3 -11
  16. package/dist/inbox/visual.js +15 -59
  17. package/dist/index.d.ts +2 -2
  18. package/dist/index.js +1 -1
  19. package/dist/render/termrender.d.ts +18 -1
  20. package/dist/render/termrender.js +66 -5
  21. package/dist/render/version.d.ts +1 -1
  22. package/dist/render/version.js +1 -1
  23. package/dist/tui/ansi.d.ts +29 -5
  24. package/dist/tui/ansi.js +123 -27
  25. package/dist/tui/app.js +24 -29
  26. package/dist/tui/input.js +13 -0
  27. package/dist/tui/render.d.ts +3 -0
  28. package/dist/tui/render.js +71 -29
  29. package/dist/types.d.ts +14 -5
  30. package/dist/web/assets/{abnfDiagram-VRR7QNED-CeahG6AG.js → abnfDiagram-VRR7QNED-C3G7ucis.js} +1 -1
  31. package/dist/web/assets/{arc-CDoEbwHt.js → arc-CfHbBVfI.js} +1 -1
  32. package/dist/web/assets/architecture-TIHT7OUA-XeXU8J5n.js +1 -0
  33. package/dist/web/assets/{architectureDiagram-ZJ3FMSHR-BPjR0pOT.js → architectureDiagram-ZJ3FMSHR-BCs4FMRj.js} +1 -1
  34. package/dist/web/assets/{blockDiagram-677ZJIJ3-COPZ3wOy.js → blockDiagram-677ZJIJ3-Ddt1PlNC.js} +1 -1
  35. package/dist/web/assets/{c4Diagram-LMCZKHZV-BDlHqm5m.js → c4Diagram-LMCZKHZV-BWwkKdPZ.js} +1 -1
  36. package/dist/web/assets/channel-BfMifkFv.js +1 -0
  37. package/dist/web/assets/{chunk-32BRIVSS-ChE1ajNa.js → chunk-32BRIVSS-BjyMBLnt.js} +1 -1
  38. package/dist/web/assets/{chunk-52WLFC77-DN_FDyhc.js → chunk-52WLFC77-DwnRc5bP.js} +1 -1
  39. package/dist/web/assets/{chunk-C7G6YPKG-B3RJ96Zq.js → chunk-C7G6YPKG-Cv5J31rb.js} +1 -1
  40. package/dist/web/assets/{chunk-EX3LRPZG-DgUeuz17.js → chunk-EX3LRPZG-Ds-y37uL.js} +1 -1
  41. package/dist/web/assets/{chunk-FWX5IMBZ-BLUVTbLv.js → chunk-FWX5IMBZ-BzgaWqkI.js} +2 -2
  42. package/dist/web/assets/{chunk-HOUHSVGY-Be0i0s6_.js → chunk-HOUHSVGY-CB_Rjcvz.js} +1 -1
  43. package/dist/web/assets/{chunk-ICXQ74PX-3WXYMfu9.js → chunk-ICXQ74PX-a8BpKR5q.js} +1 -1
  44. package/dist/web/assets/{chunk-MOJQB5TN-D-pOUBYm.js → chunk-MOJQB5TN-b09YHDGP.js} +1 -1
  45. package/dist/web/assets/{chunk-OGEWGWER-B-il3nZ9.js → chunk-OGEWGWER-CK9U88Zp.js} +1 -1
  46. package/dist/web/assets/{chunk-PUDLZKDR-D6N4Hutq.js → chunk-PUDLZKDR-DBp4NxwM.js} +1 -1
  47. package/dist/web/assets/{chunk-Q4XR5HBZ-9cq52Pkk.js → chunk-Q4XR5HBZ-Cn7tHzZm.js} +1 -1
  48. package/dist/web/assets/{chunk-RYQCIY6F-BVYSEout.js → chunk-RYQCIY6F-DS8RMCZK.js} +1 -1
  49. package/dist/web/assets/{chunk-V7JOEXUC-BuJn3hRD.js → chunk-V7JOEXUC-BCTcSsiS.js} +1 -1
  50. package/dist/web/assets/{chunk-VAUOI2AC-C6vQq47C.js → chunk-VAUOI2AC-DQVUYSsu.js} +1 -1
  51. package/dist/web/assets/{chunk-VR4S4FIN-DqY4BkmV.js → chunk-VR4S4FIN-D5E0Nzn2.js} +1 -1
  52. package/dist/web/assets/{chunk-WYO6CB5R-DgJ7qTeI.js → chunk-WYO6CB5R-reDXIQan.js} +1 -1
  53. package/dist/web/assets/{chunk-XXDRQBXY-BA7v7GnL.js → chunk-XXDRQBXY-BFea61gW.js} +1 -1
  54. package/dist/web/assets/{chunk-ZGVPDNZ5-BbkNKCvq.js → chunk-ZGVPDNZ5-BbVer9LG.js} +1 -1
  55. package/dist/web/assets/classDiagram-OUVF2IWQ-B3fMukMS.js +1 -0
  56. package/dist/web/assets/classDiagram-v2-EOCWNBFH-B3fMukMS.js +1 -0
  57. package/dist/web/assets/{cose-bilkent-JH36ORCC-CXK095PF.js → cose-bilkent-JH36ORCC-Do23kUxV.js} +1 -1
  58. package/dist/web/assets/{cynefin-VYW2F7L2-C_VXlLiV.js → cynefin-VYW2F7L2-B51RrEfW.js} +1 -1
  59. package/dist/web/assets/{cynefinDiagram-TSTJHNR4-C1xlbL_1.js → cynefinDiagram-TSTJHNR4-BMRRvJi_.js} +1 -1
  60. package/dist/web/assets/{dagre-VKFMJZFB-CCTbik65.js → dagre-VKFMJZFB-Cq3oNHAr.js} +1 -1
  61. package/dist/web/assets/{diagram-FQU43EPY-DsT-Ov2v.js → diagram-FQU43EPY-B_OlWSFb.js} +1 -1
  62. package/dist/web/assets/{diagram-G47NLZAW-DtoIPU9F.js → diagram-G47NLZAW-CinfsHac.js} +1 -1
  63. package/dist/web/assets/{diagram-NH7WQ7WH-OFu5gDhD.js → diagram-NH7WQ7WH-B1z5z_xR.js} +1 -1
  64. package/dist/web/assets/{diagram-OA4YK3LP-Dd7dF-gL.js → diagram-OA4YK3LP-CaXxWKyb.js} +1 -1
  65. package/dist/web/assets/{diagram-WEI45ONY-Mux4SvIA.js → diagram-WEI45ONY-DJ_ofe0i.js} +1 -1
  66. package/dist/web/assets/{dist-9QOA8OIw.js → dist-D6gNQP9S.js} +1 -1
  67. package/dist/web/assets/{ebnfDiagram-CCIWWBDH-BrKI9mhn.js → ebnfDiagram-CCIWWBDH-CKdV58eP.js} +1 -1
  68. package/dist/web/assets/{erDiagram-Q63AITRT-ByTXMLhH.js → erDiagram-Q63AITRT-DCTuIs_r.js} +1 -1
  69. package/dist/web/assets/eventmodeling-45OFAUF4-BW_ZjAEZ.js +1 -0
  70. package/dist/web/assets/flowDiagram-23GEKE2U-kuTegDJX.js +1 -0
  71. package/dist/web/assets/{ganttDiagram-NO4QXBWP-DrERNlg-.js → ganttDiagram-NO4QXBWP-a2WUc8wp.js} +1 -1
  72. package/dist/web/assets/{gitGraph-TEB2WS4Q-DD6JbRZi.js → gitGraph-TEB2WS4Q-DMKf1qeU.js} +1 -1
  73. package/dist/web/assets/{gitGraphDiagram-IHSO6WYX-Btg9UWYR.js → gitGraphDiagram-IHSO6WYX-DW54jWiu.js} +1 -1
  74. package/dist/web/assets/{index-CkGfXbfm.js → index-BteexwNd.js} +4 -4
  75. package/dist/web/assets/index-DOlqkLKF.css +2 -0
  76. package/dist/web/assets/{info-DKCQHKI2-e7fTIPJt.js → info-DKCQHKI2-BeOi6-NO.js} +1 -1
  77. package/dist/web/assets/{infoDiagram-FWYZ7A6U-D8qwevDR.js → infoDiagram-FWYZ7A6U-DnmfPppA.js} +1 -1
  78. package/dist/web/assets/{ishikawaDiagram-FXEZZL3T-DVk__o1X.js → ishikawaDiagram-FXEZZL3T-CPNmCU4V.js} +1 -1
  79. package/dist/web/assets/{journeyDiagram-5HDEW3XC-BAqtMRPh.js → journeyDiagram-5HDEW3XC-BJ1dpMIR.js} +1 -1
  80. package/dist/web/assets/{kanban-definition-HUTT4EX6-BSx4RvJG.js → kanban-definition-HUTT4EX6-CTUMFpnW.js} +1 -1
  81. package/dist/web/assets/{line-U7gCrM1H.js → line-BxjdmOYH.js} +1 -1
  82. package/dist/web/assets/{linear-LChDqo68.js → linear-BTFwxw2k.js} +1 -1
  83. package/dist/web/assets/{mermaid-parser.core-DN-im0ZM.js → mermaid-parser.core-xq_aCbXg.js} +3 -3
  84. package/dist/web/assets/{mermaid.core-BtXRl0Mb.js → mermaid.core-CJ7BhoQw.js} +3 -3
  85. package/dist/web/assets/{mindmap-definition-LN4V7U3C-CyKxTVZ0.js → mindmap-definition-LN4V7U3C-CGKOonoC.js} +1 -1
  86. package/dist/web/assets/{packet-7NZHBO7P-DqwDWu78.js → packet-7NZHBO7P-DcZTUk_n.js} +1 -1
  87. package/dist/web/assets/{pegDiagram-2B236MQR-BtsNWFkr.js → pegDiagram-2B236MQR-BQhasiHR.js} +1 -1
  88. package/dist/web/assets/{pie-RZYD4A2V-BHZhIJNY.js → pie-RZYD4A2V-CoNqq_hi.js} +1 -1
  89. package/dist/web/assets/{pieDiagram-ENE6RG2P-Bg2vN06Z.js → pieDiagram-ENE6RG2P-DjXgajVD.js} +1 -1
  90. package/dist/web/assets/{quadrantDiagram-ABIIQ3AL-Cy3idFFa.js → quadrantDiagram-ABIIQ3AL-DnMCZ8Lo.js} +1 -1
  91. package/dist/web/assets/{radar-I7S5WNFK-YJjo6vJv.js → radar-I7S5WNFK-DNuRmGvt.js} +1 -1
  92. package/dist/web/assets/{railroad-3IZDKUUU-CmVtviO7.js → railroad-3IZDKUUU-BWCSIZRZ.js} +1 -1
  93. package/dist/web/assets/railroad-abnf-AHOZXSZD-CUq87fi8.js +1 -0
  94. package/dist/web/assets/railroad-ebnf-EBAXGLYW-Bx6VqkgS.js +1 -0
  95. package/dist/web/assets/railroad-peg-LSFZ7HO6-DkT6zLYM.js +1 -0
  96. package/dist/web/assets/{railroadDiagram-RFXS5EU6-H_2GxnX4.js → railroadDiagram-RFXS5EU6-COtqBRRI.js} +1 -1
  97. package/dist/web/assets/{requirementDiagram-TGXJPOKE-DapbEtLD.js → requirementDiagram-TGXJPOKE-Cgm9XId4.js} +1 -1
  98. package/dist/web/assets/{sankeyDiagram-HTMAVEWB-CCQHHs1d.js → sankeyDiagram-HTMAVEWB-CCOMrkbd.js} +1 -1
  99. package/dist/web/assets/{sequenceDiagram-DBY2YBRQ-CgVVV-1t.js → sequenceDiagram-DBY2YBRQ-CL0wy8uA.js} +1 -1
  100. package/dist/web/assets/{src-BfumX_U8.js → src-CvIIQCAG.js} +1 -1
  101. package/dist/web/assets/{stateDiagram-2N3HPSRC-BBCtLj7x.js → stateDiagram-2N3HPSRC-C5b_pAiI.js} +1 -1
  102. package/dist/web/assets/stateDiagram-v2-6OUMAXLB-CbW-xCPn.js +1 -0
  103. package/dist/web/assets/{swimlanes-5IMT3BWC-DO2t3ahL.js → swimlanes-5IMT3BWC-J11uWvqa.js} +1 -1
  104. package/dist/web/assets/swimlanesDiagram-G3AALYLV-DpbNWekf.js +8 -0
  105. package/dist/web/assets/{timeline-definition-FHXFAJF6-lUeG8UJD.js → timeline-definition-FHXFAJF6-Dsvj6fbD.js} +1 -1
  106. package/dist/web/assets/{treeView-QDETBFTQ-Btl8LjLd.js → treeView-QDETBFTQ-FH3Xeeef.js} +1 -1
  107. package/dist/web/assets/{treemap-6X3UGDF4-DrCUe046.js → treemap-6X3UGDF4-D1TKNacZ.js} +1 -1
  108. package/dist/web/assets/{vennDiagram-L72KCM5P-DC1qneaM.js → vennDiagram-L72KCM5P-D4zm__Fz.js} +1 -1
  109. package/dist/web/assets/{wardley-OPB4EBWU-BuUed_9_.js → wardley-OPB4EBWU-BRLeItQR.js} +1 -1
  110. package/dist/web/assets/{wardleyDiagram-EHGQE667-CaHmI1FN.js → wardleyDiagram-EHGQE667-B7j5UliQ.js} +1 -1
  111. package/dist/web/assets/{xychartDiagram-FW5EYKEG-ZhKRrmhh.js → xychartDiagram-FW5EYKEG-Bi0MfuVK.js} +1 -1
  112. package/dist/web/index.html +2 -2
  113. package/package.json +1 -1
  114. package/dist/web/assets/architecture-TIHT7OUA-dmDc2lc6.js +0 -1
  115. package/dist/web/assets/channel-Cz6ImOzU.js +0 -1
  116. package/dist/web/assets/classDiagram-OUVF2IWQ-CDIgAUln.js +0 -1
  117. package/dist/web/assets/classDiagram-v2-EOCWNBFH-CDIgAUln.js +0 -1
  118. package/dist/web/assets/eventmodeling-45OFAUF4-DODcRvgw.js +0 -1
  119. package/dist/web/assets/flowDiagram-23GEKE2U-BOjPmrMg.js +0 -1
  120. package/dist/web/assets/index-BT22-qAj.css +0 -2
  121. package/dist/web/assets/railroad-abnf-AHOZXSZD-BwPWzzAs.js +0 -1
  122. package/dist/web/assets/railroad-ebnf-EBAXGLYW-Cc8VfiVd.js +0 -1
  123. package/dist/web/assets/railroad-peg-LSFZ7HO6-DivliSK5.js +0 -1
  124. package/dist/web/assets/stateDiagram-v2-6OUMAXLB-CBzJzUEN.js +0 -1
  125. package/dist/web/assets/swimlanesDiagram-G3AALYLV-CNLJY6SU.js +0 -8
@@ -21,6 +21,9 @@ export declare function renderInputBuffer(buffer: string, cursor: number, maxWid
21
21
  * renderer mutating state mid-frame.
22
22
  */
23
23
  export declare function clampItemReviewScroll(state: TuiState, cols: number, rows: number): void;
24
+ /** Pan the card body horizontally. Clamping happens in the pre-render clamp
25
+ * the host already runs, exactly like vertical over-scroll. */
26
+ export declare function panItemReview(state: TuiState, direction: -1 | 1): void;
24
27
  export declare function renderItemReview(state: TuiState, cols: number, rows: number): string[];
25
28
  export declare function renderFinal(state: TuiState, cols: number, rows: number): string[];
26
29
  /**
@@ -1,5 +1,7 @@
1
- import { renderMarkdown } from '../render/termrender.js';
2
- import { ESC, RESET, BOLD, DIM, ITALIC, GREEN, YELLOW, CYAN, REVERSE, sanitize, singleLine, truncate, hline, wrap, hardWrap, centerHorizontal, clipLine, } from './ansi.js';
1
+ import { renderMarkdownBlockAwareLines } from '../render/termrender.js';
2
+ import { ESC, RESET, BOLD, DIM, ITALIC, GREEN, YELLOW, CYAN, REVERSE, sanitize, singleLine, truncate, hline, wrap, hardWrap, centerHorizontal, clipLine, panLine, visibleWidth, } from './ansi.js';
3
+ /** Cells one h/l press pans by in the card body. */
4
+ const PAN_STEP = 8;
3
5
  // ── Frame buffer ─────────────────────────────────────────────────────────────
4
6
  export function diffFrame(prevFrame, nextLines, rows, cols) {
5
7
  const clipped = cols !== undefined
@@ -109,6 +111,11 @@ function buildItemReviewLayout(state, cols, rows) {
109
111
  const visual = state.visuals.get(interaction.id);
110
112
  const response = state.responses.get(interaction.id);
111
113
  const maxW = Math.min(cols - 4, 120);
114
+ // Diagrams are pictures, not prose: they render at the pane's full width
115
+ // rather than the readability cap. The card stops being centered when one
116
+ // claims that width, so the extra columns are actually available to it.
117
+ const paneW = Math.max(20, cols - 4);
118
+ const mdLines = (md) => renderMarkdownBlockAwareLines(md, maxW, paneW);
112
119
  // Pre-body: position, divider, title, subtitle (always visible)
113
120
  const preLines = [];
114
121
  const pos = `${state.currentIndex + 1}/${state.interactions.length}`;
@@ -137,13 +144,13 @@ function buildItemReviewLayout(state, cols, rows) {
137
144
  if (state.bodyMode === 'question') {
138
145
  if (interaction.subtitle) {
139
146
  bodyLines.push('');
140
- for (const line of renderMarkdown(interaction.subtitle, maxW)) {
147
+ for (const line of mdLines(interaction.subtitle)) {
141
148
  bodyLines.push(` ${line}`);
142
149
  }
143
150
  }
144
151
  if (interaction.body) {
145
152
  bodyLines.push('');
146
- for (const line of renderMarkdown(interaction.body, maxW)) {
153
+ for (const line of mdLines(interaction.body)) {
147
154
  bodyLines.push(` ${line}`);
148
155
  }
149
156
  }
@@ -152,7 +159,10 @@ function buildItemReviewLayout(state, cols, rows) {
152
159
  if (visual?.status === 'ready') {
153
160
  bodyLines.push('');
154
161
  bodyLines.push(` ${DIM}── context ${hline(maxW - 12)}${RESET}`);
155
- for (const vl of visual.content.split('\n')) {
162
+ // Keep the provider's original Markdown through layout. This is the
163
+ // same block-aware contract as question/follow-up content: prose stays
164
+ // readable while Mermaid uses the available pane width.
165
+ for (const vl of visual.markdown === undefined ? visual.content.split('\n') : mdLines(visual.markdown)) {
156
166
  bodyLines.push(` ${vl}`);
157
167
  }
158
168
  bodyLines.push(` ${DIM}${hline(maxW)}${RESET}`);
@@ -163,7 +173,7 @@ function buildItemReviewLayout(state, cols, rows) {
163
173
  if (state.followUp.status === 'running')
164
174
  bodyLines.push(` ${DIM}consulting…${RESET}`);
165
175
  else if (state.followUp.status === 'ready') {
166
- for (const line of renderMarkdown(state.followUp.markdown, maxW))
176
+ for (const line of mdLines(state.followUp.markdown))
167
177
  bodyLines.push(` ${line}`);
168
178
  }
169
179
  else
@@ -185,9 +195,11 @@ function buildItemReviewLayout(state, cols, rows) {
185
195
  }
186
196
  if (state.inputMode) {
187
197
  postLines.push(` ${DIM}${hline(maxW)}${RESET}`);
188
- const label = interaction.freetextLabel !== undefined
189
- ? interaction.freetextLabel
190
- : state.inputMode.kind === 'comment' ? 'Comment' : 'Response';
198
+ const label = state.inputMode.kind === 'follow-up'
199
+ ? 'Ask a follow-up'
200
+ : interaction.freetextLabel !== undefined
201
+ ? interaction.freetextLabel
202
+ : state.inputMode.kind === 'comment' ? 'Comment' : 'Response';
191
203
  // Show attached option in comment mode. For single-select the comment
192
204
  // qualifies the pick; for multi-select an attached option means the
193
205
  // comment is saved as a per-option note (and auto-checks the option).
@@ -234,11 +246,35 @@ function buildItemReviewLayout(state, cols, rows) {
234
246
  const reservedRows = preLines.length + postLines.length + 1; // +1 for footer
235
247
  const bodyHeight = Math.max(1, rows - reservedRows);
236
248
  const maxScroll = Math.max(0, bodyLines.length - bodyHeight);
249
+ const overflows = bodyLines.length > bodyHeight;
250
+ // Horizontal geometry, derived from what is actually on screen: a body row
251
+ // wider than the prose column takes the whole pane (which also drops the
252
+ // centering pad below), and anything past that pans.
253
+ const scroll = Math.max(0, Math.min(state.scrollOffset || 0, maxScroll));
254
+ const visible = visibleItemBodyLines(bodyLines, scroll, bodyHeight);
255
+ let widest = 0;
256
+ for (const line of visible)
257
+ widest = Math.max(widest, visibleWidth(line));
258
+ const bodyBoxW = widest > maxW + 2 ? paneW + 2 : maxW + 2;
237
259
  return {
238
260
  interaction, preLines, bodyLines, postLines,
239
- maxW, bodyHeight, maxScroll, overflows: bodyLines.length > bodyHeight,
261
+ maxW, bodyHeight, maxScroll, overflows,
262
+ bodyBoxW, maxHScroll: Math.max(0, widest - bodyBoxW),
240
263
  };
241
264
  }
265
+ /** Body rows as they are actually painted: vertical-scroll indicators replace
266
+ * their endpoint rows, so those hidden rows cannot make h/l available. */
267
+ function visibleItemBodyLines(bodyLines, scroll, bodyHeight) {
268
+ if (bodyLines.length <= bodyHeight)
269
+ return bodyLines;
270
+ const visible = bodyLines.slice(scroll, scroll + bodyHeight);
271
+ if (scroll > 0)
272
+ visible[0] = ` ${DIM}↑ ${scroll} more above${RESET}`;
273
+ const remainingBelow = bodyLines.length - (scroll + bodyHeight);
274
+ if (remainingBelow > 0)
275
+ visible[visible.length - 1] = ` ${DIM}↓ ${remainingBelow} more below${RESET}`;
276
+ return visible;
277
+ }
242
278
  /**
243
279
  * Clamp state.scrollOffset to the current body's scroll bounds. The host runs
244
280
  * this as a pre-render step (from the input path) so `renderItemReview` stays a
@@ -246,29 +282,31 @@ function buildItemReviewLayout(state, cols, rows) {
246
282
  * renderer mutating state mid-frame.
247
283
  */
248
284
  export function clampItemReviewScroll(state, cols, rows) {
249
- const { maxScroll } = buildItemReviewLayout(state, cols, rows);
285
+ const { maxScroll, maxHScroll } = buildItemReviewLayout(state, cols, rows);
250
286
  state.scrollOffset = Math.max(0, Math.min(state.scrollOffset || 0, maxScroll));
251
287
  state.bodyScrollOffsets[state.bodyMode] = state.scrollOffset;
288
+ // Publish the live horizontal reach so the input layer can tell whether h/l
289
+ // mean "pan" on this card, and keep the stored pan inside it across vertical
290
+ // scrolling and resize.
291
+ state.hscrollMax = maxHScroll;
292
+ state.hscrollOffset = Math.max(0, Math.min(state.hscrollOffset || 0, maxHScroll));
293
+ }
294
+ /** Pan the card body horizontally. Clamping happens in the pre-render clamp
295
+ * the host already runs, exactly like vertical over-scroll. */
296
+ export function panItemReview(state, direction) {
297
+ state.hscrollOffset = Math.max(0, (state.hscrollOffset || 0) + direction * PAN_STEP);
252
298
  }
253
299
  export function renderItemReview(state, cols, rows) {
254
- const { interaction, preLines, bodyLines, postLines, maxW, bodyHeight, maxScroll, overflows } = buildItemReviewLayout(state, cols, rows);
300
+ const { interaction, preLines, bodyLines, postLines, maxW, bodyHeight, maxScroll, overflows, bodyBoxW, maxHScroll } = buildItemReviewLayout(state, cols, rows);
255
301
  // Read-only clamp: renderer never mutates state (clampItemReviewScroll, run
256
302
  // pre-render, keeps state.scrollOffset itself in bounds).
257
303
  const scroll = Math.max(0, Math.min(state.scrollOffset || 0, maxScroll));
258
- let visibleBody;
259
- if (overflows) {
260
- visibleBody = bodyLines.slice(scroll, scroll + bodyHeight);
261
- if (scroll > 0) {
262
- visibleBody[0] = ` ${DIM}↑ ${scroll} more above${RESET}`;
263
- }
264
- const remainingBelow = bodyLines.length - (scroll + bodyHeight);
265
- if (remainingBelow > 0) {
266
- visibleBody[visibleBody.length - 1] = ` ${DIM}↓ ${remainingBelow} more below${RESET}`;
267
- }
268
- }
269
- else {
270
- visibleBody = bodyLines;
271
- }
304
+ let visibleBody = visibleItemBodyLines(bodyLines, scroll, bodyHeight);
305
+ // Contain every body row in the card's rectangle: rows that fit are
306
+ // untouched, a diagram wider than the pane pans under h/l with ‹/› marking
307
+ // the content still off-screen.
308
+ const hscroll = Math.max(0, Math.min(state.hscrollOffset || 0, maxHScroll));
309
+ visibleBody = visibleBody.map((line) => panLine(line, hscroll, bodyBoxW));
272
310
  // Footer hint — mention scroll keys when body overflows
273
311
  const footerParts = interaction.multiSelect === true
274
312
  ? [
@@ -286,6 +324,9 @@ export function renderItemReview(state, cols, rows) {
286
324
  if (overflows) {
287
325
  footerParts.unshift(state.inputMode ? `${DIM}pgup/pgdn${RESET} scroll` : `${DIM}u/d${RESET} scroll`);
288
326
  }
327
+ if (maxHScroll > 0 && state.inputMode === null) {
328
+ footerParts.unshift(`${DIM}h/l${RESET} pan`);
329
+ }
289
330
  if (state.inputMode === null) {
290
331
  if (state.followUpAvailable)
291
332
  footerParts.push(`${DIM}?${RESET} follow-up`);
@@ -301,9 +342,10 @@ export function renderItemReview(state, cols, rows) {
301
342
  const clamped = lines.length > rows
302
343
  ? [...lines.slice(0, rows - 1), footer]
303
344
  : lines;
304
- // Content occupies maxW cols of body + 2 cols of left prefix center the
305
- // whole block when the terminal is wider than that.
306
- return centerHorizontal(clamped, cols, maxW + 2);
345
+ // Content occupies the body rectangle (prose column, or the full pane when a
346
+ // diagram claims it) — center the whole block when the terminal is wider,
347
+ // then contain every row so nothing can wrap past the card's rectangle.
348
+ return centerHorizontal(clamped, cols, bodyBoxW).map((line) => clipLine(line, cols));
307
349
  }
308
350
  function renderActions(interaction, selectedAction, maxW, existing, showFocus = true) {
309
351
  const lines = [];
package/dist/types.d.ts CHANGED
@@ -31,9 +31,8 @@ export interface Interaction {
31
31
  id: string;
32
32
  /** Short topic — the thing being decided, not the decision. */
33
33
  title: string;
34
- /** ONE-sentence TL;DR of the choice/stakes. Renders as markdown in the
35
- * scrollable region; keep it a single line — long prose belongs in `body`. */
36
- subtitle?: string;
34
+ /** One plain-English sentence stating the decision, recommendation/status, and stakes. */
35
+ subtitle: string;
37
36
  /** The full explanation: directive-flavored markdown rendered by termrender.
38
37
  * This is where long or rich content goes (never the wall of detail in
39
38
  * `subtitle`). `bodyPath` is the same content sourced from a file. */
@@ -154,6 +153,12 @@ export interface TuiState {
154
153
  question: number;
155
154
  visual: number;
156
155
  };
156
+ /** Horizontal pan offset, in display cells, for body rows wider than the
157
+ * card's rectangle (diagrams). Rows that fit never move. */
158
+ hscrollOffset: number;
159
+ /** Cells the current body can still pan right, published by the pre-render
160
+ * clamp so the input layer knows whether h/l mean "pan" here at all. */
161
+ hscrollMax: number;
157
162
  /** The mounting host provided an active Ctrl+O editor callback. */
158
163
  editorAvailable: boolean;
159
164
  followUpAvailable: boolean;
@@ -191,7 +196,7 @@ interface TicketSummaryBase {
191
196
  dir: string;
192
197
  id: string;
193
198
  title: string;
194
- subtitle?: string;
199
+ subtitle: string;
195
200
  blockedSince: string;
196
201
  source: DeckSource;
197
202
  claim?: ClaimSummary;
@@ -214,6 +219,7 @@ export interface ReviewDescriptor {
214
219
  file: string;
215
220
  output: string;
216
221
  title: string;
222
+ subtitle: string;
217
223
  source: DeckSource;
218
224
  blockedSince: string;
219
225
  }
@@ -275,7 +281,7 @@ export interface CanonicalInteractionOption {
275
281
  export interface CanonicalInteraction {
276
282
  id: string;
277
283
  title: string;
278
- subtitle?: string;
284
+ subtitle: string;
279
285
  body?: string;
280
286
  options: CanonicalInteractionOption[];
281
287
  multiSelect?: boolean;
@@ -298,6 +304,8 @@ export type VisualResult = {
298
304
  };
299
305
  export interface VisualHandle {
300
306
  result: Promise<VisualResult>;
307
+ /** Stop watching from this UI without changing the durable request. */
308
+ detach(): void;
301
309
  /** Synchronous and idempotent. A canceled handle has no renderable result. */
302
310
  cancel(): void;
303
311
  }
@@ -327,6 +335,7 @@ export interface MountedPanel {
327
335
  handleKey(input: string, key: Key): void;
328
336
  render(): string[];
329
337
  handleResize(cols: number, rows: number): string[];
338
+ /** Detach this UI while leaving provider work alive for a later mount. */
330
339
  unmount(): void;
331
340
  loadDeck(deck: Deck, opts?: {
332
341
  progressPath?: string;
@@ -1 +1 @@
1
- import{m as e,t}from"./mermaid-parser.core-DN-im0ZM.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-BfumX_U8.js";import"./chunk-WYO6CB5R-DgJ7qTeI.js";import"./chunk-VAUOI2AC-C6vQq47C.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-D-pOUBYm.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
1
+ import{m as e,t}from"./mermaid-parser.core-xq_aCbXg.js";import{n}from"./chunk-Y2CYZVJY-DsF7k-Jl.js";import{m as r}from"./src-CvIIQCAG.js";import"./chunk-WYO6CB5R-reDXIQan.js";import"./chunk-VAUOI2AC-DQVUYSsu.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-b09YHDGP.js";import{t as s}from"./chunk-JWPE2WC7-DVXcaiue.js";var c=e().RailroadAbnf.parser.LangiumParser,l=n(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=n(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=n(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=n(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=n(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=n(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=n(e=>{s(e,o),e.title&&o.setTitle(e.title),e.rules.map(e=>o.addRule(m(e)))},`populateDb`),g={parser:{parse:n(e=>{o.clear(),r.debug(`[ABNF Parser] Starting Langium parse`);let n=c.parse(e);if(n.lexerErrors.length>0||n.parserErrors.length>0)throw new t(n);let i=n.value;r.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),r.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:o}},db:o,renderer:a,styles:i};export{g as diagram};
@@ -1 +1 @@
1
- import{n as e,t}from"./path-BWPyau1x.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-9QOA8OIw.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i)if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
1
+ import{n as e,t}from"./path-BWPyau1x.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-D6gNQP9S.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i)if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
@@ -0,0 +1 @@
1
+ import"./chunk-KEIR6QF5-BfrZ3jm6.js";import{M as e}from"./mermaid-parser.core-xq_aCbXg.js";export{e as createArchitectureServices};