@nextclaw/ui 0.14.0 → 0.14.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/assets/{api-B50yVdT8.js → api-dQ6uQSZG.js} +3 -3
  3. package/dist/assets/channels-list-page-DfpjB8XB.js +8 -0
  4. package/dist/assets/chat-page-D3QaEqjs.js +105 -0
  5. package/dist/assets/config-split-page-CeFfbaqc.js +1 -0
  6. package/dist/assets/{confirm-dialog-DRSelLdI.js → confirm-dialog-C95Y4QPC.js} +1 -1
  7. package/dist/assets/desktop-update-config-CcK6jiEb.js +1 -0
  8. package/dist/assets/{dist-D19wiQwB.js → dist-DTGzeil6.js} +1 -1
  9. package/dist/assets/{dist-NZRV-BxD.js → dist-fGogBmPA.js} +1 -1
  10. package/dist/assets/doc-browser-CuBTcBEZ.js +1 -0
  11. package/dist/assets/doc-browser-DVHhEb96.js +1 -0
  12. package/dist/assets/{doc-browser-context-C8KPbj-p.js → doc-browser-context-BfMXwnrz.js} +1 -1
  13. package/dist/assets/{ellipsis-BYy8TP0A.js → ellipsis-D2F4KMiU.js} +1 -1
  14. package/dist/assets/{external-link-528k6MDD.js → external-link-CcgtBK26.js} +1 -1
  15. package/dist/assets/index-ByJCbQqr.css +1 -0
  16. package/dist/assets/index-DjXqiloY.js +103 -0
  17. package/dist/assets/mcp-marketplace-page-98-V__3u.js +1 -0
  18. package/dist/assets/mcp-marketplace-page-BNq_PNmO.js +40 -0
  19. package/dist/assets/model-config-page-aAUkZkSX.js +1 -0
  20. package/dist/assets/plus-1Rxl5FlV.js +1 -0
  21. package/dist/assets/provider-scoped-model-input-we4_Z4gP.js +1 -0
  22. package/dist/assets/providers-config-page-CtMkPSyu.js +1 -0
  23. package/dist/assets/{react-Dsotw5vM.js → react-CTqdhFMk.js} +1 -1
  24. package/dist/assets/remote-fQ7N_r8u.js +1 -0
  25. package/dist/assets/rotate-cw-CzfIeAJj.js +1 -0
  26. package/dist/assets/runtime-config-page-TD1UrTbG.js +1 -0
  27. package/dist/assets/{save-CHDnDTok.js → save-C9fKWfVB.js} +1 -1
  28. package/dist/assets/search-D5FQ5q0X.js +1 -0
  29. package/dist/assets/search-config-page-AMEJJNIS.js +1 -0
  30. package/dist/assets/secrets-config-page-CcgJnvBO.js +3 -0
  31. package/dist/assets/select-Cu5n0E-3.js +41 -0
  32. package/dist/assets/{tag-chip-Cr8GwpRE.js → tag-chip-C5aOtjxu.js} +1 -1
  33. package/dist/assets/use-config-D8TESeo6.js +1 -0
  34. package/dist/assets/x-1qETdQ70.js +1 -0
  35. package/dist/index.html +14 -27
  36. package/package.json +9 -9
  37. package/src/app/presenters/app.presenter.ts +2 -0
  38. package/src/features/account/components/account-panel.tsx +2 -2
  39. package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +12 -63
  40. package/src/features/chat/components/conversation/chat-conversation-content.tsx +11 -50
  41. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +1 -0
  42. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +15 -2
  43. package/src/features/chat/features/input/components/chat-input-bar.container.tsx +122 -85
  44. package/src/features/chat/features/input/hooks/use-chat-input-bar-query-state.ts +120 -0
  45. package/src/features/chat/features/input/hooks/use-chat-model-favorites.ts +87 -0
  46. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +66 -41
  47. package/src/features/chat/features/input/utils/__tests__/ncp-chat-input-availability.utils.test.ts +1 -0
  48. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +22 -36
  49. package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +80 -52
  50. package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +89 -0
  51. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +9 -1
  52. package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +115 -0
  53. package/src/features/chat/features/message/components/chat-message-list.container.tsx +4 -0
  54. package/src/features/chat/features/message/utils/__tests__/chat-message-file-operation.utils.test.ts +310 -0
  55. package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +119 -1
  56. package/src/features/chat/features/message/utils/__tests__/chat-message-test.utils.ts +43 -0
  57. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +1 -346
  58. package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +51 -0
  59. package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +28 -5
  60. package/src/features/chat/features/message/utils/chat-message-tool-card.utils.ts +14 -9
  61. package/src/features/chat/features/message/utils/file-operation/diff.utils.ts +39 -36
  62. package/src/features/chat/features/message/utils/file-operation/line-builder.utils.ts +46 -38
  63. package/src/features/chat/features/message/utils/file-operation/parsed-block.utils.ts +18 -15
  64. package/src/features/chat/features/ncp/hooks/__tests__/use-ui-show-content-event.test.tsx +1 -0
  65. package/src/features/chat/features/ncp/utils/ncp-chat-query-derived.utils.ts +11 -3
  66. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +27 -0
  67. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-badge.test.tsx +25 -25
  68. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +19 -1
  69. package/src/features/chat/features/session/components/session-header/chat-session-metadata-dialog.tsx +46 -0
  70. package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +15 -2
  71. package/src/features/chat/features/session/hooks/__tests__/use-selected-session-context-window-indicator.test.tsx +63 -0
  72. package/src/features/chat/features/session/hooks/use-selected-session-context-window-indicator.ts +7 -10
  73. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +8 -0
  74. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +2 -0
  75. package/src/features/chat/features/session-type/components/__tests__/chat-sidebar-create-menu.test.tsx +24 -0
  76. package/src/features/chat/features/session-type/components/chat-sidebar-create-menu.tsx +10 -1
  77. package/src/features/chat/features/session-type/hooks/use-chat-session-type-state.ts +1 -0
  78. package/src/features/chat/features/session-type/utils/chat-session-type.utils.ts +4 -0
  79. package/src/features/chat/features/welcome/components/__tests__/chat-conversation-welcome.test.tsx +193 -0
  80. package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +123 -0
  81. package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +104 -0
  82. package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +72 -0
  83. package/src/features/chat/features/welcome/components/chat-welcome-capability-grid.tsx +54 -0
  84. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +132 -0
  85. package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +82 -0
  86. package/src/features/chat/features/welcome/components/chat-welcome.tsx +121 -0
  87. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +53 -0
  88. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-selection.utils.test.ts +63 -0
  89. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-visibility.utils.test.ts +34 -0
  90. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +71 -0
  91. package/src/features/chat/features/welcome/utils/chat-welcome-selection.utils.ts +79 -0
  92. package/src/features/chat/features/welcome/utils/chat-welcome-visibility.utils.ts +17 -0
  93. package/src/features/chat/index.ts +1 -0
  94. package/src/features/chat/managers/__tests__/chat-input.manager.test.ts +93 -0
  95. package/src/features/chat/managers/__tests__/chat-run-snapshot.manager.test.ts +129 -0
  96. package/src/features/chat/managers/__tests__/chat-run.manager.test.ts +34 -0
  97. package/src/features/chat/managers/__tests__/chat-session-preference-sync.manager.test.ts +143 -1
  98. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +30 -0
  99. package/src/features/chat/managers/__tests__/recent-selection.manager.test.ts +37 -14
  100. package/src/features/chat/managers/chat-draft-intent.manager.ts +46 -0
  101. package/src/features/chat/managers/chat-input.manager.ts +37 -15
  102. package/src/features/chat/managers/chat-run.manager.ts +3 -0
  103. package/src/features/chat/managers/chat-session-preference-sync.manager.ts +19 -2
  104. package/src/features/chat/managers/chat-thread.manager.ts +4 -0
  105. package/src/features/chat/managers/chat-ui.manager.ts +2 -6
  106. package/src/features/chat/managers/recent-selection.manager.ts +21 -67
  107. package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +47 -1
  108. package/src/features/chat/pages/ncp-chat-page.tsx +18 -0
  109. package/src/features/chat/stores/chat-input.store.ts +9 -1
  110. package/src/features/chat/stores/recent-selection.store.ts +85 -0
  111. package/src/features/chat/types/chat-input-bar.types.ts +5 -1
  112. package/src/features/marketplace/utils/marketplace-installed-cache.utils.ts +27 -23
  113. package/src/features/panel-apps/components/__tests__/panel-apps-list.test.tsx +137 -0
  114. package/src/features/panel-apps/components/panel-apps-list.tsx +119 -5
  115. package/src/features/panel-apps/index.ts +2 -0
  116. package/src/features/panel-apps/utils/panel-app-entry-match.utils.ts +17 -0
  117. package/src/features/settings/components/config/provider-form.tsx +284 -387
  118. package/src/features/settings/components/config/provider-status-badge.tsx +4 -4
  119. package/src/features/settings/components/config/secrets-config-form.tsx +11 -135
  120. package/src/features/settings/hooks/use-provider-auth-flow.ts +140 -0
  121. package/src/features/settings/pages/__tests__/model-config-page.test.tsx +16 -0
  122. package/src/features/settings/pages/secrets-config-page.tsx +2 -1
  123. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +60 -1
  124. package/src/features/settings/utils/__tests__/secrets-config-form.utils.test.ts +66 -0
  125. package/src/features/settings/utils/provider-form-context.utils.ts +131 -0
  126. package/src/features/settings/utils/provider-form-model.utils.ts +101 -0
  127. package/src/features/settings/utils/provider-form-support.utils.ts +102 -1
  128. package/src/features/settings/utils/secrets-config-form.utils.ts +139 -0
  129. package/src/features/system-status/components/config/runtime-agent-list-card.tsx +17 -11
  130. package/src/features/system-status/components/config/runtime-binding-list-card.tsx +14 -9
  131. package/src/features/system-status/components/config/runtime-config-editor.tsx +8 -5
  132. package/src/features/system-status/components/config/runtime-entry-list-card.tsx +14 -9
  133. package/src/features/system-status/components/config/runtime-settings-card.tsx +16 -7
  134. package/src/features/system-status/components/runtime-security-card.tsx +79 -25
  135. package/src/shared/components/common/__tests__/searchable-model-input.test.tsx +34 -0
  136. package/src/shared/components/common/provider-scoped-model-input.tsx +1 -0
  137. package/src/shared/components/common/searchable-model-input.tsx +102 -87
  138. package/src/shared/components/ui/README.md +7 -0
  139. package/src/shared/components/ui/popover.tsx +19 -3
  140. package/src/shared/components/ui/select.tsx +14 -4
  141. package/src/shared/hooks/use-infinite-scroll-loader.ts +14 -10
  142. package/src/shared/lib/api/chat-session-type.types.ts +7 -0
  143. package/src/shared/lib/api/index.ts +2 -0
  144. package/src/shared/lib/api/ncp-session.types.ts +1 -0
  145. package/src/shared/lib/api/preferences/preference.types.ts +22 -0
  146. package/src/shared/lib/api/preferences/preference.utils.ts +40 -0
  147. package/src/shared/lib/api/types.ts +3 -0
  148. package/src/shared/lib/i18n/locales/en-US/chat.json +20 -1
  149. package/src/shared/lib/i18n/locales/en-US/core.json +1 -0
  150. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +10 -0
  151. package/src/shared/lib/i18n/locales/zh-CN/chat.json +20 -1
  152. package/src/shared/lib/i18n/locales/zh-CN/core.json +1 -0
  153. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +10 -0
  154. package/dist/assets/app-presenter-provider-BI-ewLbO.js +0 -3
  155. package/dist/assets/arrow-left-BOCJyLbQ.js +0 -1
  156. package/dist/assets/book-open-Bd2Xbrrc.js +0 -1
  157. package/dist/assets/channels-list-page-moJkpxtU.js +0 -8
  158. package/dist/assets/chat-page-CO27cl4v.js +0 -105
  159. package/dist/assets/config-split-page-CsQ2rIdq.js +0 -1
  160. package/dist/assets/desktop-update-config-qa-SynOc.js +0 -1
  161. package/dist/assets/doc-browser-2ddUJCA0.js +0 -1
  162. package/dist/assets/doc-browser-BjHUczQb.js +0 -1
  163. package/dist/assets/es2015-BUIIKCNh.js +0 -41
  164. package/dist/assets/host-capabilities-9bB_998Z.js +0 -1
  165. package/dist/assets/index-CMNrRhnG.js +0 -101
  166. package/dist/assets/index-UTmTW1eU.css +0 -1
  167. package/dist/assets/key-round-D3vD9dj7.js +0 -1
  168. package/dist/assets/loader-circle-DV9TSRns.js +0 -1
  169. package/dist/assets/mcp-marketplace-page-CS5Lh3oA.js +0 -1
  170. package/dist/assets/mcp-marketplace-page-DEOMcWb0.js +0 -40
  171. package/dist/assets/model-config-page-BSc4o7pb.js +0 -1
  172. package/dist/assets/notice-card-qqlbsOoR.js +0 -1
  173. package/dist/assets/plus-Bf9qhPDO.js +0 -1
  174. package/dist/assets/popover-BnmPOXe3.js +0 -1
  175. package/dist/assets/provider-scoped-model-input-Ca56s475.js +0 -1
  176. package/dist/assets/providers-config-page-D8Kf4U3u.js +0 -1
  177. package/dist/assets/refresh-cw-Bmx4wc9P.js +0 -1
  178. package/dist/assets/remote-DNMEy_c4.js +0 -1
  179. package/dist/assets/rotate-cw-CL0sVBel.js +0 -1
  180. package/dist/assets/runtime-config-page-CUFtKr0U.js +0 -1
  181. package/dist/assets/search-D7L-TCH-.js +0 -1
  182. package/dist/assets/search-config-page-CA3RVAef.js +0 -1
  183. package/dist/assets/secrets-config-page-95yaH1fa.js +0 -3
  184. package/dist/assets/status-dot-Dm4h-bjn.js +0 -1
  185. package/dist/assets/tabs-CJvw5Lqp.js +0 -1
  186. package/dist/assets/tooltip-Bxzi6aDL.js +0 -1
  187. package/dist/assets/trash-2-CSfX-BvU.js +0 -1
  188. package/dist/assets/use-config-TzwzTgg0.js +0 -1
  189. package/dist/assets/x-fkIWvi07.js +0 -1
  190. package/src/features/chat/components/__tests__/chat-welcome.test.tsx +0 -45
  191. package/src/features/chat/components/chat-welcome.tsx +0 -102
  192. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-bar-controller.test.tsx +0 -152
  193. package/src/features/chat/features/input/hooks/use-chat-input-bar-controller.ts +0 -142
  194. /package/dist/assets/{config-hints-Cl2XAhzV.js → config-hints-CTqBnCDp.js} +0 -0
