@greppleai/grip 0.0.1 → 0.1.3

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 (197) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +13 -7
  3. package/dist/bun/cli.d.ts.map +1 -1
  4. package/dist/bun/cli.js +4 -2
  5. package/dist/bun/cli.js.map +1 -1
  6. package/dist/bun/embedded-assets.d.ts +3 -0
  7. package/dist/bun/embedded-assets.d.ts.map +1 -0
  8. package/dist/bun/embedded-assets.js +52 -0
  9. package/dist/bun/embedded-assets.js.map +1 -0
  10. package/dist/cli/args.d.ts.map +1 -1
  11. package/dist/cli/args.js +1 -0
  12. package/dist/cli/args.js.map +1 -1
  13. package/dist/config.d.ts.map +1 -1
  14. package/dist/config.js +3 -3
  15. package/dist/config.js.map +1 -1
  16. package/dist/core/agent-session.d.ts.map +1 -1
  17. package/dist/core/agent-session.js +17 -2
  18. package/dist/core/agent-session.js.map +1 -1
  19. package/dist/core/remote-workspace-protocol/contract.d.ts +13 -4
  20. package/dist/core/remote-workspace-protocol/contract.d.ts.map +1 -1
  21. package/dist/core/remote-workspace-protocol/contract.js +17 -1
  22. package/dist/core/remote-workspace-protocol/contract.js.map +1 -1
  23. package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts +16 -0
  24. package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts.map +1 -0
  25. package/dist/core/remote-workspace-protocol/guest-shared-client.js +54 -0
  26. package/dist/core/remote-workspace-protocol/guest-shared-client.js.map +1 -0
  27. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts +23 -0
  28. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts.map +1 -0
  29. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js +112 -0
  30. package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js.map +1 -0
  31. package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts +30 -0
  32. package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts.map +1 -0
  33. package/dist/core/remote-workspace-protocol/guest-shared-uplink.js +243 -0
  34. package/dist/core/remote-workspace-protocol/guest-shared-uplink.js.map +1 -0
  35. package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts +3 -0
  36. package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts.map +1 -0
  37. package/dist/core/remote-workspace-protocol/lima-owner-directory.js +31 -0
  38. package/dist/core/remote-workspace-protocol/lima-owner-directory.js.map +1 -0
  39. package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts +9 -0
  40. package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts.map +1 -0
  41. package/dist/core/remote-workspace-protocol/lima-owner-lock.js +89 -0
  42. package/dist/core/remote-workspace-protocol/lima-owner-lock.js.map +1 -0
  43. package/dist/core/remote-workspace-protocol/session.d.ts +2 -0
  44. package/dist/core/remote-workspace-protocol/session.d.ts.map +1 -1
  45. package/dist/core/remote-workspace-protocol/session.js +3 -0
  46. package/dist/core/remote-workspace-protocol/session.js.map +1 -1
  47. package/dist/core/remote-workspace-protocol/shared-transport.d.ts +71 -0
  48. package/dist/core/remote-workspace-protocol/shared-transport.d.ts.map +1 -0
  49. package/dist/core/remote-workspace-protocol/shared-transport.js +68 -0
  50. package/dist/core/remote-workspace-protocol/shared-transport.js.map +1 -0
  51. package/dist/core/sandbox/command.d.ts +2 -4
  52. package/dist/core/sandbox/command.d.ts.map +1 -1
  53. package/dist/core/sandbox/command.js +24 -46
  54. package/dist/core/sandbox/command.js.map +1 -1
  55. package/dist/core/sandbox/config.d.ts +91 -0
  56. package/dist/core/sandbox/config.d.ts.map +1 -0
  57. package/dist/core/sandbox/config.js +88 -0
  58. package/dist/core/sandbox/config.js.map +1 -0
  59. package/dist/core/sandbox/lima-guest-binary.d.ts +11 -0
  60. package/dist/core/sandbox/lima-guest-binary.d.ts.map +1 -0
  61. package/dist/core/sandbox/lima-guest-binary.js +140 -0
  62. package/dist/core/sandbox/lima-guest-binary.js.map +1 -0
  63. package/dist/core/sandbox/lima.d.ts +22 -3
  64. package/dist/core/sandbox/lima.d.ts.map +1 -1
  65. package/dist/core/sandbox/lima.js +434 -42
  66. package/dist/core/sandbox/lima.js.map +1 -1
  67. package/dist/core/sandbox/shared-lima-roots.d.ts +11 -0
  68. package/dist/core/sandbox/shared-lima-roots.d.ts.map +1 -0
  69. package/dist/core/sandbox/shared-lima-roots.js +25 -0
  70. package/dist/core/sandbox/shared-lima-roots.js.map +1 -0
  71. package/dist/core/sandbox/update-shared-guest-binary.d.ts +8 -0
  72. package/dist/core/sandbox/update-shared-guest-binary.d.ts.map +1 -0
  73. package/dist/core/sandbox/update-shared-guest-binary.js +100 -0
  74. package/dist/core/sandbox/update-shared-guest-binary.js.map +1 -0
  75. package/dist/core/settings-manager.d.ts +1 -5
  76. package/dist/core/settings-manager.d.ts.map +1 -1
  77. package/dist/core/settings-manager.js.map +1 -1
  78. package/dist/core/tools/bash.d.ts.map +1 -1
  79. package/dist/core/tools/bash.js +25 -14
  80. package/dist/core/tools/bash.js.map +1 -1
  81. package/dist/core/tools/file-mutation-queue.d.ts +2 -0
  82. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  83. package/dist/core/tools/file-mutation-queue.js +9 -0
  84. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  85. package/dist/core/tools/operations.d.ts +13 -2
  86. package/dist/core/tools/operations.d.ts.map +1 -1
  87. package/dist/core/tools/operations.js +132 -36
  88. package/dist/core/tools/operations.js.map +1 -1
  89. package/dist/daemon/activity-budget.d.ts +10 -0
  90. package/dist/daemon/activity-budget.d.ts.map +1 -0
  91. package/dist/daemon/activity-budget.js +34 -0
  92. package/dist/daemon/activity-budget.js.map +1 -0
  93. package/dist/daemon/config.d.ts +2 -0
  94. package/dist/daemon/config.d.ts.map +1 -1
  95. package/dist/daemon/config.js +15 -0
  96. package/dist/daemon/config.js.map +1 -1
  97. package/dist/daemon/outbound-budget.d.ts +10 -0
  98. package/dist/daemon/outbound-budget.d.ts.map +1 -0
  99. package/dist/daemon/outbound-budget.js +30 -0
  100. package/dist/daemon/outbound-budget.js.map +1 -0
  101. package/dist/daemon/security.d.ts.map +1 -1
  102. package/dist/daemon/security.js +3 -1
  103. package/dist/daemon/security.js.map +1 -1
  104. package/dist/daemon/server.d.ts.map +1 -1
  105. package/dist/daemon/server.js +104 -24
  106. package/dist/daemon/server.js.map +1 -1
  107. package/dist/daemon/shared-connection.d.ts +33 -0
  108. package/dist/daemon/shared-connection.d.ts.map +1 -0
  109. package/dist/daemon/shared-connection.js +271 -0
  110. package/dist/daemon/shared-connection.js.map +1 -0
  111. package/dist/daemon/shared-revision.d.ts +5 -0
  112. package/dist/daemon/shared-revision.d.ts.map +1 -0
  113. package/dist/daemon/shared-revision.js +61 -0
  114. package/dist/daemon/shared-revision.js.map +1 -0
  115. package/dist/daemon/shared-roots.d.ts +9 -0
  116. package/dist/daemon/shared-roots.d.ts.map +1 -0
  117. package/dist/daemon/shared-roots.js +33 -0
  118. package/dist/daemon/shared-roots.js.map +1 -0
  119. package/dist/daemon/workspace-runtime.d.ts +1 -1
  120. package/dist/daemon/workspace-runtime.d.ts.map +1 -1
  121. package/dist/daemon/workspace-runtime.js +42 -10
  122. package/dist/daemon/workspace-runtime.js.map +1 -1
  123. package/dist/main.d.ts.map +1 -1
  124. package/dist/main.js +5 -3
  125. package/dist/main.js.map +1 -1
  126. package/dist/modes/interactive/components/assistant-message.d.ts +1 -0
  127. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/assistant-message.js +13 -3
  129. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  130. package/dist/modes/interactive/components/branch-summary-message.d.ts +4 -3
  131. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  132. package/dist/modes/interactive/components/branch-summary-message.js +5 -4
  133. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  134. package/dist/modes/interactive/components/compaction-summary-message.d.ts +4 -3
  135. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  136. package/dist/modes/interactive/components/compaction-summary-message.js +5 -4
  137. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  138. package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
  139. package/dist/modes/interactive/components/custom-message.js +4 -3
  140. package/dist/modes/interactive/components/custom-message.js.map +1 -1
  141. package/dist/modes/interactive/components/hook-execution.d.ts.map +1 -1
  142. package/dist/modes/interactive/components/hook-execution.js +3 -2
  143. package/dist/modes/interactive/components/hook-execution.js.map +1 -1
  144. package/dist/modes/interactive/components/message-card.d.ts +9 -0
  145. package/dist/modes/interactive/components/message-card.d.ts.map +1 -0
  146. package/dist/modes/interactive/components/message-card.js +24 -0
  147. package/dist/modes/interactive/components/message-card.js.map +1 -0
  148. package/dist/modes/interactive/components/skill-invocation-message.d.ts +4 -3
  149. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  150. package/dist/modes/interactive/components/skill-invocation-message.js +5 -4
  151. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  152. package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
  153. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  154. package/dist/modes/interactive/components/tool-execution.js +27 -21
  155. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  156. package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  157. package/dist/modes/interactive/components/user-message.js +3 -2
  158. package/dist/modes/interactive/components/user-message.js.map +1 -1
  159. package/dist/modes/interactive/interactive-mode.d.ts +0 -3
  160. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  161. package/dist/modes/interactive/interactive-mode.js +55 -44
  162. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  163. package/dist/modes/interactive/theme/theme.d.ts +3 -0
  164. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  165. package/dist/modes/interactive/theme/theme.js +10 -0
  166. package/dist/modes/interactive/theme/theme.js.map +1 -1
  167. package/dist/utils/clipboard-native.d.ts.map +1 -1
  168. package/dist/utils/clipboard-native.js +1 -1
  169. package/dist/utils/clipboard-native.js.map +1 -1
  170. package/dist/utils/image-resize.d.ts.map +1 -1
  171. package/dist/utils/image-resize.js +5 -0
  172. package/dist/utils/image-resize.js.map +1 -1
  173. package/dist/utils/photon.d.ts.map +1 -1
  174. package/dist/utils/photon.js +1 -0
  175. package/dist/utils/photon.js.map +1 -1
  176. package/docs/daemon.md +6 -4
  177. package/docs/docs.json +1 -1
  178. package/docs/index.md +8 -12
  179. package/docs/packages.md +3 -3
  180. package/docs/quickstart.md +11 -19
  181. package/docs/sandbox.md +40 -117
  182. package/docs/settings.md +12 -11
  183. package/docs/termux.md +1 -18
  184. package/docs/usage.md +2 -2
  185. package/examples/README.md +0 -2
  186. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  187. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  188. package/examples/extensions/sandbox/package.json +1 -1
  189. package/examples/extensions/with-deps/package.json +1 -1
  190. package/npm-shrinkwrap.json +12 -12
  191. package/package.json +5 -6
  192. package/dist/core/sandbox/docker.d.ts +0 -169
  193. package/dist/core/sandbox/docker.d.ts.map +0 -1
  194. package/dist/core/sandbox/docker.js +0 -633
  195. package/dist/core/sandbox/docker.js.map +0 -1
  196. package/examples/sandbox-docker/Dockerfile +0 -274
  197. package/examples/sandbox-docker/README.md +0 -88
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AA6B7E;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,oBAAoB,CAAiC;IAC7D,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAE5B,YACC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,mBAAmB,SAAgB,EACnC,SAAS,SAAI,EACb,oBAAoB,GAAE,SAAS,mBAAmB,EAAO,EAgBzD;IAEQ,UAAU,IAAI,IAAI,CAK1B;IAED,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAKxC;IAED,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAK1C;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKlC;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CASvC;IAED,OAAO,CAAC,qBAAqB;IAyB7B,OAAO,CAAC,oBAAoB;IAuB5B,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,sBAAsB;IAmB9B,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,UAAmB,GAAG,IAAI,CAS7E;CACD","sourcesContent":["import type { AssistantContent, AssistantMessage } from \"@greppleai/grip-ai\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { MarkdownTransformer } from \"../../../core/extensions/types.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { createMarkdownTransform } from \"./markdown-transform.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\ninterface AssistantContentVisibility {\n\tanyVisible: boolean;\n\tvisibleAfter: boolean[];\n}\n\nfunction isVisibleAssistantContent(content: AssistantContent): boolean {\n\treturn (\n\t\t(content.type === \"text\" && Boolean(content.text.trim())) ||\n\t\t(content.type === \"thinking\" && Boolean(content.thinking.trim()))\n\t);\n}\n\nfunction inspectAssistantContentVisibility(content: readonly AssistantContent[]): AssistantContentVisibility {\n\tconst visibleAfter = new Array<boolean>(content.length);\n\tlet anyVisible = false;\n\tfor (let index = content.length - 1; index >= 0; index--) {\n\t\tvisibleAfter[index] = anyVisible;\n\t\tif (isVisibleAssistantContent(content[index])) anyVisible = true;\n\t}\n\treturn { anyVisible, visibleAfter };\n}\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate markdownTransformers: readonly MarkdownTransformer[];\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\tprivate isStreaming = false;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tmarkdownTransformers: readonly MarkdownTransformer[] = [],\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.markdownTransformers = markdownTransformers;\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) {\n\t\t\treturn lines;\n\t\t}\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\t\treturn lines;\n\t}\n\n\tprivate renderThinkingContent(thinking: string, hasVisibleContentAfter: boolean): void {\n\t\tif (this.hideThinkingBlock) {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(theme.italic(theme.fg(\"thinkingText\", this.hiddenThinkingLabel)), this.outputPad, 0),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Markdown(\n\t\t\t\t\tthinking.trim(),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t\tthis.markdownTheme,\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant-thinking\", this.isStreaming, this.markdownTransformers),\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (hasVisibleContentAfter) this.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate renderMessageContent(message: AssistantMessage): boolean {\n\t\tconst visibility = inspectAssistantContentVisibility(message.content);\n\t\tif (visibility.anyVisible) this.contentContainer.addChild(new Spacer(1));\n\t\tlet hasToolCalls = false;\n\t\tfor (let index = 0; index < message.content.length; index++) {\n\t\t\tconst content = message.content[index];\n\t\t\tif (content.type === \"toolCall\") {\n\t\t\t\thasToolCalls = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme, undefined, {\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant\", this.isStreaming, this.markdownTransformers),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\" && content.thinking.trim()) {\n\t\t\t\tthis.renderThinkingContent(content.thinking, visibility.visibleAfter[index]);\n\t\t\t}\n\t\t}\n\t\treturn hasToolCalls;\n\t}\n\n\tprivate appendCompletionStatus(message: string): void {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", message), this.outputPad, 0));\n\t}\n\n\tprivate renderCompletionStatus(message: AssistantMessage, hasToolCalls: boolean): void {\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.appendCompletionStatus(\n\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (hasToolCalls) return;\n\t\tif (message.stopReason === \"aborted\") {\n\t\t\tconst abortMessage =\n\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t: \"Operation aborted\";\n\t\t\tthis.appendCompletionStatus(abortMessage);\n\t\t} else if (message.stopReason === \"error\") {\n\t\t\tthis.appendCompletionStatus(`Error: ${message.errorMessage || \"Unknown error\"}`);\n\t\t}\n\t}\n\n\tupdateContent(message: AssistantMessage, isStreaming = this.isStreaming): void {\n\t\tthis.lastMessage = message;\n\t\tthis.isStreaming = isStreaming;\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst hasToolCalls = this.renderMessageContent(message);\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tthis.renderCompletionStatus(message, hasToolCalls);\n\t}\n}\n"]}
1
+ {"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AA6B7E;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,oBAAoB,CAAiC;IAC7D,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAS;IAExB,YACC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,mBAAmB,SAAgB,EACnC,SAAS,SAAI,EACb,oBAAoB,GAAE,SAAS,mBAAmB,EAAO,EAgBzD;IAEQ,UAAU,IAAI,IAAI,CAK1B;IAED,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAKxC;IAED,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAK1C;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKlC;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAevC;IAED,OAAO,CAAC,qBAAqB;IAyB7B,OAAO,CAAC,oBAAoB;IAuB5B,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,sBAAsB;IAmB9B,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,UAAmB,GAAG,IAAI,CAU7E;CACD","sourcesContent":["import type { AssistantContent, AssistantMessage } from \"@greppleai/grip-ai\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { MarkdownTransformer } from \"../../../core/extensions/types.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { createMarkdownTransform } from \"./markdown-transform.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\ninterface AssistantContentVisibility {\n\tanyVisible: boolean;\n\tvisibleAfter: boolean[];\n}\n\nfunction isVisibleAssistantContent(content: AssistantContent): boolean {\n\treturn (\n\t\t(content.type === \"text\" && Boolean(content.text.trim())) ||\n\t\t(content.type === \"thinking\" && Boolean(content.thinking.trim()))\n\t);\n}\n\nfunction inspectAssistantContentVisibility(content: readonly AssistantContent[]): AssistantContentVisibility {\n\tconst visibleAfter = new Array<boolean>(content.length);\n\tlet anyVisible = false;\n\tfor (let index = content.length - 1; index >= 0; index--) {\n\t\tvisibleAfter[index] = anyVisible;\n\t\tif (isVisibleAssistantContent(content[index])) anyVisible = true;\n\t}\n\treturn { anyVisible, visibleAfter };\n}\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate markdownTransformers: readonly MarkdownTransformer[];\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\tprivate isStreaming = false;\n\tprivate isFinal = false;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tmarkdownTransformers: readonly MarkdownTransformer[] = [],\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.markdownTransformers = markdownTransformers;\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tif (width < 1) return [];\n\t\tconst lines = super.render(this.isFinal ? Math.max(1, width - 2) : width);\n\t\tif (this.isFinal) {\n\t\t\tconst marker = theme.bgFromFg(\"accent\", \" \");\n\t\t\tfor (let index = 0; index < lines.length; index++) {\n\t\t\t\tconst content = lines[index].trimEnd();\n\t\t\t\tlines[index] = width <= 2 || !content ? marker : `${marker}${content}`;\n\t\t\t}\n\t\t}\n\t\tif (this.hasToolCalls || lines.length === 0) return lines;\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\t\treturn lines;\n\t}\n\n\tprivate renderThinkingContent(thinking: string, hasVisibleContentAfter: boolean): void {\n\t\tif (this.hideThinkingBlock) {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(theme.italic(theme.fg(\"thinkingText\", this.hiddenThinkingLabel)), this.outputPad, 0),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Markdown(\n\t\t\t\t\tthinking.trim(),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t\tthis.markdownTheme,\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant-thinking\", this.isStreaming, this.markdownTransformers),\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (hasVisibleContentAfter) this.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate renderMessageContent(message: AssistantMessage): boolean {\n\t\tconst visibility = inspectAssistantContentVisibility(message.content);\n\t\tif (visibility.anyVisible) this.contentContainer.addChild(new Spacer(1));\n\t\tlet hasToolCalls = false;\n\t\tfor (let index = 0; index < message.content.length; index++) {\n\t\t\tconst content = message.content[index];\n\t\t\tif (content.type === \"toolCall\") {\n\t\t\t\thasToolCalls = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme, undefined, {\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant\", this.isStreaming, this.markdownTransformers),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\" && content.thinking.trim()) {\n\t\t\t\tthis.renderThinkingContent(content.thinking, visibility.visibleAfter[index]);\n\t\t\t}\n\t\t}\n\t\treturn hasToolCalls;\n\t}\n\n\tprivate appendCompletionStatus(message: string): void {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", message), this.outputPad, 0));\n\t}\n\n\tprivate renderCompletionStatus(message: AssistantMessage, hasToolCalls: boolean): void {\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.appendCompletionStatus(\n\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (hasToolCalls) return;\n\t\tif (message.stopReason === \"aborted\") {\n\t\t\tconst abortMessage =\n\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t: \"Operation aborted\";\n\t\t\tthis.appendCompletionStatus(abortMessage);\n\t\t} else if (message.stopReason === \"error\") {\n\t\t\tthis.appendCompletionStatus(`Error: ${message.errorMessage || \"Unknown error\"}`);\n\t\t}\n\t}\n\n\tupdateContent(message: AssistantMessage, isStreaming = this.isStreaming): void {\n\t\tthis.lastMessage = message;\n\t\tthis.isStreaming = isStreaming;\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst hasToolCalls = this.renderMessageContent(message);\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tthis.isFinal = !isStreaming && !hasToolCalls && message.stopReason === \"stop\";\n\t\tthis.renderCompletionStatus(message, hasToolCalls);\n\t}\n}\n"]}
@@ -31,6 +31,7 @@ export class AssistantMessageComponent extends Container {
31
31
  lastMessage;
32
32
  hasToolCalls = false;
33
33
  isStreaming = false;
34
+ isFinal = false;
34
35
  constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme(), hiddenThinkingLabel = "Thinking...", outputPad = 1, markdownTransformers = []) {
35
36
  super();
36
37
  this.hideThinkingBlock = hideThinkingBlock;
@@ -70,10 +71,18 @@ export class AssistantMessageComponent extends Container {
70
71
  }
71
72
  }
72
73
  render(width) {
73
- const lines = super.render(width);
74
- if (this.hasToolCalls || lines.length === 0) {
75
- return lines;
74
+ if (width < 1)
75
+ return [];
76
+ const lines = super.render(this.isFinal ? Math.max(1, width - 2) : width);
77
+ if (this.isFinal) {
78
+ const marker = theme.bgFromFg("accent", " ");
79
+ for (let index = 0; index < lines.length; index++) {
80
+ const content = lines[index].trimEnd();
81
+ lines[index] = width <= 2 || !content ? marker : `${marker}${content}`;
82
+ }
76
83
  }
84
+ if (this.hasToolCalls || lines.length === 0)
85
+ return lines;
77
86
  lines[0] = OSC133_ZONE_START + lines[0];
78
87
  lines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];
79
88
  return lines;
@@ -143,6 +152,7 @@ export class AssistantMessageComponent extends Container {
143
152
  this.contentContainer.clear();
144
153
  const hasToolCalls = this.renderMessageContent(message);
145
154
  this.hasToolCalls = hasToolCalls;
155
+ this.isFinal = !isStreaming && !hasToolCalls && message.stopReason === "stop";
146
156
  this.renderCompletionStatus(message, hasToolCalls);
147
157
  }
148
158
  }
@@ -1 +1 @@
1
- {"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantContent, AssistantMessage } from \"@greppleai/grip-ai\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { MarkdownTransformer } from \"../../../core/extensions/types.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { createMarkdownTransform } from \"./markdown-transform.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\ninterface AssistantContentVisibility {\n\tanyVisible: boolean;\n\tvisibleAfter: boolean[];\n}\n\nfunction isVisibleAssistantContent(content: AssistantContent): boolean {\n\treturn (\n\t\t(content.type === \"text\" && Boolean(content.text.trim())) ||\n\t\t(content.type === \"thinking\" && Boolean(content.thinking.trim()))\n\t);\n}\n\nfunction inspectAssistantContentVisibility(content: readonly AssistantContent[]): AssistantContentVisibility {\n\tconst visibleAfter = new Array<boolean>(content.length);\n\tlet anyVisible = false;\n\tfor (let index = content.length - 1; index >= 0; index--) {\n\t\tvisibleAfter[index] = anyVisible;\n\t\tif (isVisibleAssistantContent(content[index])) anyVisible = true;\n\t}\n\treturn { anyVisible, visibleAfter };\n}\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate markdownTransformers: readonly MarkdownTransformer[];\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\tprivate isStreaming = false;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tmarkdownTransformers: readonly MarkdownTransformer[] = [],\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.markdownTransformers = markdownTransformers;\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) {\n\t\t\treturn lines;\n\t\t}\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\t\treturn lines;\n\t}\n\n\tprivate renderThinkingContent(thinking: string, hasVisibleContentAfter: boolean): void {\n\t\tif (this.hideThinkingBlock) {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(theme.italic(theme.fg(\"thinkingText\", this.hiddenThinkingLabel)), this.outputPad, 0),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Markdown(\n\t\t\t\t\tthinking.trim(),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t\tthis.markdownTheme,\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant-thinking\", this.isStreaming, this.markdownTransformers),\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (hasVisibleContentAfter) this.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate renderMessageContent(message: AssistantMessage): boolean {\n\t\tconst visibility = inspectAssistantContentVisibility(message.content);\n\t\tif (visibility.anyVisible) this.contentContainer.addChild(new Spacer(1));\n\t\tlet hasToolCalls = false;\n\t\tfor (let index = 0; index < message.content.length; index++) {\n\t\t\tconst content = message.content[index];\n\t\t\tif (content.type === \"toolCall\") {\n\t\t\t\thasToolCalls = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme, undefined, {\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant\", this.isStreaming, this.markdownTransformers),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\" && content.thinking.trim()) {\n\t\t\t\tthis.renderThinkingContent(content.thinking, visibility.visibleAfter[index]);\n\t\t\t}\n\t\t}\n\t\treturn hasToolCalls;\n\t}\n\n\tprivate appendCompletionStatus(message: string): void {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", message), this.outputPad, 0));\n\t}\n\n\tprivate renderCompletionStatus(message: AssistantMessage, hasToolCalls: boolean): void {\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.appendCompletionStatus(\n\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (hasToolCalls) return;\n\t\tif (message.stopReason === \"aborted\") {\n\t\t\tconst abortMessage =\n\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t: \"Operation aborted\";\n\t\t\tthis.appendCompletionStatus(abortMessage);\n\t\t} else if (message.stopReason === \"error\") {\n\t\t\tthis.appendCompletionStatus(`Error: ${message.errorMessage || \"Unknown error\"}`);\n\t\t}\n\t}\n\n\tupdateContent(message: AssistantMessage, isStreaming = this.isStreaming): void {\n\t\tthis.lastMessage = message;\n\t\tthis.isStreaming = isStreaming;\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst hasToolCalls = this.renderMessageContent(message);\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tthis.renderCompletionStatus(message, hasToolCalls);\n\t}\n}\n"]}
1
+ {"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantContent, AssistantMessage } from \"@greppleai/grip-ai\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { MarkdownTransformer } from \"../../../core/extensions/types.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { createMarkdownTransform } from \"./markdown-transform.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\ninterface AssistantContentVisibility {\n\tanyVisible: boolean;\n\tvisibleAfter: boolean[];\n}\n\nfunction isVisibleAssistantContent(content: AssistantContent): boolean {\n\treturn (\n\t\t(content.type === \"text\" && Boolean(content.text.trim())) ||\n\t\t(content.type === \"thinking\" && Boolean(content.thinking.trim()))\n\t);\n}\n\nfunction inspectAssistantContentVisibility(content: readonly AssistantContent[]): AssistantContentVisibility {\n\tconst visibleAfter = new Array<boolean>(content.length);\n\tlet anyVisible = false;\n\tfor (let index = content.length - 1; index >= 0; index--) {\n\t\tvisibleAfter[index] = anyVisible;\n\t\tif (isVisibleAssistantContent(content[index])) anyVisible = true;\n\t}\n\treturn { anyVisible, visibleAfter };\n}\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate markdownTransformers: readonly MarkdownTransformer[];\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\tprivate isStreaming = false;\n\tprivate isFinal = false;\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = \"Thinking...\",\n\t\toutputPad = 1,\n\t\tmarkdownTransformers: readonly MarkdownTransformer[] = [],\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.markdownTransformers = markdownTransformers;\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tif (width < 1) return [];\n\t\tconst lines = super.render(this.isFinal ? Math.max(1, width - 2) : width);\n\t\tif (this.isFinal) {\n\t\t\tconst marker = theme.bgFromFg(\"accent\", \" \");\n\t\t\tfor (let index = 0; index < lines.length; index++) {\n\t\t\t\tconst content = lines[index].trimEnd();\n\t\t\t\tlines[index] = width <= 2 || !content ? marker : `${marker}${content}`;\n\t\t\t}\n\t\t}\n\t\tif (this.hasToolCalls || lines.length === 0) return lines;\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\t\treturn lines;\n\t}\n\n\tprivate renderThinkingContent(thinking: string, hasVisibleContentAfter: boolean): void {\n\t\tif (this.hideThinkingBlock) {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Text(theme.italic(theme.fg(\"thinkingText\", this.hiddenThinkingLabel)), this.outputPad, 0),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.contentContainer.addChild(\n\t\t\t\tnew Markdown(\n\t\t\t\t\tthinking.trim(),\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t\tthis.markdownTheme,\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant-thinking\", this.isStreaming, this.markdownTransformers),\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\tif (hasVisibleContentAfter) this.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate renderMessageContent(message: AssistantMessage): boolean {\n\t\tconst visibility = inspectAssistantContentVisibility(message.content);\n\t\tif (visibility.anyVisible) this.contentContainer.addChild(new Spacer(1));\n\t\tlet hasToolCalls = false;\n\t\tfor (let index = 0; index < message.content.length; index++) {\n\t\t\tconst content = message.content[index];\n\t\t\tif (content.type === \"toolCall\") {\n\t\t\t\thasToolCalls = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tthis.contentContainer.addChild(\n\t\t\t\t\tnew Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme, undefined, {\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant\", this.isStreaming, this.markdownTransformers),\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else if (content.type === \"thinking\" && content.thinking.trim()) {\n\t\t\t\tthis.renderThinkingContent(content.thinking, visibility.visibleAfter[index]);\n\t\t\t}\n\t\t}\n\t\treturn hasToolCalls;\n\t}\n\n\tprivate appendCompletionStatus(message: string): void {\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"error\", message), this.outputPad, 0));\n\t}\n\n\tprivate renderCompletionStatus(message: AssistantMessage, hasToolCalls: boolean): void {\n\t\tif (message.stopReason === \"length\") {\n\t\t\tthis.appendCompletionStatus(\n\t\t\t\t\"Error: Model stopped because it reached the maximum output token limit. The response may be incomplete.\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (hasToolCalls) return;\n\t\tif (message.stopReason === \"aborted\") {\n\t\t\tconst abortMessage =\n\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t: \"Operation aborted\";\n\t\t\tthis.appendCompletionStatus(abortMessage);\n\t\t} else if (message.stopReason === \"error\") {\n\t\t\tthis.appendCompletionStatus(`Error: ${message.errorMessage || \"Unknown error\"}`);\n\t\t}\n\t}\n\n\tupdateContent(message: AssistantMessage, isStreaming = this.isStreaming): void {\n\t\tthis.lastMessage = message;\n\t\tthis.isStreaming = isStreaming;\n\t\t// Clear content container\n\t\tthis.contentContainer.clear();\n\n\t\tconst hasToolCalls = this.renderMessageContent(message);\n\t\tthis.hasToolCalls = hasToolCalls;\n\t\tthis.isFinal = !isStreaming && !hasToolCalls && message.stopReason === \"stop\";\n\t\tthis.renderCompletionStatus(message, hasToolCalls);\n\t}\n}\n"]}
@@ -1,10 +1,11 @@
1
- import { Box, type MarkdownTheme } from "@greppleai/grip-tui";
1
+ import { type MarkdownTheme } from "@greppleai/grip-tui";
2
2
  import type { BranchSummaryMessage } from "../../../core/messages.ts";
