message.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. package chat
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "strings"
  6. "time"
  7. "github.com/charmbracelet/lipgloss"
  8. "github.com/charmbracelet/x/ansi"
  9. "github.com/sst/opencode/internal/config"
  10. "github.com/sst/opencode/internal/diff"
  11. "github.com/sst/opencode/internal/tui/styles"
  12. "github.com/sst/opencode/internal/tui/theme"
  13. "github.com/sst/opencode/pkg/client"
  14. "golang.org/x/text/cases"
  15. "golang.org/x/text/language"
  16. )
  17. const (
  18. maxResultHeight = 10
  19. )
  20. func toMarkdown(content string, width int) string {
  21. r := styles.GetMarkdownRenderer(width)
  22. rendered, _ := r.Render(content)
  23. return strings.TrimSuffix(rendered, "\n")
  24. }
  25. func renderUserMessage(msg client.MessageInfo, width int) string {
  26. t := theme.CurrentTheme()
  27. style := styles.BaseStyle().
  28. BorderLeft(true).
  29. Foreground(t.TextMuted()).
  30. BorderForeground(t.Secondary()).
  31. BorderStyle(lipgloss.ThickBorder())
  32. baseStyle := styles.BaseStyle()
  33. // var styledAttachments []string
  34. // attachmentStyles := baseStyle.
  35. // MarginLeft(1).
  36. // Background(t.TextMuted()).
  37. // Foreground(t.Text())
  38. // for _, attachment := range msg.BinaryContent() {
  39. // file := filepath.Base(attachment.Path)
  40. // var filename string
  41. // if len(file) > 10 {
  42. // filename = fmt.Sprintf(" %s %s...", styles.DocumentIcon, file[0:7])
  43. // } else {
  44. // filename = fmt.Sprintf(" %s %s", styles.DocumentIcon, file)
  45. // }
  46. // styledAttachments = append(styledAttachments, attachmentStyles.Render(filename))
  47. // }
  48. // Add timestamp info
  49. timestamp := time.UnixMilli(int64(msg.Metadata.Time.Created)).Local().Format("02 Jan 2006 03:04 PM")
  50. username, _ := config.GetUsername()
  51. info := baseStyle.
  52. Foreground(t.TextMuted()).
  53. Render(fmt.Sprintf(" %s (%s)", username, timestamp))
  54. content := ""
  55. // if len(styledAttachments) > 0 {
  56. // attachmentContent := baseStyle.Width(width).Render(lipgloss.JoinHorizontal(lipgloss.Left, styledAttachments...))
  57. // content = renderMessage(msg.Content().String(), true, isFocused, width, append(info, attachmentContent)...)
  58. // } else {
  59. for _, p := range msg.Parts {
  60. part, err := p.ValueByDiscriminator()
  61. if err != nil {
  62. continue //TODO: handle error?
  63. }
  64. switch part.(type) {
  65. case client.MessagePartText:
  66. textPart := part.(client.MessagePartText)
  67. text := toMarkdown(textPart.Text, width)
  68. content = style.Render(lipgloss.JoinVertical(lipgloss.Left, text, info))
  69. }
  70. }
  71. return styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
  72. }
  73. func convertToMap(input *any) (map[string]any, bool) {
  74. if input == nil {
  75. return nil, false // Handle nil pointer
  76. }
  77. value := *input // Dereference the pointer to get the interface value
  78. m, ok := value.(map[string]any) // Type assertion
  79. return m, ok
  80. }
  81. func renderAssistantMessage(
  82. msg client.MessageInfo,
  83. width int,
  84. showToolMessages bool,
  85. ) string {
  86. t := theme.CurrentTheme()
  87. style := styles.BaseStyle().
  88. BorderLeft(true).
  89. Foreground(t.TextMuted()).
  90. BorderForeground(t.Primary()).
  91. BorderStyle(lipgloss.ThickBorder())
  92. toolStyle := styles.BaseStyle().
  93. BorderLeft(true).
  94. Foreground(t.TextMuted()).
  95. BorderForeground(t.TextMuted()).
  96. BorderStyle(lipgloss.ThickBorder())
  97. baseStyle := styles.BaseStyle()
  98. messages := []string{}
  99. // content := strings.TrimSpace(msg.Content().String())
  100. // thinking := msg.IsThinking()
  101. // thinkingContent := msg.ReasoningContent().Thinking
  102. // finished := msg.IsFinished()
  103. // finishData := msg.FinishPart()
  104. // Add timestamp info
  105. timestamp := time.UnixMilli(int64(msg.Metadata.Time.Created)).Local().Format("02 Jan 2006 03:04 PM")
  106. modelName := msg.Metadata.Assistant.ModelID
  107. info := baseStyle.
  108. Foreground(t.TextMuted()).
  109. Render(fmt.Sprintf(" %s (%s)", modelName, timestamp))
  110. for _, p := range msg.Parts {
  111. part, err := p.ValueByDiscriminator()
  112. if err != nil {
  113. continue //TODO: handle error?
  114. }
  115. switch part.(type) {
  116. case client.MessagePartText:
  117. textPart := part.(client.MessagePartText)
  118. text := toMarkdown(textPart.Text, width)
  119. content := style.Render(lipgloss.JoinVertical(lipgloss.Left, text, info))
  120. message := styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
  121. messages = append(messages, message)
  122. case client.MessagePartToolInvocation:
  123. if !showToolMessages {
  124. continue
  125. }
  126. toolInvocationPart := part.(client.MessagePartToolInvocation)
  127. toolInvocation, _ := toolInvocationPart.ToolInvocation.ValueByDiscriminator()
  128. switch toolInvocation.(type) {
  129. case client.MessageToolInvocationToolCall:
  130. toolCall := toolInvocation.(client.MessageToolInvocationToolCall)
  131. toolName := renderToolName(toolCall.ToolName)
  132. var toolArgs []string
  133. toolMap, _ := convertToMap(toolCall.Args)
  134. for _, arg := range toolMap {
  135. toolArgs = append(toolArgs, fmt.Sprintf("%v", arg))
  136. }
  137. params := renderParams(width-lipgloss.Width(toolName)-1, toolArgs...)
  138. title := styles.Padded().Render(fmt.Sprintf("%s: %s", toolName, params))
  139. content := toolStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
  140. title,
  141. " In progress...",
  142. ))
  143. message := styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
  144. messages = append(messages, message)
  145. case client.MessageToolInvocationToolResult:
  146. toolInvocationResult := toolInvocation.(client.MessageToolInvocationToolResult)
  147. toolName := renderToolName(toolInvocationResult.ToolName)
  148. var toolArgs []string
  149. toolMap, _ := convertToMap(toolInvocationResult.Args)
  150. for _, arg := range toolMap {
  151. toolArgs = append(toolArgs, fmt.Sprintf("%v", arg))
  152. }
  153. params := renderParams(width-lipgloss.Width(toolName)-1, toolArgs...)
  154. title := styles.Padded().Render(fmt.Sprintf("%s: %s", toolName, params))
  155. metadata := msg.Metadata.Tool[toolInvocationResult.ToolCallId].(map[string]any)
  156. var markdown string
  157. if toolInvocationResult.ToolName == "edit" {
  158. filename := toolMap["filePath"].(string)
  159. title = styles.Padded().Render(fmt.Sprintf("%s: %s", toolName, filename))
  160. oldString := toolMap["oldString"].(string)
  161. newString := toolMap["newString"].(string)
  162. patch, _, _ := diff.GenerateDiff(oldString, newString, filename)
  163. formattedDiff, _ := diff.FormatDiff(patch, diff.WithTotalWidth(width))
  164. markdown = strings.TrimSpace(formattedDiff)
  165. message := toolStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
  166. title,
  167. markdown,
  168. ))
  169. messages = append(messages, message)
  170. } else if toolInvocationResult.ToolName == "view" {
  171. result := toolInvocationResult.Result
  172. if metadata["preview"] != nil {
  173. result = metadata["preview"].(string)
  174. }
  175. filename := toolMap["filePath"].(string)
  176. ext := filepath.Ext(filename)
  177. if ext == "" {
  178. ext = ""
  179. } else {
  180. ext = strings.ToLower(ext[1:])
  181. }
  182. result = fmt.Sprintf("```%s\n%s\n```", ext, truncateHeight(result, 10))
  183. markdown = toMarkdown(result, width)
  184. content := toolStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
  185. title,
  186. markdown,
  187. ))
  188. message := styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
  189. messages = append(messages, message)
  190. } else {
  191. result := truncateHeight(strings.TrimSpace(toolInvocationResult.Result), 10)
  192. markdown = toMarkdown(result, width)
  193. content := toolStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
  194. title,
  195. markdown,
  196. ))
  197. message := styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
  198. messages = append(messages, message)
  199. }
  200. }
  201. }
  202. }
  203. // if finished {
  204. // // Add finish info if available
  205. // switch finishData.Reason {
  206. // case message.FinishReasonCanceled:
  207. // info = append(info, baseStyle.
  208. // Width(width-1).
  209. // Foreground(t.Warning()).
  210. // Render("(canceled)"),
  211. // )
  212. // case message.FinishReasonError:
  213. // info = append(info, baseStyle.
  214. // Width(width-1).
  215. // Foreground(t.Error()).
  216. // Render("(error)"),
  217. // )
  218. // case message.FinishReasonPermissionDenied:
  219. // info = append(info, baseStyle.
  220. // Width(width-1).
  221. // Foreground(t.Info()).
  222. // Render("(permission denied)"),
  223. // )
  224. // }
  225. // }
  226. // if content != "" || (finished && finishData.Reason == message.FinishReasonEndTurn) {
  227. // if content == "" {
  228. // content = "*Finished without output*"
  229. // }
  230. //
  231. // content = renderMessage(content, false, width, info...)
  232. // messages = append(messages, content)
  233. // // position += messages[0].height
  234. // position++ // for the space
  235. // } else if thinking && thinkingContent != "" {
  236. // // Render the thinking content with timestamp
  237. // content = renderMessage(thinkingContent, false, width, info...)
  238. // messages = append(messages, content)
  239. // position += lipgloss.Height(content)
  240. // position++ // for the space
  241. // }
  242. // Only render tool messages if they should be shown
  243. if showToolMessages {
  244. // for i, toolCall := range msg.ToolCalls() {
  245. // toolCallContent := renderToolMessage(
  246. // toolCall,
  247. // allMessages,
  248. // messagesService,
  249. // focusedUIMessageId,
  250. // false,
  251. // width,
  252. // i+1,
  253. // )
  254. // messages = append(messages, toolCallContent)
  255. // }
  256. }
  257. return strings.Join(messages, "\n\n")
  258. }
  259. func renderToolName(name string) string {
  260. switch name {
  261. // case agent.AgentToolName:
  262. // return "Task"
  263. case "ls":
  264. return "List"
  265. default:
  266. return cases.Title(language.Und).String(name)
  267. }
  268. }
  269. func renderToolAction(name string) string {
  270. switch name {
  271. // case agent.AgentToolName:
  272. // return "Preparing prompt..."
  273. case "bash":
  274. return "Building command..."
  275. case "edit":
  276. return "Preparing edit..."
  277. case "fetch":
  278. return "Writing fetch..."
  279. case "glob":
  280. return "Finding files..."
  281. case "grep":
  282. return "Searching content..."
  283. case "ls":
  284. return "Listing directory..."
  285. case "view":
  286. return "Reading file..."
  287. case "write":
  288. return "Preparing write..."
  289. case "patch":
  290. return "Preparing patch..."
  291. case "batch":
  292. return "Running batch operations..."
  293. }
  294. return "Working..."
  295. }
  296. // renders params, params[0] (params[1]=params[2] ....)
  297. func renderParams(paramsWidth int, params ...string) string {
  298. if len(params) == 0 {
  299. return ""
  300. }
  301. mainParam := params[0]
  302. if len(mainParam) > paramsWidth {
  303. mainParam = mainParam[:paramsWidth-3] + "..."
  304. }
  305. if len(params) == 1 {
  306. return mainParam
  307. }
  308. otherParams := params[1:]
  309. // create pairs of key/value
  310. // if odd number of params, the last one is a key without value
  311. if len(otherParams)%2 != 0 {
  312. otherParams = append(otherParams, "")
  313. }
  314. parts := make([]string, 0, len(otherParams)/2)
  315. for i := 0; i < len(otherParams); i += 2 {
  316. key := otherParams[i]
  317. value := otherParams[i+1]
  318. if value == "" {
  319. continue
  320. }
  321. parts = append(parts, fmt.Sprintf("%s=%s", key, value))
  322. }
  323. partsRendered := strings.Join(parts, ", ")
  324. remainingWidth := paramsWidth - lipgloss.Width(partsRendered) - 5 // for the space
  325. if remainingWidth < 30 {
  326. // No space for the params, just show the main
  327. return mainParam
  328. }
  329. if len(parts) > 0 {
  330. mainParam = fmt.Sprintf("%s (%s)", mainParam, strings.Join(parts, ", "))
  331. }
  332. return ansi.Truncate(mainParam, paramsWidth, "...")
  333. }
  334. func removeWorkingDirPrefix(path string) string {
  335. wd := config.WorkingDirectory()
  336. if strings.HasPrefix(path, wd) {
  337. path = strings.TrimPrefix(path, wd)
  338. }
  339. if strings.HasPrefix(path, "/") {
  340. path = strings.TrimPrefix(path, "/")
  341. }
  342. if strings.HasPrefix(path, "./") {
  343. path = strings.TrimPrefix(path, "./")
  344. }
  345. if strings.HasPrefix(path, "../") {
  346. path = strings.TrimPrefix(path, "../")
  347. }
  348. return path
  349. }
  350. func renderToolParams(paramWidth int, toolCall any) string {
  351. params := ""
  352. switch toolCall {
  353. // // case agent.AgentToolName:
  354. // // var params agent.AgentParams
  355. // // json.Unmarshal([]byte(toolCall.Input), &params)
  356. // // prompt := strings.ReplaceAll(params.Prompt, "\n", " ")
  357. // // return renderParams(paramWidth, prompt)
  358. // case "bash":
  359. // var params tools.BashParams
  360. // json.Unmarshal([]byte(toolCall.Input), &params)
  361. // command := strings.ReplaceAll(params.Command, "\n", " ")
  362. // return renderParams(paramWidth, command)
  363. // case "edit":
  364. // var params tools.EditParams
  365. // json.Unmarshal([]byte(toolCall.Input), &params)
  366. // filePath := removeWorkingDirPrefix(params.FilePath)
  367. // return renderParams(paramWidth, filePath)
  368. // case "fetch":
  369. // var params tools.FetchParams
  370. // json.Unmarshal([]byte(toolCall.Input), &params)
  371. // url := params.URL
  372. // toolParams := []string{
  373. // url,
  374. // }
  375. // if params.Format != "" {
  376. // toolParams = append(toolParams, "format", params.Format)
  377. // }
  378. // if params.Timeout != 0 {
  379. // toolParams = append(toolParams, "timeout", (time.Duration(params.Timeout) * time.Second).String())
  380. // }
  381. // return renderParams(paramWidth, toolParams...)
  382. // case tools.GlobToolName:
  383. // var params tools.GlobParams
  384. // json.Unmarshal([]byte(toolCall.Input), &params)
  385. // pattern := params.Pattern
  386. // toolParams := []string{
  387. // pattern,
  388. // }
  389. // if params.Path != "" {
  390. // toolParams = append(toolParams, "path", params.Path)
  391. // }
  392. // return renderParams(paramWidth, toolParams...)
  393. // case tools.GrepToolName:
  394. // var params tools.GrepParams
  395. // json.Unmarshal([]byte(toolCall.Input), &params)
  396. // pattern := params.Pattern
  397. // toolParams := []string{
  398. // pattern,
  399. // }
  400. // if params.Path != "" {
  401. // toolParams = append(toolParams, "path", params.Path)
  402. // }
  403. // if params.Include != "" {
  404. // toolParams = append(toolParams, "include", params.Include)
  405. // }
  406. // if params.LiteralText {
  407. // toolParams = append(toolParams, "literal", "true")
  408. // }
  409. // return renderParams(paramWidth, toolParams...)
  410. // case tools.LSToolName:
  411. // var params tools.LSParams
  412. // json.Unmarshal([]byte(toolCall.Input), &params)
  413. // path := params.Path
  414. // if path == "" {
  415. // path = "."
  416. // }
  417. // return renderParams(paramWidth, path)
  418. // case tools.ViewToolName:
  419. // var params tools.ViewParams
  420. // json.Unmarshal([]byte(toolCall.Input), &params)
  421. // filePath := removeWorkingDirPrefix(params.FilePath)
  422. // toolParams := []string{
  423. // filePath,
  424. // }
  425. // if params.Limit != 0 {
  426. // toolParams = append(toolParams, "limit", fmt.Sprintf("%d", params.Limit))
  427. // }
  428. // if params.Offset != 0 {
  429. // toolParams = append(toolParams, "offset", fmt.Sprintf("%d", params.Offset))
  430. // }
  431. // return renderParams(paramWidth, toolParams...)
  432. // case tools.WriteToolName:
  433. // var params tools.WriteParams
  434. // json.Unmarshal([]byte(toolCall.Input), &params)
  435. // filePath := removeWorkingDirPrefix(params.FilePath)
  436. // return renderParams(paramWidth, filePath)
  437. // case tools.BatchToolName:
  438. // var params tools.BatchParams
  439. // json.Unmarshal([]byte(toolCall.Input), &params)
  440. // return renderParams(paramWidth, fmt.Sprintf("%d parallel calls", len(params.Calls)))
  441. // default:
  442. // input := strings.ReplaceAll(toolCall, "\n", " ")
  443. // params = renderParams(paramWidth, input)
  444. }
  445. return params
  446. }
  447. func truncateHeight(content string, height int) string {
  448. lines := strings.Split(content, "\n")
  449. if len(lines) > height {
  450. return strings.Join(lines[:height], "\n")
  451. }
  452. return content
  453. }
  454. func renderToolResponse(toolCall any, response any, width int) string {
  455. return ""
  456. // t := theme.CurrentTheme()
  457. // baseStyle := styles.BaseStyle()
  458. //
  459. // if response.IsError {
  460. // errContent := fmt.Sprintf("Error: %s", strings.ReplaceAll(response.Content, "\n", " "))
  461. // errContent = ansi.Truncate(errContent, width-1, "...")
  462. // return baseStyle.
  463. // Width(width).
  464. // Foreground(t.Error()).
  465. // Render(errContent)
  466. // }
  467. //
  468. // resultContent := truncateHeight(response.Content, maxResultHeight)
  469. // switch toolCall.Name {
  470. // case agent.AgentToolName:
  471. // return styles.ForceReplaceBackgroundWithLipgloss(
  472. // toMarkdown(resultContent, false, width),
  473. // t.Background(),
  474. // )
  475. // case tools.BashToolName:
  476. // resultContent = fmt.Sprintf("```bash\n%s\n```", resultContent)
  477. // return styles.ForceReplaceBackgroundWithLipgloss(
  478. // toMarkdown(resultContent, width),
  479. // t.Background(),
  480. // )
  481. // case tools.EditToolName:
  482. // metadata := tools.EditResponseMetadata{}
  483. // json.Unmarshal([]byte(response.Metadata), &metadata)
  484. // formattedDiff, _ := diff.FormatDiff(metadata.Diff, diff.WithTotalWidth(width))
  485. // return formattedDiff
  486. // case tools.FetchToolName:
  487. // var params tools.FetchParams
  488. // json.Unmarshal([]byte(toolCall.Input), &params)
  489. // mdFormat := "markdown"
  490. // switch params.Format {
  491. // case "text":
  492. // mdFormat = "text"
  493. // case "html":
  494. // mdFormat = "html"
  495. // }
  496. // resultContent = fmt.Sprintf("```%s\n%s\n```", mdFormat, resultContent)
  497. // return styles.ForceReplaceBackgroundWithLipgloss(
  498. // toMarkdown(resultContent, width),
  499. // t.Background(),
  500. // )
  501. // case tools.GlobToolName:
  502. // return baseStyle.Width(width).Foreground(t.TextMuted()).Render(resultContent)
  503. // case tools.GrepToolName:
  504. // return baseStyle.Width(width).Foreground(t.TextMuted()).Render(resultContent)
  505. // case tools.LSToolName:
  506. // return baseStyle.Width(width).Foreground(t.TextMuted()).Render(resultContent)
  507. // case tools.ViewToolName:
  508. // metadata := tools.ViewResponseMetadata{}
  509. // json.Unmarshal([]byte(response.Metadata), &metadata)
  510. // ext := filepath.Ext(metadata.FilePath)
  511. // if ext == "" {
  512. // ext = ""
  513. // } else {
  514. // ext = strings.ToLower(ext[1:])
  515. // }
  516. // resultContent = fmt.Sprintf("```%s\n%s\n```", ext, truncateHeight(metadata.Content, maxResultHeight))
  517. // return styles.ForceReplaceBackgroundWithLipgloss(
  518. // toMarkdown(resultContent, width),
  519. // t.Background(),
  520. // )
  521. // case tools.WriteToolName:
  522. // params := tools.WriteParams{}
  523. // json.Unmarshal([]byte(toolCall.Input), &params)
  524. // metadata := tools.WriteResponseMetadata{}
  525. // json.Unmarshal([]byte(response.Metadata), &metadata)
  526. // ext := filepath.Ext(params.FilePath)
  527. // if ext == "" {
  528. // ext = ""
  529. // } else {
  530. // ext = strings.ToLower(ext[1:])
  531. // }
  532. // resultContent = fmt.Sprintf("```%s\n%s\n```", ext, truncateHeight(params.Content, maxResultHeight))
  533. // return styles.ForceReplaceBackgroundWithLipgloss(
  534. // toMarkdown(resultContent, width),
  535. // t.Background(),
  536. // )
  537. // case tools.BatchToolName:
  538. // var batchResult tools.BatchResult
  539. // if err := json.Unmarshal([]byte(resultContent), &batchResult); err != nil {
  540. // return baseStyle.Width(width).Foreground(t.Error()).Render(fmt.Sprintf("Error parsing batch result: %s", err))
  541. // }
  542. //
  543. // var toolCalls []string
  544. // for i, result := range batchResult.Results {
  545. // toolName := renderToolName(result.ToolName)
  546. //
  547. // // Format the tool input as a string
  548. // inputStr := string(result.ToolInput)
  549. //
  550. // // Format the result
  551. // var resultStr string
  552. // if result.Error != "" {
  553. // resultStr = fmt.Sprintf("Error: %s", result.Error)
  554. // } else {
  555. // var toolResponse tools.ToolResponse
  556. // if err := json.Unmarshal(result.Result, &toolResponse); err != nil {
  557. // resultStr = "Error parsing tool response"
  558. // } else {
  559. // resultStr = truncateHeight(toolResponse.Content, 3)
  560. // }
  561. // }
  562. //
  563. // // Format the tool call
  564. // toolCall := fmt.Sprintf("%d. %s: %s\n %s", i+1, toolName, inputStr, resultStr)
  565. // toolCalls = append(toolCalls, toolCall)
  566. // }
  567. //
  568. // return baseStyle.Width(width).Foreground(t.TextMuted()).Render(strings.Join(toolCalls, "\n\n"))
  569. // default:
  570. // resultContent = fmt.Sprintf("```text\n%s\n```", resultContent)
  571. // return styles.ForceReplaceBackgroundWithLipgloss(
  572. // toMarkdown(resultContent, width),
  573. // t.Background(),
  574. // )
  575. // }
  576. }
  577. // func renderToolMessage(
  578. // toolCall message.ToolCall,
  579. // allMessages []message.Message,
  580. // messagesService message.Service,
  581. // focusedUIMessageId string,
  582. // nested bool,
  583. // width int,
  584. // position int,
  585. // ) string {
  586. // if nested {
  587. // width = width - 3
  588. // }
  589. //
  590. // t := theme.CurrentTheme()
  591. // baseStyle := styles.BaseStyle()
  592. //
  593. // style := baseStyle.
  594. // Width(width - 1).
  595. // BorderLeft(true).
  596. // BorderStyle(lipgloss.ThickBorder()).
  597. // PaddingLeft(1).
  598. // BorderForeground(t.TextMuted())
  599. //
  600. // response := findToolResponse(toolCall.ID, allMessages)
  601. // toolNameText := baseStyle.Foreground(t.TextMuted()).
  602. // Render(fmt.Sprintf("%s: ", renderToolName(toolCall.Name)))
  603. //
  604. // if !toolCall.Finished {
  605. // // Get a brief description of what the tool is doing
  606. // toolAction := renderToolAction(toolCall.Name)
  607. //
  608. // progressText := baseStyle.
  609. // Width(width - 2 - lipgloss.Width(toolNameText)).
  610. // Foreground(t.TextMuted()).
  611. // Render(fmt.Sprintf("%s", toolAction))
  612. //
  613. // content := style.Render(lipgloss.JoinHorizontal(lipgloss.Left, toolNameText, progressText))
  614. // return content
  615. // }
  616. //
  617. // params := renderToolParams(width-1-lipgloss.Width(toolNameText), toolCall)
  618. // responseContent := ""
  619. // if response != nil {
  620. // responseContent = renderToolResponse(toolCall, *response, width-2)
  621. // responseContent = strings.TrimSuffix(responseContent, "\n")
  622. // } else {
  623. // responseContent = baseStyle.
  624. // Italic(true).
  625. // Width(width - 2).
  626. // Foreground(t.TextMuted()).
  627. // Render("Waiting for response...")
  628. // }
  629. //
  630. // parts := []string{}
  631. // if !nested {
  632. // formattedParams := baseStyle.
  633. // Width(width - 2 - lipgloss.Width(toolNameText)).
  634. // Foreground(t.TextMuted()).
  635. // Render(params)
  636. //
  637. // parts = append(parts, lipgloss.JoinHorizontal(lipgloss.Left, toolNameText, formattedParams))
  638. // } else {
  639. // prefix := baseStyle.
  640. // Foreground(t.TextMuted()).
  641. // Render(" └ ")
  642. // formattedParams := baseStyle.
  643. // Width(width - 2 - lipgloss.Width(toolNameText)).
  644. // Foreground(t.TextMuted()).
  645. // Render(params)
  646. // parts = append(parts, lipgloss.JoinHorizontal(lipgloss.Left, prefix, toolNameText, formattedParams))
  647. // }
  648. //
  649. // // if toolCall.Name == agent.AgentToolName {
  650. // // taskMessages, _ := messagesService.List(context.Background(), toolCall.ID)
  651. // // toolCalls := []message.ToolCall{}
  652. // // for _, v := range taskMessages {
  653. // // toolCalls = append(toolCalls, v.ToolCalls()...)
  654. // // }
  655. // // for _, call := range toolCalls {
  656. // // rendered := renderToolMessage(call, []message.Message{}, messagesService, focusedUIMessageId, true, width, 0)
  657. // // parts = append(parts, rendered.content)
  658. // // }
  659. // // }
  660. // if responseContent != "" && !nested {
  661. // parts = append(parts, responseContent)
  662. // }
  663. //
  664. // content := style.Render(
  665. // lipgloss.JoinVertical(
  666. // lipgloss.Left,
  667. // parts...,
  668. // ),
  669. // )
  670. // if nested {
  671. // content = lipgloss.JoinVertical(
  672. // lipgloss.Left,
  673. // parts...,
  674. // )
  675. // }
  676. // return content
  677. // }