@@ -1,42 +1,5 @@
1
1
  import { ToolInvocationStatus, type UiMessage } from "@nextclaw/agent-chat";
2
- import { adaptChatMessages } from "../chat-message.utils";
3
- import type { ChatMessageSource } from "../chat-message.utils";
4
-
5
- function toSource(uiMessages: UiMessage[]): ChatMessageSource[] {
6
- return uiMessages as unknown as ChatMessageSource[];
7
- }
8
-
9
- const defaultTexts = {
10
- roleLabels: {
11
- user: "You",
12
- assistant: "Assistant",
13
- tool: "Tool",
14
- system: "System",
15
- fallback: "Message",
16
- },
17
- reasoningLabel: "Reasoning",
18
- toolCallLabel: "Tool Call",
19
- toolResultLabel: "Tool Result",
20
- toolInputLabel: "Input",
21
- toolNoOutputLabel: "No output",
22
- toolOutputLabel: "Output",
23
- toolStatusPreparingLabel: "Preparing",
24
- toolStatusRunningLabel: "Running",
25
- toolStatusCompletedLabel: "Completed",
26
- toolStatusFailedLabel: "Failed",
27
- toolStatusCancelledLabel: "Cancelled",
28
- imageAttachmentLabel: "Image attachment",
29
- fileAttachmentLabel: "File attachment",
30
- unknownPartLabel: "Unknown Part",
31
- };
32
-
33
- function adapt(uiMessages: ChatMessageSource[]) {
34
- return adaptChatMessages({
35
- uiMessages,
36
- formatTimestamp: (value) => `formatted:${value}`,
37
- texts: defaultTexts,
38
- });
39
- }
2
+ import { adapt, toSource, type ChatMessageSource } from "./chat-message-test.utils";
40
3
 