3
+ import { MessageCard } from "./message-card.ts";
3
4
  /**
4
5
  * Component that renders a branch summary message with collapsed/expanded state.
5
- * Uses same background color as custom messages for visual consistency.
6
+ * Uses a colored label to distinguish it without filling the whole card background.
6
7
  */
7
- export declare class BranchSummaryMessageComponent extends Box {
8
+ export declare class BranchSummaryMessageComponent extends MessageCard {
8
9
  private expanded;
9
10
  private message;
10
11
  private markdownTheme;
@@ -1 +1 @@
1
- {"version":3,"file":"branch-summary-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/branch-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AACtF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAItE;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,GAAG;IACrD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,aAAa,CAAgB;IAErC,YAAY,OAAO,EAAE,oBAAoB,EAAE,aAAa,GAAE,aAAkC,EAK3F;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,aAAa;CA0BrB","sourcesContent":["import { Box, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { BranchSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\n/**\n * Component that renders a branch summary message with collapsed/expanded state.\n * Uses same background color as custom messages for visual consistency.\n */\nexport class BranchSummaryMessageComponent extends Box {\n\tprivate expanded = false;\n\tprivate message: BranchSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: BranchSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[branch]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = \"**Branch Summary**\\n\\n\";\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", \"Branch summary (\") +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" to expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"branch-summary-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/branch-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGtE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,WAAW;IAC7D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,aAAa,CAAgB;IAErC,YAAY,OAAO,EAAE,oBAAoB,EAAE,aAAa,GAAE,aAAkC,EAK3F;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,aAAa;CA0BrB","sourcesContent":["import { Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { BranchSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\n/**\n * Component that renders a branch summary message with collapsed/expanded state.\n * Uses a colored label to distinguish it without filling the whole card background.\n */\nexport class BranchSummaryMessageComponent extends MessageCard {\n\tprivate expanded = false;\n\tprivate message: BranchSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: BranchSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1);\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[branch]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = \"**Branch Summary**\\n\\n\";\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", \"Branch summary (\") +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" to expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
@@ -1,16 +1,17 @@
1
- import { Box, Markdown, Spacer, Text } from "@greppleai/grip-tui";
1
+ import { Markdown, Spacer, Text } from "@greppleai/grip-tui";
2
2
  import { getMarkdownTheme, theme } from "../theme/theme.js";
3
3
  import { keyText } from "./keybinding-hints.js";
4
+ import { MessageCard } from "./message-card.js";
4
5
  /**
5
6
  * Component that renders a branch summary message with collapsed/expanded state.
6
- * Uses same background color as custom messages for visual consistency.
7
+ * Uses a colored label to distinguish it without filling the whole card background.
7
8
  */
8
- export class BranchSummaryMessageComponent extends Box {
9
+ export class BranchSummaryMessageComponent extends MessageCard {
9
10
  expanded = false;
10
11
  message;
11
12
  markdownTheme;
12
13
  constructor(message, markdownTheme = getMarkdownTheme()) {
13
- super(1, 1, (t) => theme.bg("customMessageBg", t));
14
+ super(1, 1);
14
15
  this.message = message;
15
16
  this.markdownTheme = markdownTheme;
16
17
  this.updateDisplay();
@@ -1 +1 @@
1
- {"version":3,"file":"branch-summary-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/branch-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAsB,MAAM,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEtF,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAO,6BAA8B,SAAQ,GAAG;IAC7C,QAAQ,GAAG,KAAK,CAAC;IACjB,OAAO,CAAuB;IAC9B,aAAa,CAAgB;IAErC,YAAY,OAA6B,EAAE,aAAa,GAAkB,gBAAgB,EAAE,EAAE;QAC7F,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED,WAAW,CAAC,QAAiB,EAAQ;QACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEQ,UAAU,GAAS;QAC3B,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEO,aAAa,GAAS;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,oBAAoB,EAAE,yBAAyB,CAAC,CAAC;QACxE,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,wBAAwB,CAAC;YACxC,IAAI,CAAC,QAAQ,CACZ,IAAI,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE;gBACrE,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,IAAI,CAAC;aAC5D,CAAC,CACF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,QAAQ,CACZ,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,kBAAkB,CAAC;gBAChD,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;gBAC5C,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,aAAa,CAAC,EAC7C,CAAC,EACD,CAAC,CACD,CACD,CAAC;QACH,CAAC;IAAA,CACD;CACD","sourcesContent":["import { Box, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { BranchSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\n/**\n * Component that renders a branch summary message with collapsed/expanded state.\n * Uses same background color as custom messages for visual consistency.\n */\nexport class BranchSummaryMessageComponent extends Box {\n\tprivate expanded = false;\n\tprivate message: BranchSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: BranchSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[branch]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = \"**Branch Summary**\\n\\n\";\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", \"Branch summary (\") +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" to expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"branch-summary-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/branch-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEjF,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAO,6BAA8B,SAAQ,WAAW;IACrD,QAAQ,GAAG,KAAK,CAAC;IACjB,OAAO,CAAuB;IAC9B,aAAa,CAAgB;IAErC,YAAY,OAA6B,EAAE,aAAa,GAAkB,gBAAgB,EAAE,EAAE;QAC7F,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACZ,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED,WAAW,CAAC,QAAiB,EAAQ;QACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEQ,UAAU,GAAS;QAC3B,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEO,aAAa,GAAS;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,oBAAoB,EAAE,yBAAyB,CAAC,CAAC;QACxE,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,wBAAwB,CAAC;YACxC,IAAI,CAAC,QAAQ,CACZ,IAAI,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE;gBACrE,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,IAAI,CAAC;aAC5D,CAAC,CACF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,QAAQ,CACZ,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,kBAAkB,CAAC;gBAChD,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;gBAC5C,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,aAAa,CAAC,EAC7C,CAAC,EACD,CAAC,CACD,CACD,CAAC;QACH,CAAC;IAAA,CACD;CACD","sourcesContent":["import { Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { BranchSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\n/**\n * Component that renders a branch summary message with collapsed/expanded state.\n * Uses a colored label to distinguish it without filling the whole card background.\n */\nexport class BranchSummaryMessageComponent extends MessageCard {\n\tprivate expanded = false;\n\tprivate message: BranchSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: BranchSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1);\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[branch]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = \"**Branch Summary**\\n\\n\";\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", \"Branch summary (\") +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" to expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
@@ -1,10 +1,11 @@
1
- import { Box, type MarkdownTheme } from "@greppleai/grip-tui";
1
+ import { type MarkdownTheme } from "@greppleai/grip-tui";
2
2
  import type { CompactionSummaryMessage } from "../../../core/messages.ts";
3
+ import { MessageCard } from "./message-card.ts";
3
4
  /**
4
5
  * Component that renders a compaction message with collapsed/expanded state.
5
- * Uses same background color as custom messages for visual consistency.
6
+ * Uses a colored label to distinguish it without filling the whole card background.
6
7
  */
7
- export declare class CompactionSummaryMessageComponent extends Box {
8
+ export declare class CompactionSummaryMessageComponent extends MessageCard {
8
9
  private expanded;
9
10
  private message;
10
11
  private markdownTheme;
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-summary-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/compaction-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AACtF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAI1E;;;GAGG;AACH,qBAAa,iCAAkC,SAAQ,GAAG;IACzD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,aAAa,CAAgB;IAErC,YAAY,OAAO,EAAE,wBAAwB,EAAE,aAAa,GAAE,aAAkC,EAK/F;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,aAAa;CA2BrB","sourcesContent":["import { Box, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { CompactionSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\n/**\n * Component that renders a compaction message with collapsed/expanded state.\n * Uses same background color as custom messages for visual consistency.\n */\nexport class CompactionSummaryMessageComponent extends Box {\n\tprivate expanded = false;\n\tprivate message: CompactionSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: CompactionSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst tokenStr = this.message.tokensBefore.toLocaleString();\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[compaction]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = `**Compacted from ${tokenStr} tokens**\\n\\n`;\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", `Compacted from ${tokenStr} tokens (`) +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" to expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"compaction-summary-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/compaction-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAG1E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;GAGG;AACH,qBAAa,iCAAkC,SAAQ,WAAW;IACjE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,aAAa,CAAgB;IAErC,YAAY,OAAO,EAAE,wBAAwB,EAAE,aAAa,GAAE,aAAkC,EAK/F;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,aAAa;CA2BrB","sourcesContent":["import { Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { CompactionSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\n/**\n * Component that renders a compaction message with collapsed/expanded state.\n * Uses a colored label to distinguish it without filling the whole card background.\n */\nexport class CompactionSummaryMessageComponent extends MessageCard {\n\tprivate expanded = false;\n\tprivate message: CompactionSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: CompactionSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1, \"customMessageLabel\");\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst tokenStr = this.message.tokensBefore.toLocaleString();\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[compaction]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = `**Compacted from ${tokenStr} tokens**\\n\\n`;\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", `Compacted from ${tokenStr} tokens (`) +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" to expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
@@ -1,16 +1,17 @@
1
- import { Box, Markdown, Spacer, Text } from "@greppleai/grip-tui";
1
+ import { Markdown, Spacer, Text } from "@greppleai/grip-tui";
2
2
  import { getMarkdownTheme, theme } from "../theme/theme.js";
3
3
  import { keyText } from "./keybinding-hints.js";
4
+ import { MessageCard } from "./message-card.js";
4
5
  /**
5
6
  * Component that renders a compaction message with collapsed/expanded state.
6
- * Uses same background color as custom messages for visual consistency.
7
+ * Uses a colored label to distinguish it without filling the whole card background.
7
8
  */
8
- export class CompactionSummaryMessageComponent extends Box {
9
+ export class CompactionSummaryMessageComponent extends MessageCard {
9
10
  expanded = false;
10
11
  message;
11
12
  markdownTheme;
12
13
  constructor(message, markdownTheme = getMarkdownTheme()) {
13
- super(1, 1, (t) => theme.bg("customMessageBg", t));
14
+ super(1, 1, "customMessageLabel");
14
15
  this.message = message;
15
16
  this.markdownTheme = markdownTheme;
16
17
  this.updateDisplay();
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-summary-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/compaction-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAsB,MAAM,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEtF,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAO,iCAAkC,SAAQ,GAAG;IACjD,QAAQ,GAAG,KAAK,CAAC;IACjB,OAAO,CAA2B;IAClC,aAAa,CAAgB;IAErC,YAAY,OAAiC,EAAE,aAAa,GAAkB,gBAAgB,EAAE,EAAE;QACjG,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED,WAAW,CAAC,QAAiB,EAAQ;QACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEQ,UAAU,GAAS;QAC3B,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEO,aAAa,GAAS;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,oBAAoB,EAAE,6BAA6B,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,oBAAoB,QAAQ,eAAe,CAAC;YAC3D,IAAI,CAAC,QAAQ,CACZ,IAAI,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE;gBACrE,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,IAAI,CAAC;aAC5D,CAAC,CACF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,QAAQ,CACZ,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,kBAAkB,QAAQ,WAAW,CAAC;gBACnE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;gBAC5C,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,aAAa,CAAC,EAC7C,CAAC,EACD,CAAC,CACD,CACD,CAAC;QACH,CAAC;IAAA,CACD;CACD","sourcesContent":["import { Box, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { CompactionSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\n/**\n * Component that renders a compaction message with collapsed/expanded state.\n * Uses same background color as custom messages for visual consistency.\n */\nexport class CompactionSummaryMessageComponent extends Box {\n\tprivate expanded = false;\n\tprivate message: CompactionSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: CompactionSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1, (t) => theme.bg(\"customMessageBg\", t));\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst tokenStr = this.message.tokensBefore.toLocaleString();\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[compaction]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = `**Compacted from ${tokenStr} tokens**\\n\\n`;\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", `Compacted from ${tokenStr} tokens (`) +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" to expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"compaction-summary-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/compaction-summary-message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAsB,MAAM,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAC;AAEjF,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAO,iCAAkC,SAAQ,WAAW;IACzD,QAAQ,GAAG,KAAK,CAAC;IACjB,OAAO,CAA2B;IAClC,aAAa,CAAgB;IAErC,YAAY,OAAiC,EAAE,aAAa,GAAkB,gBAAgB,EAAE,EAAE;QACjG,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,oBAAoB,CAAC,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAED,WAAW,CAAC,QAAiB,EAAQ;QACpC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEQ,UAAU,GAAS;QAC3B,KAAK,CAAC,UAAU,EAAE,CAAC;QACnB,IAAI,CAAC,aAAa,EAAE,CAAC;IAAA,CACrB;IAEO,aAAa,GAAS;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC;QAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,oBAAoB,EAAE,6BAA6B,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7B,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACnB,MAAM,MAAM,GAAG,oBAAoB,QAAQ,eAAe,CAAC;YAC3D,IAAI,CAAC,QAAQ,CACZ,IAAI,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE;gBACrE,KAAK,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,IAAI,CAAC;aAC5D,CAAC,CACF,CAAC;QACH,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,QAAQ,CACZ,IAAI,IAAI,CACP,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,kBAAkB,QAAQ,WAAW,CAAC;gBACnE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;gBAC5C,KAAK,CAAC,EAAE,CAAC,mBAAmB,EAAE,aAAa,CAAC,EAC7C,CAAC,EACD,CAAC,CACD,CACD,CAAC;QACH,CAAC;IAAA,CACD;CACD","sourcesContent":["import { Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { CompactionSummaryMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\n/**\n * Component that renders a compaction message with collapsed/expanded state.\n * Uses a colored label to distinguish it without filling the whole card background.\n */\nexport class CompactionSummaryMessageComponent extends MessageCard {\n\tprivate expanded = false;\n\tprivate message: CompactionSummaryMessage;\n\tprivate markdownTheme: MarkdownTheme;\n\n\tconstructor(message: CompactionSummaryMessage, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper(1, 1, \"customMessageLabel\");\n\t\tthis.message = message;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.updateDisplay();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\tthis.clear();\n\n\t\tconst tokenStr = this.message.tokensBefore.toLocaleString();\n\t\tconst label = theme.fg(\"customMessageLabel\", `\\x1b[1m[compaction]\\x1b[22m`);\n\t\tthis.addChild(new Text(label, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.expanded) {\n\t\t\tconst header = `**Compacted from ${tokenStr} tokens**\\n\\n`;\n\t\t\tthis.addChild(\n\t\t\t\tnew Markdown(header + this.message.summary, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"customMessageText\", `Compacted from ${tokenStr} tokens (`) +\n\t\t\t\t\t\ttheme.fg(\"dim\", keyText(\"app.tools.expand\")) +\n\t\t\t\t\t\ttheme.fg(\"customMessageText\", \" to expand)\"),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"custom-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-message.ts"],"names":[],"mappings":"AAEA,OAAO,EAAO,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/D;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,cAAc,CAAC,CAAkB;IACzC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAC,CAAY;IACpC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,SAAS,CAAS;IAE1B,YACC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,EAC/B,cAAc,CAAC,EAAE,eAAe,EAChC,aAAa,GAAE,aAAkC,EAejD;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAKnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,OAAO;CAkDf","sourcesContent":["import type { TextContent } from \"@greppleai/grip-ai\";\nimport type { Component } from \"@greppleai/grip-tui\";\nimport { Box, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { MessageRenderer } from \"../../../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\n\n/**\n * Component that renders a custom message entry from extensions.\n * Uses distinct styling to differentiate from user messages.\n */\nexport class CustomMessageComponent extends Container {\n\tprivate message: CustomMessage<unknown>;\n\tprivate customRenderer?: MessageRenderer;\n\tprivate box: Box;\n\tprivate customComponent?: Component;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate _expanded = false;\n\n\tconstructor(\n\t\tmessage: CustomMessage<unknown>,\n\t\tcustomRenderer?: MessageRenderer,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t) {\n\t\tsuper();\n\t\tthis.message = message;\n\t\tthis.customRenderer = customRenderer;\n\t\tthis.markdownTheme = markdownTheme;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// State summaries use the success palette to distinguish them from other custom messages.\n\t\tthis.box = new Box(1, 1, (t) =>\n\t\t\ttheme.bg(this.message.customType === \"compress_context\" ? \"toolSuccessBg\" : \"customMessageBg\", t),\n\t\t);\n\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this._expanded !== expanded) {\n\t\t\tthis._expanded = expanded;\n\t\t\tthis.rebuild();\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\t// Remove previous content component\n\t\tif (this.customComponent) {\n\t\t\tthis.removeChild(this.customComponent);\n\t\t\tthis.customComponent = undefined;\n\t\t}\n\t\tthis.removeChild(this.box);\n\n\t\t// Try custom renderer first - it handles its own styling\n\t\tif (this.customRenderer) {\n\t\t\ttry {\n\t\t\t\tconst component = this.customRenderer(this.message, { expanded: this._expanded }, theme);\n\t\t\t\tif (component) {\n\t\t\t\t\t// Custom renderer provides its own styled component\n\t\t\t\t\tthis.customComponent = component;\n\t\t\t\t\tthis.addChild(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through to default rendering\n\t\t\t}\n\t\t}\n\n\t\t// Default rendering uses our box\n\t\tthis.addChild(this.box);\n\t\tthis.box.clear();\n\n\t\t// Default rendering: label + content\n\t\tconst labelColor = this.message.customType === \"compress_context\" ? \"success\" : \"customMessageLabel\";\n\t\tconst label = theme.fg(labelColor, `\\x1b[1m[${this.message.customType}]\\x1b[22m`);\n\t\tthis.box.addChild(new Text(label, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\t// Extract text content\n\t\tlet text: string;\n\t\tif (typeof this.message.content === \"string\") {\n\t\t\ttext = this.message.content;\n\t\t} else {\n\t\t\ttext = this.message.content\n\t\t\t\t.filter((c): c is TextContent => c.type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tthis.box.addChild(\n\t\t\tnew Markdown(text, 0, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"custom-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-message.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAI/D;;;GAGG;AACH,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,cAAc,CAAC,CAAkB;IACzC,OAAO,CAAC,GAAG,CAAc;IACzB,OAAO,CAAC,eAAe,CAAC,CAAY;IACpC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,SAAS,CAAS;IAE1B,YACC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,EAC/B,cAAc,CAAC,EAAE,eAAe,EAChC,aAAa,GAAE,aAAkC,EAajD;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAKnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,OAAO;CAkDf","sourcesContent":["import type { TextContent } from \"@greppleai/grip-ai\";\nimport type { Component } from \"@greppleai/grip-tui\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { MessageRenderer } from \"../../../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\n/**\n * Component that renders a custom message entry from extensions.\n * Uses distinct styling to differentiate from user messages.\n */\nexport class CustomMessageComponent extends Container {\n\tprivate message: CustomMessage<unknown>;\n\tprivate customRenderer?: MessageRenderer;\n\tprivate box: MessageCard;\n\tprivate customComponent?: Component;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate _expanded = false;\n\n\tconstructor(\n\t\tmessage: CustomMessage<unknown>,\n\t\tcustomRenderer?: MessageRenderer,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t) {\n\t\tsuper();\n\t\tthis.message = message;\n\t\tthis.customRenderer = customRenderer;\n\t\tthis.markdownTheme = markdownTheme;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// State summaries keep their success-colored label without coloring the whole card.\n\t\tthis.box = new MessageCard(1, 1);\n\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this._expanded !== expanded) {\n\t\t\tthis._expanded = expanded;\n\t\t\tthis.rebuild();\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\t// Remove previous content component\n\t\tif (this.customComponent) {\n\t\t\tthis.removeChild(this.customComponent);\n\t\t\tthis.customComponent = undefined;\n\t\t}\n\t\tthis.removeChild(this.box);\n\n\t\t// Try custom renderer first - it handles its own styling\n\t\tif (this.customRenderer) {\n\t\t\ttry {\n\t\t\t\tconst component = this.customRenderer(this.message, { expanded: this._expanded }, theme);\n\t\t\t\tif (component) {\n\t\t\t\t\t// Custom renderer provides its own styled component\n\t\t\t\t\tthis.customComponent = component;\n\t\t\t\t\tthis.addChild(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through to default rendering\n\t\t\t}\n\t\t}\n\n\t\t// Default rendering uses our box\n\t\tthis.addChild(this.box);\n\t\tthis.box.clear();\n\n\t\t// Default rendering: label + content\n\t\tconst labelColor = this.message.customType === \"compress_context\" ? \"success\" : \"customMessageLabel\";\n\t\tconst label = theme.fg(labelColor, `\\x1b[1m[${this.message.customType}]\\x1b[22m`);\n\t\tthis.box.addChild(new Text(label, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\t// Extract text content\n\t\tlet text: string;\n\t\tif (typeof this.message.content === \"string\") {\n\t\t\ttext = this.message.content;\n\t\t} else {\n\t\t\ttext = this.message.content\n\t\t\t\t.filter((c): c is TextContent => c.type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tthis.box.addChild(\n\t\t\tnew Markdown(text, 0, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
@@ -1,5 +1,6 @@
1
- import { Box, Container, Markdown, Spacer, Text } from "@greppleai/grip-tui";
1
+ import { Container, Markdown, Spacer, Text } from "@greppleai/grip-tui";
2
2
  import { getMarkdownTheme, theme } from "../theme/theme.js";
3
+ import { MessageCard } from "./message-card.js";
3
4
  /**
4
5
  * Component that renders a custom message entry from extensions.
5
6
  * Uses distinct styling to differentiate from user messages.
@@ -17,8 +18,8 @@ export class CustomMessageComponent extends Container {
17
18
  this.customRenderer = customRenderer;
18
19
  this.markdownTheme = markdownTheme;
19
20
  this.addChild(new Spacer(1));
20
- // State summaries use the success palette to distinguish them from other custom messages.
21
- this.box = new Box(1, 1, (t) => theme.bg(this.message.customType === "compress_context" ? "toolSuccessBg" : "customMessageBg", t));
21
+ // State summaries keep their success-colored label without coloring the whole card.
22
+ this.box = new MessageCard(1, 1);
22
23
  this.rebuild();
23
24
  }
24
25
  setExpanded(expanded) {
@@ -1 +1 @@
1
- {"version":3,"file":"custom-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TextContent } from \"@greppleai/grip-ai\";\nimport type { Component } from \"@greppleai/grip-tui\";\nimport { Box, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { MessageRenderer } from \"../../../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\n\n/**\n * Component that renders a custom message entry from extensions.\n * Uses distinct styling to differentiate from user messages.\n */\nexport class CustomMessageComponent extends Container {\n\tprivate message: CustomMessage<unknown>;\n\tprivate customRenderer?: MessageRenderer;\n\tprivate box: Box;\n\tprivate customComponent?: Component;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate _expanded = false;\n\n\tconstructor(\n\t\tmessage: CustomMessage<unknown>,\n\t\tcustomRenderer?: MessageRenderer,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t) {\n\t\tsuper();\n\t\tthis.message = message;\n\t\tthis.customRenderer = customRenderer;\n\t\tthis.markdownTheme = markdownTheme;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// State summaries use the success palette to distinguish them from other custom messages.\n\t\tthis.box = new Box(1, 1, (t) =>\n\t\t\ttheme.bg(this.message.customType === \"compress_context\" ? \"toolSuccessBg\" : \"customMessageBg\", t),\n\t\t);\n\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this._expanded !== expanded) {\n\t\t\tthis._expanded = expanded;\n\t\t\tthis.rebuild();\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\t// Remove previous content component\n\t\tif (this.customComponent) {\n\t\t\tthis.removeChild(this.customComponent);\n\t\t\tthis.customComponent = undefined;\n\t\t}\n\t\tthis.removeChild(this.box);\n\n\t\t// Try custom renderer first - it handles its own styling\n\t\tif (this.customRenderer) {\n\t\t\ttry {\n\t\t\t\tconst component = this.customRenderer(this.message, { expanded: this._expanded }, theme);\n\t\t\t\tif (component) {\n\t\t\t\t\t// Custom renderer provides its own styled component\n\t\t\t\t\tthis.customComponent = component;\n\t\t\t\t\tthis.addChild(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through to default rendering\n\t\t\t}\n\t\t}\n\n\t\t// Default rendering uses our box\n\t\tthis.addChild(this.box);\n\t\tthis.box.clear();\n\n\t\t// Default rendering: label + content\n\t\tconst labelColor = this.message.customType === \"compress_context\" ? \"success\" : \"customMessageLabel\";\n\t\tconst label = theme.fg(labelColor, `\\x1b[1m[${this.message.customType}]\\x1b[22m`);\n\t\tthis.box.addChild(new Text(label, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\t// Extract text content\n\t\tlet text: string;\n\t\tif (typeof this.message.content === \"string\") {\n\t\t\ttext = this.message.content;\n\t\t} else {\n\t\t\ttext = this.message.content\n\t\t\t\t.filter((c): c is TextContent => c.type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tthis.box.addChild(\n\t\t\tnew Markdown(text, 0, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"custom-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/custom-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TextContent } from \"@greppleai/grip-ai\";\nimport type { Component } from \"@greppleai/grip-tui\";\nimport { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { MessageRenderer } from \"../../../core/extensions/types.ts\";\nimport type { CustomMessage } from \"../../../core/messages.ts\";\nimport { getMarkdownTheme, theme } from \"../theme/theme.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\n/**\n * Component that renders a custom message entry from extensions.\n * Uses distinct styling to differentiate from user messages.\n */\nexport class CustomMessageComponent extends Container {\n\tprivate message: CustomMessage<unknown>;\n\tprivate customRenderer?: MessageRenderer;\n\tprivate box: MessageCard;\n\tprivate customComponent?: Component;\n\tprivate markdownTheme: MarkdownTheme;\n\tprivate _expanded = false;\n\n\tconstructor(\n\t\tmessage: CustomMessage<unknown>,\n\t\tcustomRenderer?: MessageRenderer,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t) {\n\t\tsuper();\n\t\tthis.message = message;\n\t\tthis.customRenderer = customRenderer;\n\t\tthis.markdownTheme = markdownTheme;\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// State summaries keep their success-colored label without coloring the whole card.\n\t\tthis.box = new MessageCard(1, 1);\n\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this._expanded !== expanded) {\n\t\t\tthis._expanded = expanded;\n\t\t\tthis.rebuild();\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\t// Remove previous content component\n\t\tif (this.customComponent) {\n\t\t\tthis.removeChild(this.customComponent);\n\t\t\tthis.customComponent = undefined;\n\t\t}\n\t\tthis.removeChild(this.box);\n\n\t\t// Try custom renderer first - it handles its own styling\n\t\tif (this.customRenderer) {\n\t\t\ttry {\n\t\t\t\tconst component = this.customRenderer(this.message, { expanded: this._expanded }, theme);\n\t\t\t\tif (component) {\n\t\t\t\t\t// Custom renderer provides its own styled component\n\t\t\t\t\tthis.customComponent = component;\n\t\t\t\t\tthis.addChild(component);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Fall through to default rendering\n\t\t\t}\n\t\t}\n\n\t\t// Default rendering uses our box\n\t\tthis.addChild(this.box);\n\t\tthis.box.clear();\n\n\t\t// Default rendering: label + content\n\t\tconst labelColor = this.message.customType === \"compress_context\" ? \"success\" : \"customMessageLabel\";\n\t\tconst label = theme.fg(labelColor, `\\x1b[1m[${this.message.customType}]\\x1b[22m`);\n\t\tthis.box.addChild(new Text(label, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\t// Extract text content\n\t\tlet text: string;\n\t\tif (typeof this.message.content === \"string\") {\n\t\t\ttext = this.message.content;\n\t\t} else {\n\t\t\ttext = this.message.content\n\t\t\t\t.filter((c): c is TextContent => c.type === \"text\")\n\t\t\t\t.map((c) => c.text)\n\t\t\t\t.join(\"\\n\");\n\t\t}\n\n\t\tthis.box.addChild(\n\t\t\tnew Markdown(text, 0, 0, this.markdownTheme, {\n\t\t\t\tcolor: (text: string) => theme.fg(\"customMessageText\", text),\n\t\t\t}),\n\t\t);\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"hook-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/hook-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAO,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AACjG,OAAO,KAAK,EAA2B,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAkGjG,uFAAuF;AACvF,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,MAAM,EAAE,mBAAmB,EAAE,aAAa,GAAE,aAAkC,EAQzF;IAED,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAM9C;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAInC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,sBAAsB;IAuB9B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,oBAAoB;IAoB5B,OAAO,CAAC,aAAa;CAuBrB","sourcesContent":["import { Box, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { HookExecutionCallNotice, HookExecutionNotice } from \"../../../core/hooks/types.ts\";\nimport { getMarkdownTheme, type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { keyHint, keyText } from \"./keybinding-hints.ts\";\n\nconst MAX_CALL_LABEL_CHARS = 300;\n\ninterface HookCallView {\n\tlabel: string;\n\tsource: string;\n\tstatus: string;\n\tstatusColor: ThemeColor;\n}\n\ninterface CountedValue {\n\tvalue: string;\n\tcount: number;\n}\n\ninterface GroupedHookCall {\n\tcall: HookExecutionCallNotice;\n\tinvocationCount: number;\n\ttotalDurationMs: number;\n\tstatuses: CountedValue[];\n}\n\nfunction truncateCallLabel(label: string): string {\n\treturn label.length <= MAX_CALL_LABEL_CHARS ? label : `${label.slice(0, MAX_CALL_LABEL_CHARS - 1)}…`;\n}\n\nfunction hookCallView(call: HookExecutionCallNotice): HookCallView {\n\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\tconst exit = call.exitCode === null ? \"\" : `, exit ${call.exitCode}`;\n\treturn {\n\t\tlabel: `${call.type} ${truncateCallLabel(call.label)}`,\n\t\tsource: `${call.source.kind}: ${call.source.path}`,\n\t\tstatus: `${call.status}${exit}, ${call.durationMs}ms`,\n\t\tstatusColor: successful ? \"success\" : call.status === \"cancelled\" ? \"warning\" : \"error\",\n\t};\n}\n\nfunction callIdentity(call: HookExecutionCallNotice): string {\n\treturn JSON.stringify([call.type, truncateCallLabel(call.label), call.source.kind, call.source.path]);\n}\n\nfunction statusIdentity(call: HookExecutionCallNotice): string {\n\treturn call.exitCode === null ? call.status : `${call.status} (exit ${call.exitCode})`;\n}\n\nfunction countValues(values: Iterable<string>): CountedValue[] {\n\tconst counts = new Map<string, CountedValue>();\n\tfor (const value of values) {\n\t\tconst existing = counts.get(value);\n\t\tif (existing) {\n\t\t\texisting.count += 1;\n\t\t} else {\n\t\t\tcounts.set(value, { value, count: 1 });\n\t\t}\n\t}\n\treturn [...counts.values()];\n}\n\nfunction groupedHookCalls(notices: readonly HookExecutionNotice[]): GroupedHookCall[] {\n\tconst groups = new Map<string, GroupedHookCall>();\n\tfor (const notice of notices) {\n\t\tfor (const call of notice.calls) {\n\t\t\tconst identity = callIdentity(call);\n\t\t\tconst status = statusIdentity(call);\n\t\t\tconst existing = groups.get(identity);\n\t\t\tif (existing) {\n\t\t\t\texisting.invocationCount += 1;\n\t\t\t\texisting.totalDurationMs += call.durationMs;\n\t\t\t\tconst existingStatus = existing.statuses.find((entry) => entry.value === status);\n\t\t\t\tif (existingStatus) existingStatus.count += 1;\n\t\t\t\telse existing.statuses.push({ value: status, count: 1 });\n\t\t\t} else {\n\t\t\t\tgroups.set(identity, {\n\t\t\t\t\tcall,\n\t\t\t\t\tinvocationCount: 1,\n\t\t\t\t\ttotalDurationMs: call.durationMs,\n\t\t\t\t\tstatuses: [{ value: status, count: 1 }],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn [...groups.values()];\n}\n\nfunction groupedCallStatus(group: GroupedHookCall): string {\n\tconst statuses = group.statuses.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\"; \");\n\treturn `${statuses}, ~${(group.totalDurationMs / group.invocationCount).toFixed(1)}ms`;\n}\n\nfunction groupedCallStatusColor(group: GroupedHookCall): ThemeColor {\n\tif (group.statuses.every(({ value }) => value === \"completed\" || value === \"completed (exit 0)\")) return \"success\";\n\tif (group.statuses.every(({ value }) => value.startsWith(\"cancelled\"))) return \"warning\";\n\treturn \"error\";\n}\n\n/** TUI-only card for completed hook calls and their model-visible returned prompts. */\nexport class HookExecutionComponent extends Container {\n\tprivate readonly notices: HookExecutionNotice[];\n\tprivate readonly markdownTheme: MarkdownTheme;\n\tprivate readonly box: Box;\n\tprivate expanded = false;\n\n\tconstructor(notice: HookExecutionNotice, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper();\n\t\tthis.notices = [notice];\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.box = new Box(1, 1, (text: string) => theme.bg(\"selectedBg\", text));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.box);\n\t\tthis.rebuild();\n\t}\n\n\tappendNotice(notice: HookExecutionNotice): void {\n\t\tif (notice.event !== this.notices[0].event) {\n\t\t\tthrow new Error(`Cannot group ${notice.event} notice with ${this.notices[0].event}`);\n\t\t}\n\t\tthis.notices.push(notice);\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this.expanded === expanded) return;\n\t\tthis.expanded = expanded;\n\t\tthis.rebuild();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\tthis.box.clear();\n\t\tconst firstNotice = this.notices[0];\n\t\tif (!this.expanded) {\n\t\t\tthis.renderCollapsedNotices(firstNotice);\n\t\t} else if (this.notices.length === 1) {\n\t\t\tthis.renderSingleNotice(firstNotice);\n\t\t} else {\n\t\t\tthis.renderGroupedNotices(firstNotice);\n\t\t}\n\t}\n\n\tprivate renderCollapsedNotices(firstNotice: HookExecutionNotice): void {\n\t\tconst subjects = countValues(this.notices.flatMap((notice) => (notice.subject ? [notice.subject] : [])));\n\t\tconst subject = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\tconst title = theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`));\n\t\tthis.box.addChild(new Text(title + (subject ? theme.fg(\"muted\", ` · ${subject}`) : \"\"), 0, 0));\n\t\tconst statuses = countValues(\n\t\t\tthis.notices.flatMap((notice) =>\n\t\t\t\tnotice.calls.map((call) => {\n\t\t\t\t\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\t\t\t\t\treturn successful ? \"completed\" : call.status === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tconst summary = statuses\n\t\t\t.map(({ value, count }) => {\n\t\t\t\tconst color = value === \"completed\" ? \"success\" : value === \"cancelled\" ? \"warning\" : \"error\";\n\t\t\t\treturn theme.fg(color, `${count} ${value}`);\n\t\t\t})\n\t\t\t.join(theme.fg(\"muted\", \", \"));\n\t\tthis.box.addChild(new Text(summary || theme.fg(\"muted\", \"0 calls\"), 0, 0));\n\t\tif (keyText(\"app.tools.expand\")) this.box.addChild(new Text(keyHint(\"app.tools.expand\", \"to expand\"), 0, 0));\n\t}\n\n\tprivate renderSingleNotice(notice: HookExecutionNotice): void {\n\t\tconst subject = notice.subject ? theme.fg(\"muted\", ` · ${notice.subject}`) : \"\";\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${notice.event}`)) + subject, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const call of notice.calls.map(hookCallView)) {\n\t\t\tconst status = theme.fg(call.statusColor, call.status);\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(notice.returnedPrompts), false);\n\t}\n\n\tprivate renderGroupedNotices(firstNotice: HookExecutionNotice): void {\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`)), 0, 0));\n\t\tconst subjects = countValues(\n\t\t\tthis.notices.flatMap((notice) => (notice.subject === undefined ? [] : [notice.subject])),\n\t\t);\n\t\tif (subjects.length > 0) {\n\t\t\tconst summary = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Tools: ${summary}`), 0, 0));\n\t\t}\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const group of groupedHookCalls(this.notices)) {\n\t\t\tconst call = hookCallView(group.call);\n\t\t\tconst status = theme.fg(groupedCallStatusColor(group), groupedCallStatus(group));\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(this.notices.flatMap((notice) => notice.returnedPrompts)), true);\n\t}\n\n\tprivate renderPrompts(prompts: readonly CountedValue[], grouped: boolean): void {\n\t\tif (prompts.length === 0) return;\n\t\tthis.box.addChild(new Spacer(1));\n\t\tthis.box.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"warning\", theme.bold(prompts.length === 1 && !grouped ? \"Returned prompt\" : \"Returned prompts\")),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tfor (let index = 0; index < prompts.length; index++) {\n\t\t\tconst prompt = prompts[index];\n\t\t\tif (prompts.length > 1 || grouped) {\n\t\t\t\tconst count = prompt.count > 1 ? ` ×${prompt.count}` : \"\";\n\t\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Prompt ${index + 1}${count}`), 0, 0));\n\t\t\t}\n\t\t\tthis.box.addChild(\n\t\t\t\tnew Markdown(prompt.value, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"text\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"hook-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/hook-execution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,qBAAqB,CAAC;AAC5F,OAAO,KAAK,EAA2B,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAmGjG,uFAAuF;AACvF,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAS;IAEzB,YAAY,MAAM,EAAE,mBAAmB,EAAE,aAAa,GAAE,aAAkC,EAYzF;IAED,YAAY,CAAC,MAAM,EAAE,mBAAmB,GAAG,IAAI,CAM9C;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAInC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,OAAO,CAAC,OAAO;IAYf,OAAO,CAAC,sBAAsB;IAuB9B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,oBAAoB;IAoB5B,OAAO,CAAC,aAAa;CAuBrB","sourcesContent":["import { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { HookExecutionCallNotice, HookExecutionNotice } from \"../../../core/hooks/types.ts\";\nimport { getMarkdownTheme, type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { keyHint, keyText } from \"./keybinding-hints.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\nconst MAX_CALL_LABEL_CHARS = 300;\n\ninterface HookCallView {\n\tlabel: string;\n\tsource: string;\n\tstatus: string;\n\tstatusColor: ThemeColor;\n}\n\ninterface CountedValue {\n\tvalue: string;\n\tcount: number;\n}\n\ninterface GroupedHookCall {\n\tcall: HookExecutionCallNotice;\n\tinvocationCount: number;\n\ttotalDurationMs: number;\n\tstatuses: CountedValue[];\n}\n\nfunction truncateCallLabel(label: string): string {\n\treturn label.length <= MAX_CALL_LABEL_CHARS ? label : `${label.slice(0, MAX_CALL_LABEL_CHARS - 1)}…`;\n}\n\nfunction hookCallView(call: HookExecutionCallNotice): HookCallView {\n\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\tconst exit = call.exitCode === null ? \"\" : `, exit ${call.exitCode}`;\n\treturn {\n\t\tlabel: `${call.type} ${truncateCallLabel(call.label)}`,\n\t\tsource: `${call.source.kind}: ${call.source.path}`,\n\t\tstatus: `${call.status}${exit}, ${call.durationMs}ms`,\n\t\tstatusColor: successful ? \"success\" : call.status === \"cancelled\" ? \"warning\" : \"error\",\n\t};\n}\n\nfunction callIdentity(call: HookExecutionCallNotice): string {\n\treturn JSON.stringify([call.type, truncateCallLabel(call.label), call.source.kind, call.source.path]);\n}\n\nfunction statusIdentity(call: HookExecutionCallNotice): string {\n\treturn call.exitCode === null ? call.status : `${call.status} (exit ${call.exitCode})`;\n}\n\nfunction countValues(values: Iterable<string>): CountedValue[] {\n\tconst counts = new Map<string, CountedValue>();\n\tfor (const value of values) {\n\t\tconst existing = counts.get(value);\n\t\tif (existing) {\n\t\t\texisting.count += 1;\n\t\t} else {\n\t\t\tcounts.set(value, { value, count: 1 });\n\t\t}\n\t}\n\treturn [...counts.values()];\n}\n\nfunction groupedHookCalls(notices: readonly HookExecutionNotice[]): GroupedHookCall[] {\n\tconst groups = new Map<string, GroupedHookCall>();\n\tfor (const notice of notices) {\n\t\tfor (const call of notice.calls) {\n\t\t\tconst identity = callIdentity(call);\n\t\t\tconst status = statusIdentity(call);\n\t\t\tconst existing = groups.get(identity);\n\t\t\tif (existing) {\n\t\t\t\texisting.invocationCount += 1;\n\t\t\t\texisting.totalDurationMs += call.durationMs;\n\t\t\t\tconst existingStatus = existing.statuses.find((entry) => entry.value === status);\n\t\t\t\tif (existingStatus) existingStatus.count += 1;\n\t\t\t\telse existing.statuses.push({ value: status, count: 1 });\n\t\t\t} else {\n\t\t\t\tgroups.set(identity, {\n\t\t\t\t\tcall,\n\t\t\t\t\tinvocationCount: 1,\n\t\t\t\t\ttotalDurationMs: call.durationMs,\n\t\t\t\t\tstatuses: [{ value: status, count: 1 }],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn [...groups.values()];\n}\n\nfunction groupedCallStatus(group: GroupedHookCall): string {\n\tconst statuses = group.statuses.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\"; \");\n\treturn `${statuses}, ~${(group.totalDurationMs / group.invocationCount).toFixed(1)}ms`;\n}\n\nfunction groupedCallStatusColor(group: GroupedHookCall): ThemeColor {\n\tif (group.statuses.every(({ value }) => value === \"completed\" || value === \"completed (exit 0)\")) return \"success\";\n\tif (group.statuses.every(({ value }) => value.startsWith(\"cancelled\"))) return \"warning\";\n\treturn \"error\";\n}\n\n/** TUI-only card for completed hook calls and their model-visible returned prompts. */\nexport class HookExecutionComponent extends Container {\n\tprivate readonly notices: HookExecutionNotice[];\n\tprivate readonly markdownTheme: MarkdownTheme;\n\tprivate readonly box: MessageCard;\n\tprivate expanded = false;\n\n\tconstructor(notice: HookExecutionNotice, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper();\n\t\tthis.notices = [notice];\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.box = new MessageCard(\n\t\t\t1,\n\t\t\t1,\n\t\t\tnotice.event === \"Stop\" || notice.event === \"StopFailure\" ? \"warning\" : undefined,\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.box);\n\t\tthis.rebuild();\n\t}\n\n\tappendNotice(notice: HookExecutionNotice): void {\n\t\tif (notice.event !== this.notices[0].event) {\n\t\t\tthrow new Error(`Cannot group ${notice.event} notice with ${this.notices[0].event}`);\n\t\t}\n\t\tthis.notices.push(notice);\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this.expanded === expanded) return;\n\t\tthis.expanded = expanded;\n\t\tthis.rebuild();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\tthis.box.clear();\n\t\tconst firstNotice = this.notices[0];\n\t\tif (!this.expanded) {\n\t\t\tthis.renderCollapsedNotices(firstNotice);\n\t\t} else if (this.notices.length === 1) {\n\t\t\tthis.renderSingleNotice(firstNotice);\n\t\t} else {\n\t\t\tthis.renderGroupedNotices(firstNotice);\n\t\t}\n\t}\n\n\tprivate renderCollapsedNotices(firstNotice: HookExecutionNotice): void {\n\t\tconst subjects = countValues(this.notices.flatMap((notice) => (notice.subject ? [notice.subject] : [])));\n\t\tconst subject = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\tconst title = theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`));\n\t\tthis.box.addChild(new Text(title + (subject ? theme.fg(\"muted\", ` · ${subject}`) : \"\"), 0, 0));\n\t\tconst statuses = countValues(\n\t\t\tthis.notices.flatMap((notice) =>\n\t\t\t\tnotice.calls.map((call) => {\n\t\t\t\t\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\t\t\t\t\treturn successful ? \"completed\" : call.status === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tconst summary = statuses\n\t\t\t.map(({ value, count }) => {\n\t\t\t\tconst color = value === \"completed\" ? \"success\" : value === \"cancelled\" ? \"warning\" : \"error\";\n\t\t\t\treturn theme.fg(color, `${count} ${value}`);\n\t\t\t})\n\t\t\t.join(theme.fg(\"muted\", \", \"));\n\t\tthis.box.addChild(new Text(summary || theme.fg(\"muted\", \"0 calls\"), 0, 0));\n\t\tif (keyText(\"app.tools.expand\")) this.box.addChild(new Text(keyHint(\"app.tools.expand\", \"to expand\"), 0, 0));\n\t}\n\n\tprivate renderSingleNotice(notice: HookExecutionNotice): void {\n\t\tconst subject = notice.subject ? theme.fg(\"muted\", ` · ${notice.subject}`) : \"\";\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${notice.event}`)) + subject, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const call of notice.calls.map(hookCallView)) {\n\t\t\tconst status = theme.fg(call.statusColor, call.status);\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(notice.returnedPrompts), false);\n\t}\n\n\tprivate renderGroupedNotices(firstNotice: HookExecutionNotice): void {\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`)), 0, 0));\n\t\tconst subjects = countValues(\n\t\t\tthis.notices.flatMap((notice) => (notice.subject === undefined ? [] : [notice.subject])),\n\t\t);\n\t\tif (subjects.length > 0) {\n\t\t\tconst summary = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Tools: ${summary}`), 0, 0));\n\t\t}\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const group of groupedHookCalls(this.notices)) {\n\t\t\tconst call = hookCallView(group.call);\n\t\t\tconst status = theme.fg(groupedCallStatusColor(group), groupedCallStatus(group));\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(this.notices.flatMap((notice) => notice.returnedPrompts)), true);\n\t}\n\n\tprivate renderPrompts(prompts: readonly CountedValue[], grouped: boolean): void {\n\t\tif (prompts.length === 0) return;\n\t\tthis.box.addChild(new Spacer(1));\n\t\tthis.box.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"warning\", theme.bold(prompts.length === 1 && !grouped ? \"Returned prompt\" : \"Returned prompts\")),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tfor (let index = 0; index < prompts.length; index++) {\n\t\t\tconst prompt = prompts[index];\n\t\t\tif (prompts.length > 1 || grouped) {\n\t\t\t\tconst count = prompt.count > 1 ? ` ×${prompt.count}` : \"\";\n\t\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Prompt ${index + 1}${count}`), 0, 0));\n\t\t\t}\n\t\t\tthis.box.addChild(\n\t\t\t\tnew Markdown(prompt.value, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"text\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
@@ -1,6 +1,7 @@
1
- import { Box, Container, Markdown, Spacer, Text } from "@greppleai/grip-tui";
1
+ import { Container, Markdown, Spacer, Text } from "@greppleai/grip-tui";
2
2
  import { getMarkdownTheme, theme } from "../theme/theme.js";
3
3
  import { keyHint, keyText } from "./keybinding-hints.js";
4
+ import { MessageCard } from "./message-card.js";
4
5
  const MAX_CALL_LABEL_CHARS = 300;
5
6
  function truncateCallLabel(label) {
6
7
  return label.length <= MAX_CALL_LABEL_CHARS ? label : `${label.slice(0, MAX_CALL_LABEL_CHARS - 1)}…`;
@@ -83,7 +84,7 @@ export class HookExecutionComponent extends Container {
83
84
  super();
84
85
  this.notices = [notice];
85
86
  this.markdownTheme = markdownTheme;
86
- this.box = new Box(1, 1, (text) => theme.bg("selectedBg", text));
87
+ this.box = new MessageCard(1, 1, notice.event === "Stop" || notice.event === "StopFailure" ? "warning" : undefined);
87
88
  this.addChild(new Spacer(1));
88
89
  this.addChild(this.box);
89
90
  this.rebuild();
@@ -1 +1 @@
1
- {"version":3,"file":"hook-execution.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/hook-execution.ts"],"names":[],"mappings":"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","sourcesContent":["import { Box, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { HookExecutionCallNotice, HookExecutionNotice } from \"../../../core/hooks/types.ts\";\nimport { getMarkdownTheme, type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { keyHint, keyText } from \"./keybinding-hints.ts\";\n\nconst MAX_CALL_LABEL_CHARS = 300;\n\ninterface HookCallView {\n\tlabel: string;\n\tsource: string;\n\tstatus: string;\n\tstatusColor: ThemeColor;\n}\n\ninterface CountedValue {\n\tvalue: string;\n\tcount: number;\n}\n\ninterface GroupedHookCall {\n\tcall: HookExecutionCallNotice;\n\tinvocationCount: number;\n\ttotalDurationMs: number;\n\tstatuses: CountedValue[];\n}\n\nfunction truncateCallLabel(label: string): string {\n\treturn label.length <= MAX_CALL_LABEL_CHARS ? label : `${label.slice(0, MAX_CALL_LABEL_CHARS - 1)}…`;\n}\n\nfunction hookCallView(call: HookExecutionCallNotice): HookCallView {\n\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\tconst exit = call.exitCode === null ? \"\" : `, exit ${call.exitCode}`;\n\treturn {\n\t\tlabel: `${call.type} ${truncateCallLabel(call.label)}`,\n\t\tsource: `${call.source.kind}: ${call.source.path}`,\n\t\tstatus: `${call.status}${exit}, ${call.durationMs}ms`,\n\t\tstatusColor: successful ? \"success\" : call.status === \"cancelled\" ? \"warning\" : \"error\",\n\t};\n}\n\nfunction callIdentity(call: HookExecutionCallNotice): string {\n\treturn JSON.stringify([call.type, truncateCallLabel(call.label), call.source.kind, call.source.path]);\n}\n\nfunction statusIdentity(call: HookExecutionCallNotice): string {\n\treturn call.exitCode === null ? call.status : `${call.status} (exit ${call.exitCode})`;\n}\n\nfunction countValues(values: Iterable<string>): CountedValue[] {\n\tconst counts = new Map<string, CountedValue>();\n\tfor (const value of values) {\n\t\tconst existing = counts.get(value);\n\t\tif (existing) {\n\t\t\texisting.count += 1;\n\t\t} else {\n\t\t\tcounts.set(value, { value, count: 1 });\n\t\t}\n\t}\n\treturn [...counts.values()];\n}\n\nfunction groupedHookCalls(notices: readonly HookExecutionNotice[]): GroupedHookCall[] {\n\tconst groups = new Map<string, GroupedHookCall>();\n\tfor (const notice of notices) {\n\t\tfor (const call of notice.calls) {\n\t\t\tconst identity = callIdentity(call);\n\t\t\tconst status = statusIdentity(call);\n\t\t\tconst existing = groups.get(identity);\n\t\t\tif (existing) {\n\t\t\t\texisting.invocationCount += 1;\n\t\t\t\texisting.totalDurationMs += call.durationMs;\n\t\t\t\tconst existingStatus = existing.statuses.find((entry) => entry.value === status);\n\t\t\t\tif (existingStatus) existingStatus.count += 1;\n\t\t\t\telse existing.statuses.push({ value: status, count: 1 });\n\t\t\t} else {\n\t\t\t\tgroups.set(identity, {\n\t\t\t\t\tcall,\n\t\t\t\t\tinvocationCount: 1,\n\t\t\t\t\ttotalDurationMs: call.durationMs,\n\t\t\t\t\tstatuses: [{ value: status, count: 1 }],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn [...groups.values()];\n}\n\nfunction groupedCallStatus(group: GroupedHookCall): string {\n\tconst statuses = group.statuses.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\"; \");\n\treturn `${statuses}, ~${(group.totalDurationMs / group.invocationCount).toFixed(1)}ms`;\n}\n\nfunction groupedCallStatusColor(group: GroupedHookCall): ThemeColor {\n\tif (group.statuses.every(({ value }) => value === \"completed\" || value === \"completed (exit 0)\")) return \"success\";\n\tif (group.statuses.every(({ value }) => value.startsWith(\"cancelled\"))) return \"warning\";\n\treturn \"error\";\n}\n\n/** TUI-only card for completed hook calls and their model-visible returned prompts. */\nexport class HookExecutionComponent extends Container {\n\tprivate readonly notices: HookExecutionNotice[];\n\tprivate readonly markdownTheme: MarkdownTheme;\n\tprivate readonly box: Box;\n\tprivate expanded = false;\n\n\tconstructor(notice: HookExecutionNotice, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper();\n\t\tthis.notices = [notice];\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.box = new Box(1, 1, (text: string) => theme.bg(\"selectedBg\", text));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.box);\n\t\tthis.rebuild();\n\t}\n\n\tappendNotice(notice: HookExecutionNotice): void {\n\t\tif (notice.event !== this.notices[0].event) {\n\t\t\tthrow new Error(`Cannot group ${notice.event} notice with ${this.notices[0].event}`);\n\t\t}\n\t\tthis.notices.push(notice);\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this.expanded === expanded) return;\n\t\tthis.expanded = expanded;\n\t\tthis.rebuild();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\tthis.box.clear();\n\t\tconst firstNotice = this.notices[0];\n\t\tif (!this.expanded) {\n\t\t\tthis.renderCollapsedNotices(firstNotice);\n\t\t} else if (this.notices.length === 1) {\n\t\t\tthis.renderSingleNotice(firstNotice);\n\t\t} else {\n\t\t\tthis.renderGroupedNotices(firstNotice);\n\t\t}\n\t}\n\n\tprivate renderCollapsedNotices(firstNotice: HookExecutionNotice): void {\n\t\tconst subjects = countValues(this.notices.flatMap((notice) => (notice.subject ? [notice.subject] : [])));\n\t\tconst subject = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\tconst title = theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`));\n\t\tthis.box.addChild(new Text(title + (subject ? theme.fg(\"muted\", ` · ${subject}`) : \"\"), 0, 0));\n\t\tconst statuses = countValues(\n\t\t\tthis.notices.flatMap((notice) =>\n\t\t\t\tnotice.calls.map((call) => {\n\t\t\t\t\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\t\t\t\t\treturn successful ? \"completed\" : call.status === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tconst summary = statuses\n\t\t\t.map(({ value, count }) => {\n\t\t\t\tconst color = value === \"completed\" ? \"success\" : value === \"cancelled\" ? \"warning\" : \"error\";\n\t\t\t\treturn theme.fg(color, `${count} ${value}`);\n\t\t\t})\n\t\t\t.join(theme.fg(\"muted\", \", \"));\n\t\tthis.box.addChild(new Text(summary || theme.fg(\"muted\", \"0 calls\"), 0, 0));\n\t\tif (keyText(\"app.tools.expand\")) this.box.addChild(new Text(keyHint(\"app.tools.expand\", \"to expand\"), 0, 0));\n\t}\n\n\tprivate renderSingleNotice(notice: HookExecutionNotice): void {\n\t\tconst subject = notice.subject ? theme.fg(\"muted\", ` · ${notice.subject}`) : \"\";\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${notice.event}`)) + subject, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const call of notice.calls.map(hookCallView)) {\n\t\t\tconst status = theme.fg(call.statusColor, call.status);\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(notice.returnedPrompts), false);\n\t}\n\n\tprivate renderGroupedNotices(firstNotice: HookExecutionNotice): void {\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`)), 0, 0));\n\t\tconst subjects = countValues(\n\t\t\tthis.notices.flatMap((notice) => (notice.subject === undefined ? [] : [notice.subject])),\n\t\t);\n\t\tif (subjects.length > 0) {\n\t\t\tconst summary = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Tools: ${summary}`), 0, 0));\n\t\t}\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const group of groupedHookCalls(this.notices)) {\n\t\t\tconst call = hookCallView(group.call);\n\t\t\tconst status = theme.fg(groupedCallStatusColor(group), groupedCallStatus(group));\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(this.notices.flatMap((notice) => notice.returnedPrompts)), true);\n\t}\n\n\tprivate renderPrompts(prompts: readonly CountedValue[], grouped: boolean): void {\n\t\tif (prompts.length === 0) return;\n\t\tthis.box.addChild(new Spacer(1));\n\t\tthis.box.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"warning\", theme.bold(prompts.length === 1 && !grouped ? \"Returned prompt\" : \"Returned prompts\")),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tfor (let index = 0; index < prompts.length; index++) {\n\t\t\tconst prompt = prompts[index];\n\t\t\tif (prompts.length > 1 || grouped) {\n\t\t\t\tconst count = prompt.count > 1 ? ` ×${prompt.count}` : \"\";\n\t\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Prompt ${index + 1}${count}`), 0, 0));\n\t\t\t}\n\t\t\tthis.box.addChild(\n\t\t\t\tnew Markdown(prompt.value, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"text\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"hook-execution.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/hook-execution.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@greppleai/grip-tui\";\nimport type { HookExecutionCallNotice, HookExecutionNotice } from \"../../../core/hooks/types.ts\";\nimport { getMarkdownTheme, type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { keyHint, keyText } from \"./keybinding-hints.ts\";\nimport { MessageCard } from \"./message-card.ts\";\n\nconst MAX_CALL_LABEL_CHARS = 300;\n\ninterface HookCallView {\n\tlabel: string;\n\tsource: string;\n\tstatus: string;\n\tstatusColor: ThemeColor;\n}\n\ninterface CountedValue {\n\tvalue: string;\n\tcount: number;\n}\n\ninterface GroupedHookCall {\n\tcall: HookExecutionCallNotice;\n\tinvocationCount: number;\n\ttotalDurationMs: number;\n\tstatuses: CountedValue[];\n}\n\nfunction truncateCallLabel(label: string): string {\n\treturn label.length <= MAX_CALL_LABEL_CHARS ? label : `${label.slice(0, MAX_CALL_LABEL_CHARS - 1)}…`;\n}\n\nfunction hookCallView(call: HookExecutionCallNotice): HookCallView {\n\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\tconst exit = call.exitCode === null ? \"\" : `, exit ${call.exitCode}`;\n\treturn {\n\t\tlabel: `${call.type} ${truncateCallLabel(call.label)}`,\n\t\tsource: `${call.source.kind}: ${call.source.path}`,\n\t\tstatus: `${call.status}${exit}, ${call.durationMs}ms`,\n\t\tstatusColor: successful ? \"success\" : call.status === \"cancelled\" ? \"warning\" : \"error\",\n\t};\n}\n\nfunction callIdentity(call: HookExecutionCallNotice): string {\n\treturn JSON.stringify([call.type, truncateCallLabel(call.label), call.source.kind, call.source.path]);\n}\n\nfunction statusIdentity(call: HookExecutionCallNotice): string {\n\treturn call.exitCode === null ? call.status : `${call.status} (exit ${call.exitCode})`;\n}\n\nfunction countValues(values: Iterable<string>): CountedValue[] {\n\tconst counts = new Map<string, CountedValue>();\n\tfor (const value of values) {\n\t\tconst existing = counts.get(value);\n\t\tif (existing) {\n\t\t\texisting.count += 1;\n\t\t} else {\n\t\t\tcounts.set(value, { value, count: 1 });\n\t\t}\n\t}\n\treturn [...counts.values()];\n}\n\nfunction groupedHookCalls(notices: readonly HookExecutionNotice[]): GroupedHookCall[] {\n\tconst groups = new Map<string, GroupedHookCall>();\n\tfor (const notice of notices) {\n\t\tfor (const call of notice.calls) {\n\t\t\tconst identity = callIdentity(call);\n\t\t\tconst status = statusIdentity(call);\n\t\t\tconst existing = groups.get(identity);\n\t\t\tif (existing) {\n\t\t\t\texisting.invocationCount += 1;\n\t\t\t\texisting.totalDurationMs += call.durationMs;\n\t\t\t\tconst existingStatus = existing.statuses.find((entry) => entry.value === status);\n\t\t\t\tif (existingStatus) existingStatus.count += 1;\n\t\t\t\telse existing.statuses.push({ value: status, count: 1 });\n\t\t\t} else {\n\t\t\t\tgroups.set(identity, {\n\t\t\t\t\tcall,\n\t\t\t\t\tinvocationCount: 1,\n\t\t\t\t\ttotalDurationMs: call.durationMs,\n\t\t\t\t\tstatuses: [{ value: status, count: 1 }],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn [...groups.values()];\n}\n\nfunction groupedCallStatus(group: GroupedHookCall): string {\n\tconst statuses = group.statuses.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\"; \");\n\treturn `${statuses}, ~${(group.totalDurationMs / group.invocationCount).toFixed(1)}ms`;\n}\n\nfunction groupedCallStatusColor(group: GroupedHookCall): ThemeColor {\n\tif (group.statuses.every(({ value }) => value === \"completed\" || value === \"completed (exit 0)\")) return \"success\";\n\tif (group.statuses.every(({ value }) => value.startsWith(\"cancelled\"))) return \"warning\";\n\treturn \"error\";\n}\n\n/** TUI-only card for completed hook calls and their model-visible returned prompts. */\nexport class HookExecutionComponent extends Container {\n\tprivate readonly notices: HookExecutionNotice[];\n\tprivate readonly markdownTheme: MarkdownTheme;\n\tprivate readonly box: MessageCard;\n\tprivate expanded = false;\n\n\tconstructor(notice: HookExecutionNotice, markdownTheme: MarkdownTheme = getMarkdownTheme()) {\n\t\tsuper();\n\t\tthis.notices = [notice];\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.box = new MessageCard(\n\t\t\t1,\n\t\t\t1,\n\t\t\tnotice.event === \"Stop\" || notice.event === \"StopFailure\" ? \"warning\" : undefined,\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(this.box);\n\t\tthis.rebuild();\n\t}\n\n\tappendNotice(notice: HookExecutionNotice): void {\n\t\tif (notice.event !== this.notices[0].event) {\n\t\t\tthrow new Error(`Cannot group ${notice.event} notice with ${this.notices[0].event}`);\n\t\t}\n\t\tthis.notices.push(notice);\n\t\tthis.rebuild();\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tif (this.expanded === expanded) return;\n\t\tthis.expanded = expanded;\n\t\tthis.rebuild();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.rebuild();\n\t}\n\n\tprivate rebuild(): void {\n\t\tthis.box.clear();\n\t\tconst firstNotice = this.notices[0];\n\t\tif (!this.expanded) {\n\t\t\tthis.renderCollapsedNotices(firstNotice);\n\t\t} else if (this.notices.length === 1) {\n\t\t\tthis.renderSingleNotice(firstNotice);\n\t\t} else {\n\t\t\tthis.renderGroupedNotices(firstNotice);\n\t\t}\n\t}\n\n\tprivate renderCollapsedNotices(firstNotice: HookExecutionNotice): void {\n\t\tconst subjects = countValues(this.notices.flatMap((notice) => (notice.subject ? [notice.subject] : [])));\n\t\tconst subject = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\tconst title = theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`));\n\t\tthis.box.addChild(new Text(title + (subject ? theme.fg(\"muted\", ` · ${subject}`) : \"\"), 0, 0));\n\t\tconst statuses = countValues(\n\t\t\tthis.notices.flatMap((notice) =>\n\t\t\t\tnotice.calls.map((call) => {\n\t\t\t\t\tconst successful = call.status === \"completed\" && (call.exitCode === 0 || call.exitCode === null);\n\t\t\t\t\treturn successful ? \"completed\" : call.status === \"cancelled\" ? \"cancelled\" : \"failed\";\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tconst summary = statuses\n\t\t\t.map(({ value, count }) => {\n\t\t\t\tconst color = value === \"completed\" ? \"success\" : value === \"cancelled\" ? \"warning\" : \"error\";\n\t\t\t\treturn theme.fg(color, `${count} ${value}`);\n\t\t\t})\n\t\t\t.join(theme.fg(\"muted\", \", \"));\n\t\tthis.box.addChild(new Text(summary || theme.fg(\"muted\", \"0 calls\"), 0, 0));\n\t\tif (keyText(\"app.tools.expand\")) this.box.addChild(new Text(keyHint(\"app.tools.expand\", \"to expand\"), 0, 0));\n\t}\n\n\tprivate renderSingleNotice(notice: HookExecutionNotice): void {\n\t\tconst subject = notice.subject ? theme.fg(\"muted\", ` · ${notice.subject}`) : \"\";\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${notice.event}`)) + subject, 0, 0));\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const call of notice.calls.map(hookCallView)) {\n\t\t\tconst status = theme.fg(call.statusColor, call.status);\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(notice.returnedPrompts), false);\n\t}\n\n\tprivate renderGroupedNotices(firstNotice: HookExecutionNotice): void {\n\t\tthis.box.addChild(new Text(theme.fg(\"warning\", theme.bold(`Hook · ${firstNotice.event}`)), 0, 0));\n\t\tconst subjects = countValues(\n\t\t\tthis.notices.flatMap((notice) => (notice.subject === undefined ? [] : [notice.subject])),\n\t\t);\n\t\tif (subjects.length > 0) {\n\t\t\tconst summary = subjects.map(({ value, count }) => `${value}${count > 1 ? ` ×${count}` : \"\"}`).join(\", \");\n\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Tools: ${summary}`), 0, 0));\n\t\t}\n\t\tthis.box.addChild(new Spacer(1));\n\n\t\tfor (const group of groupedHookCalls(this.notices)) {\n\t\t\tconst call = hookCallView(group.call);\n\t\t\tconst status = theme.fg(groupedCallStatusColor(group), groupedCallStatus(group));\n\t\t\tthis.box.addChild(new Text(`${theme.fg(\"text\", call.label)} ${status}`, 0, 0));\n\t\t\tthis.box.addChild(new Text(theme.fg(\"dim\", call.source), 0, 0));\n\t\t}\n\t\tthis.renderPrompts(countValues(this.notices.flatMap((notice) => notice.returnedPrompts)), true);\n\t}\n\n\tprivate renderPrompts(prompts: readonly CountedValue[], grouped: boolean): void {\n\t\tif (prompts.length === 0) return;\n\t\tthis.box.addChild(new Spacer(1));\n\t\tthis.box.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"warning\", theme.bold(prompts.length === 1 && !grouped ? \"Returned prompt\" : \"Returned prompts\")),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tfor (let index = 0; index < prompts.length; index++) {\n\t\t\tconst prompt = prompts[index];\n\t\t\tif (prompts.length > 1 || grouped) {\n\t\t\t\tconst count = prompt.count > 1 ? ` ×${prompt.count}` : \"\";\n\t\t\t\tthis.box.addChild(new Text(theme.fg(\"muted\", `Prompt ${index + 1}${count}`), 0, 0));\n\t\t\t}\n\t\t\tthis.box.addChild(\n\t\t\t\tnew Markdown(prompt.value, 0, 0, this.markdownTheme, {\n\t\t\t\t\tcolor: (text: string) => theme.fg(\"text\", text),\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import { Box } from "@greppleai/grip-tui";
2
+ import { type ThemeColor } from "../theme/theme.ts";
3
+ /** Message card with internal padding, an optional one-cell stripe, and no trailing spaces. */
4
+ export declare class MessageCard extends Box {
5
+ private readonly stripeColor?;
6
+ constructor(paddingX: number, paddingY: number, stripeColor?: ThemeColor);
7
+ render(width: number): string[];
8
+ }
9
+ //# sourceMappingURL=message-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-card.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/message-card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,KAAK,UAAU,EAAS,MAAM,mBAAmB,CAAC;AAE3D,+FAA+F;AAC/F,qBAAa,WAAY,SAAQ,GAAG;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAa;IAE1C,YAAY,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,UAAU,EAGvE;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAUvC;CACD","sourcesContent":["import { Box } from \"@greppleai/grip-tui\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\n\n/** Message card with internal padding, an optional one-cell stripe, and no trailing spaces. */\nexport class MessageCard extends Box {\n\tprivate readonly stripeColor?: ThemeColor;\n\n\tconstructor(paddingX: number, paddingY: number, stripeColor?: ThemeColor) {\n\t\tsuper(paddingX, paddingY);\n\t\tthis.stripeColor = stripeColor;\n\t}\n\n\toverride render(width: number): string[] {\n\t\tif (!this.stripeColor) return super.render(width).map((line) => line.trimEnd());\n\t\tif (width < 1) return [];\n\n\t\tconst marker = theme.bgFromFg(this.stripeColor, \" \");\n\t\treturn super.render(Math.max(1, width - 2)).map((line) => {\n\t\t\tif (width <= 2) return marker;\n\t\t\tconst content = line.trimEnd();\n\t\t\treturn content ? `${marker} ${content}` : marker;\n\t\t});\n\t}\n}\n"]}