41
4
  it("maps markdown, reasoning, and tool parts into UI view models", () => {
42
5
  const messages: UiMessage[] = [
@@ -576,311 +539,3 @@ it("renders asset tool results as previewable files", () => {
576
539
  },
577
540
  });
578
541
  });
579
-
580
- it("builds edit-file previews from structured args before the tool finishes", () => {
581
- const adapted = adapt([
582
- {
583
- id: "assistant-edit-preview",
584
- role: "assistant",
585
- parts: [
586
- {
587
- type: "tool-invocation",
588
- toolInvocation: {
589
- status: ToolInvocationStatus.CALL,
590
- toolCallId: "edit-call-1",
591
- toolName: "edit_file",
592
- args: JSON.stringify({
593
- path: "src/app.ts",
594
- oldText: "const color = 'red';",
595
- newText: "const color = 'blue';",
596
- }),
597
- parsedArgs: {
598
- path: "src/app.ts",
599
- oldText: "const color = 'red';",
600
- newText: "const color = 'blue';",
601
- },
602
- },
603
- },
604
- ],
605
- },
606
- ] as unknown as ChatMessageSource[]);
607
-
608
- const editLines =
609
- adapted[0]?.parts[0]?.type === "tool-card"
610
- ? (adapted[0].parts[0].card.fileOperation?.blocks[0]?.lines ?? [])
611
- : [];
612
-
613
- expect(adapted[0]?.parts[0]).toMatchObject({
614
- type: "tool-card",
615
- card: {
616
- toolName: "edit_file",
617
- summary: "src/app.ts",
618
- statusTone: "running",
619
- fileOperation: {
620
- blocks: [
621
- {
622
- path: "src/app.ts",
623
- lines: [
624
- {
625
- kind: "remove",
626
- text: "const color = 'red';",
627
- },
628
- {
629
- kind: "add",
630
- text: "const color = 'blue';",
631
- },
632
- ],
633
- },
634
- ],
635
- },
636
- },
637
- });
638
- expect(editLines[0]).not.toHaveProperty("oldLineNumber");
639
- expect(editLines[1]).not.toHaveProperty("newLineNumber");
640
- });
641
-
642
- it("uses structured edit-file result line numbers after the tool finishes", () => {
643
- const adapted = adapt([
644
- {
645
- id: "assistant-edit-result",
646
- role: "assistant",
647
- parts: [
648
- {
649
- type: "tool-invocation",
650
- toolInvocation: {
651
- status: ToolInvocationStatus.RESULT,
652
- toolCallId: "edit-result-1",
653
- toolName: "edit_file",
654
- args: JSON.stringify({
655
- path: "src/app.ts",
656
- oldText: "const color = 'red';",
657
- newText: "const color = 'blue';",
658
- }),
659
- parsedArgs: {
660
- path: "src/app.ts",
661
- oldText: "const color = 'red';",
662
- newText: "const color = 'blue';",
663
- },
664
- result: {
665
- path: "src/app.ts",
666
- oldStartLine: 27,
667
- newStartLine: 27,
668
- message: "Edited src/app.ts",
669
- },
670
- },
671
- },
672
- ],
673
- },
674
- ] as unknown as ChatMessageSource[]);
675
-
676
- expect(adapted[0]?.parts[0]).toMatchObject({
677
- type: "tool-card",
678
- card: {
679
- toolName: "edit_file",
680
- summary: "src/app.ts",
681
- statusTone: "success",
682
- fileOperation: {
683
- blocks: [
684
- {
685
- path: "src/app.ts",
686
- lines: [
687
- {
688
- kind: "remove",
689
- text: "const color = 'red';",
690
- oldLineNumber: 27,
691
- },
692
- {
693
- kind: "add",
694
- text: "const color = 'blue';",
695
- newLineNumber: 27,
696
- },
697
- ],
698
- },
699
- ],
700
- },
701
- },
702
- });
703
- });
704
-
705
- it("builds write-file previews from partial native args before the JSON is complete", () => {
706
- const adapted = adapt([
707
- {
708
- id: "assistant-write-preview",
709
- role: "assistant",
710
- parts: [
711
- {
712
- type: "tool-invocation",
713
- toolInvocation: {
714
- status: ToolInvocationStatus.PARTIAL_CALL,
715
- toolCallId: "write-call-1",
716
- toolName: "write_file",
717
- args: '{"path":"games/snake.html","content":"<!DOCTYPE html>\\n<canvas id=\\"game\\"></canvas>\\n<script>const score = 1;',
718
- },
719
- },
720
- ],
721
- },
722
- ] as unknown as ChatMessageSource[]);
723
-
724
- expect(adapted[0]?.parts[0]).toMatchObject({
725
- type: "tool-card",
726
- card: {
727
- toolName: "write_file",
728
- summary: "games/snake.html",
729
- statusTone: "running",
730
- statusLabel: "Running",
731
- fileOperation: {
732
- blocks: [
733
- {
734
- display: "preview",
735
- path: "games/snake.html",
736
- lines: expect.arrayContaining([
737
- expect.objectContaining({
738
- kind: "add",
739
- text: "<!DOCTYPE html>",
740
- }),
741
- expect.objectContaining({
742
- kind: "add",
743
- text: '<canvas id="game"></canvas>',
744
- }),
745
- ]),
746
- },
747
- ],
748
- },
749
- },
750
- });
751
- });
752
-
753
- it("keeps completed write-file cards in preview mode instead of falling back to raw byte summaries", () => {
754
- const adapted = adapt([
755
- {
756
- id: "assistant-write-result",
757
- role: "assistant",
758
- parts: [
759
- {
760
- type: "tool-invocation",
761
- toolInvocation: {
762
- status: ToolInvocationStatus.RESULT,
763
- toolCallId: "write-result-1",
764
- toolName: "write_file",
765
- args: JSON.stringify({
766
- path: "games/snake.html",
767
- content: '<!DOCTYPE html>\n<canvas id="game"></canvas>',
768
- }),
769
- result: "Wrote 3906 bytes to games/snake.html",
770
- },
771
- },
772
- ],
773
- },
774
- ] as unknown as ChatMessageSource[]);
775
-
776
- expect(adapted[0]?.parts[0]).toMatchObject({
777
- type: "tool-card",
778
- card: {
779
- toolName: "write_file",
780
- summary: "games/snake.html",
781
- statusTone: "success",
782
- fileOperation: {
783
- blocks: [
784
- {
785
- display: "preview",
786
- path: "games/snake.html",
787
- lines: [
788
- {
789
- kind: "add",
790
- text: "<!DOCTYPE html>",
791
- newLineNumber: 1,
792
- },
793
- {
794
- kind: "add",
795
- text: '<canvas id="game"></canvas>',
796
- newLineNumber: 2,
797
- },
798
- ],
799
- },
800
- ],
801
- },
802
- },
803
- });
804
- expect(adapted[0]?.parts[0]).not.toMatchObject({
805
- type: "tool-card",
806
- card: {
807
- output: "Wrote 3906 bytes to games/snake.html",
808
- },
809
- });
810
- });
811
-
812
- it("renders codex file_change results as structured diff previews", () => {
813
- const adapted = adapt([
814
- {
815
- id: "assistant-file-change",
816
- role: "assistant",
817
- parts: [
818
- {
819
- type: "tool-invocation",
820
- toolInvocation: {
821
- status: ToolInvocationStatus.RESULT,
822
- toolCallId: "file-change-1",
823
- toolName: "file_change",
824
- args: JSON.stringify({
825
- changes: [
826
- {
827
- path: "src/main.ts",
828
- diff: [
829
- "--- a/src/main.ts",
830
- "+++ b/src/main.ts",
831
- "@@ -109,1 +109,1 @@",
832
- "-console.log('old');",
833
- "+console.log('new');",
834
- ].join("\n"),
835
- },
836
- ],
837
- }),
838
- result: {
839
- status: "completed",
840
- changes: [
841
- {
842
- path: "src/main.ts",
843
- diff: [
844
- "--- a/src/main.ts",
845
- "+++ b/src/main.ts",
846
- "@@ -109,1 +109,1 @@",
847
- "-console.log('old');",
848
- "+console.log('new');",
849
- ].join("\n"),
850
- },
851
- ],
852
- },
853
- },
854
- },
855
- ],
856
- },
857
- ] as unknown as ChatMessageSource[]);
858
-
859
- expect(adapted[0]?.parts[0]).toMatchObject({
860
- type: "tool-card",
861
- card: {
862
- toolName: "file_change",
863
- summary: "src/main.ts",
864
- statusTone: "success",
865
- fileOperation: {
866
- blocks: [
867
- {
868
- path: "src/main.ts",
869
- lines: [
870
- {
871
- kind: "remove",
872
- text: "console.log('old');",
873
- oldLineNumber: 109,
874
- },
875
- {
876
- kind: "add",
877
- text: "console.log('new');",
878
- newLineNumber: 109,
879
- },
880
- ],
881
- },
882
- ],
883
- },
884
- },
885
- });
886
- });
@@ -0,0 +1,51 @@
1
+ import {
2
+ createPanelAppResourceUri,
3
+ RIGHT_PANEL_PANEL_APP_TAB_KIND,
4
+ } from '@/features/right-panel-resources';
5
+ import type { DocBrowserTab } from '@/shared/components/doc-browser/doc-browser-context';
6
+
7
+ export const PANEL_APP_INLINE_IFRAME_SANDBOX = [
8
+ 'allow-scripts',
9
+ 'allow-forms',
10
+ 'allow-modals',
11
+ 'allow-popups',
12
+ 'allow-popups-to-escape-sandbox',
13
+ 'allow-downloads',
14
+ 'allow-pointer-lock',
15
+ 'allow-presentation',
16
+ ].join(' ');
17
+
18
+ export const PANEL_APP_INLINE_CARD_MAX_HEIGHT_PX = 420;
19
+
20
+ const PANEL_APP_INLINE_CARD_SEARCH_PARAMS = {
21
+ nextclawDisplayMode: 'card',
22
+ nextclawPlacement: 'inline',
23
+ } as const;
24
+
25
+ export function createFallbackPanelAppContentPath(appId: string): string {
26
+ return `/api/panel-apps/${encodeURIComponent(appId)}/content`;
27
+ }
28
+
29
+ export function createInlinePanelAppCardUrl(url: string): string {
30
+ const separator = url.includes('?') ? '&' : '?';
31
+ return `${url}${separator}${new URLSearchParams(PANEL_APP_INLINE_CARD_SEARCH_PARAMS).toString()}`;
32
+ }
33
+
34
+ export function createInlinePanelAppTab(params: {
35
+ appId: string;
36
+ title: string;
37
+ url: string;
38
+ }): DocBrowserTab {
39
+ const { appId, title, url } = params;
40
+ return {
41
+ currentUrl: url,
42
+ dedupeKey: `panel-app:${appId}`,
43
+ history: [url],
44
+ historyIndex: 0,
45
+ id: `inline-panel-app:${appId}`,
46
+ kind: RIGHT_PANEL_PANEL_APP_TAB_KIND,
47
+ navVersion: 0,
48
+ resourceUri: createPanelAppResourceUri(appId),
49
+ title,
50
+ };
51
+ }
@@ -42,6 +42,12 @@ function readPurpose(value: unknown): ChatUiShowContentRequest["purpose"] {
42
42
  : undefined;
43
43
  }
44
44
 
45
+ function readPlacement(value: unknown): ChatUiShowContentRequest["placement"] {
46
+ return value === "inline" || value === "side_panel"
47
+ ? value
48
+ : undefined;
49
+ }
50
+
45
51
  function readShowContentRequest(value: unknown): ChatUiShowContentRequest | null {
46
52
  if (!isRecord(value) || !isRecord(value.target)) {
47
53
  return null;
@@ -57,6 +63,7 @@ function readShowContentRequest(value: unknown): ChatUiShowContentRequest | null
57
63
  }
58
64
  const title = readOptionalString(value.title);
59
65
  const purpose = readPurpose(value.purpose);
66
+ const placement = readPlacement(value.placement);
60
67
 
61
68
  if (targetType === "file") {
62
69
  const path = readOptionalString(payload.path);
@@ -74,6 +81,7 @@ function readShowContentRequest(value: unknown): ChatUiShowContentRequest | null
74
81
  },
75
82
  title,
76
83
  purpose,
84
+ placement,
77
85
  };
78
86
  }
79
87
 
@@ -91,6 +99,7 @@ function readShowContentRequest(value: unknown): ChatUiShowContentRequest | null
91
99
  },
92
100
  title,
93
101
  purpose,
102
+ placement,
94
103
  };
95
104
  }
96
105
 
@@ -107,6 +116,7 @@ function readShowContentRequest(value: unknown): ChatUiShowContentRequest | null
107
116
  },
108
117
  title,
109
118
  purpose,
119
+ placement,
110
120
  };
111
121
  }
112
122
 
@@ -147,6 +157,22 @@ export function buildShowContentToolCard(params: {
147
157
  if (!request) {
148
158
  return null;
149
159
  }
160
+ const sidePanelRequest: ChatUiShowContentRequest = {
161
+ ...request,
162
+ placement: "side_panel",
163
+ };
164
+ const showContentAction = {
165
+ kind: "show-content" as const,
166
+ label: actionLabel,
167
+ request: sidePanelRequest,
168
+ };
169
+ const panelApp = request.target.type === "panel_app" && request.placement === "inline"
170
+ ? {
171
+ appId: request.target.payload.appId,
172
+ title: request.title,
173
+ action: showContentAction,
174
+ }
175
+ : undefined;
150
176
  return {
151
177
  kind: "result",
152
178
  name: SHOW_CONTENT_TOOL_NAME,
@@ -156,10 +182,7 @@ export function buildShowContentToolCard(params: {
156
182
  hasResult: true,
157
183
  statusTone: "success",
158
184
  statusLabel,
159
- action: {
160
- kind: "show-content",
161
- label: actionLabel,
162
- request,
163
- },
185
+ action: showContentAction,
186
+ ...(panelApp ? { panelApp } : {}),
164
187
  };
165
188
  }
@@ -15,6 +15,7 @@ export type ToolCardViewSource = ToolCard & {
15
15
  action?: ChatToolPartViewModel["action"];
16
16
  fileOperation?: ChatToolPartViewModel["fileOperation"];
17
17
  outputData?: unknown;
18
+ panelApp?: ChatToolPartViewModel["panelApp"];
18
19
  };
19
20
 
20
21
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -133,6 +134,9 @@ export function buildToolCard(
133
134
  ...("fileOperation" in toolCard && toolCard.fileOperation
134
135
  ? { fileOperation: toolCard.fileOperation }
135
136
  : {}),
137
+ ...("panelApp" in toolCard && toolCard.panelApp
138
+ ? { panelApp: toolCard.panelApp }
139
+ : {}),
136
140
  };
137
141
  }
138
142
 
@@ -146,17 +150,18 @@ export function resolveToolCardStatus(params: {
146
150
  ChatToolPartViewModel,
147
151
  "kind" | "hasResult" | "statusTone" | "statusLabel"
148
152
  > {
153
+ const { cancelled, error, result, status, texts } = params;
149
154
  const rawStatus =
150
- typeof params.status === "string" ? params.status.trim().toLowerCase() : "";
155
+ typeof status === "string" ? status.trim().toLowerCase() : "";
151
156
  const hasError =
152
- typeof params.error === "string" && params.error.trim().length > 0;
153
- const isCancelled = params.cancelled === true || rawStatus === "cancelled";
157
+ typeof error === "string" && error.trim().length > 0;
158
+ const isCancelled = cancelled === true || rawStatus === "cancelled";
154
159
  if (isCancelled) {
155
160
  return {
156
161
  kind: "result",
157
162
  hasResult: true,
158
163
  statusTone: "cancelled",
159
- statusLabel: params.texts.toolStatusCancelledLabel,
164
+ statusLabel: texts.toolStatusCancelledLabel,
160
165
  };
161
166
  }
162
167
  if (hasError || rawStatus === "error") {
@@ -164,15 +169,15 @@ export function resolveToolCardStatus(params: {
164
169
  kind: "result",
165
170
  hasResult: true,
166
171
  statusTone: "error",
167
- statusLabel: params.texts.toolStatusFailedLabel,
172
+ statusLabel: texts.toolStatusFailedLabel,
168
173
  };
169
174
  }
170
- if (rawStatus === "result" || params.result != null) {
175
+ if (rawStatus === "result" || result != null) {
171
176
  return {
172
177
  kind: "result",
173
178
  hasResult: true,
174
179
  statusTone: "success",
175
- statusLabel: params.texts.toolStatusCompletedLabel,
180
+ statusLabel: texts.toolStatusCompletedLabel,
176
181
  };
177
182
  }
178
183
  if (rawStatus === "partial-call") {
@@ -180,14 +185,14 @@ export function resolveToolCardStatus(params: {
180
185
  kind: "call",
181
186
  hasResult: false,
182
187
  statusTone: "running",
183
- statusLabel: params.texts.toolStatusRunningLabel,
188
+ statusLabel: texts.toolStatusRunningLabel,
184
189
  };
185
190
  }
186
191
  return {
187
192
  kind: "call",
188
193
  hasResult: false,
189
194
  statusTone: "running",
190
- statusLabel: params.texts.toolStatusRunningLabel,
195
+ statusLabel: texts.toolStatusRunningLabel,
191
196
  };
192
197
  }
193
198