@nextclaw/ui 0.19.2 → 0.19.4

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 (249) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/dist/assets/{abnfDiagram-VRR7QNED-DvTjgVMf.js → abnfDiagram-VRR7QNED-DrbfiNNF.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-C1nfBAXR.js → aiden0z-pptx-renderer.es-DdLb2IVc.js} +1 -1
  4. package/dist/assets/{api-BH6Oslx4.js → api-CUSlBpt_.js} +3 -3
  5. package/dist/assets/{appearance-settings-page-zhgfcGgW.js → appearance-settings-page-cTuZdROP.js} +1 -1
  6. package/dist/assets/{arc-BvhODBXv.js → arc-BXlMc9B0.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-Bq0i9WiE.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-BKO9RgNS.js → architectureDiagram-ZJ3FMSHR-DXAJxxO0.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-DHpDXy7D.js → blockDiagram-677ZJIJ3-i4Kz7zU0.js} +1 -1
  10. package/dist/assets/{book-open-DGTS6oEO.js → book-open-BJ-xAIp9.js} +1 -1
  11. package/dist/assets/{c4Diagram-LMCZKHZV-BG_iQnDr.js → c4Diagram-LMCZKHZV-BDc0TkPd.js} +1 -1
  12. package/dist/assets/channel-iCaZQ87u.js +1 -0
  13. package/dist/assets/{channels-list-page-3D5lhZoT.js → channels-list-page-nRHOasWA.js} +2 -2
  14. package/dist/assets/chunk-2Q5K7J3B-DFQtlnJS.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-DR9M0LLx.js → chunk-32BRIVSS-DuReei0Z.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-698iC3BB.js → chunk-52WLFC77-D8APq0tm.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-DikTj9Ey.js → chunk-5FCAYU7R-BsPrNXNS.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-ko0o7_q0.js → chunk-5HE753X5-BRtkOPoJ.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-CEHufhgo.js → chunk-5JV3BV7I-ChPc8e8U.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-CGgno1cu.js → chunk-5TONJI2A-CzTjlJGb.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-DkiB4cUQ.js → chunk-5VM5RSS4-xsv_22jR.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-DzptcytQ.js → chunk-7BUUIJ7U-CwECujme.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-DMRSQJvT.js → chunk-BIQX33UG-DQAm173p.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-Bw1BwwuY.js → chunk-C7G6YPKG-BkpajeQb.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-C0z6eHBk.js → chunk-CQNSW5MT-ND65pWRA.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-ByPyAouO.js → chunk-CYSBUYHQ-vMu6ijxG.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-CJlAXWZg.js → chunk-EMLP6XTP-CEaeW3gH.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-Dqq4zKJu.js → chunk-EX3LRPZG-a4ER-HDr.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-B0SNoCmi.js → chunk-FWX5IMBZ-C_8fQssn.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-DG_GJih_.js → chunk-HOUHSVGY-CEvw-PEd.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-Drxk_6bE.js → chunk-ICXQ74PX-BEtF0fGY.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-DdLFPpnd.js → chunk-JG7HCLWE-DGRrVp8-.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-B8M4G76F.js → chunk-JWPE2WC7-DRqpp_nM.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-XP6FJHY_.js → chunk-MOJQB5TN-BDEriOXs.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-C_6x18Qw.js → chunk-MOZMSUNE-JWRNzdnw.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-N25BzCmG.js → chunk-OGEWGWER-DS2VTgCx.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-ASDEm7y0.js → chunk-OSBZ3O6U-3LiYTBUB.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-CTlvAcn7.js → chunk-PUDLZKDR-o9M2s_SO.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-Da9BWfB6.js → chunk-Q4XR5HBZ-DTB7Ma2N.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-Bi6RpTJO.js → chunk-QBLGF6JB-DpkxZJL5.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-DkMsSPvK.js → chunk-R7FJI6CG-CRejkHJU.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-DZp0eU-j.js → chunk-RYQCIY6F-B1boBjLt.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-vfinDSrj.js → chunk-U6XO7XAA-DgKa97bj.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-C9Nksccs.js → chunk-V7JOEXUC-DE56eh0N.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-CF9A-TeE.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-Xv6zfwXs.js → chunk-VR4S4FIN-BzomPZq2.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-mOJNzbWl.js → chunk-WYO6CB5R-C6gy8rSd.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-BRxdfVCU.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-1-aKSu4o.js → chunk-YOTPTUD7-BK98yfl9.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-OJ8veMpK.js → chunk-ZGVPDNZ5-DFXAhwUT.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-SR_Nq_PP.js → chunk-ZIRB5QZD-Bbu6BiNC.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-Bo28fPi-.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-BOVN9wUz.js +1 -0
  54. package/dist/assets/{config-split-page-B55XD_ZY.js → config-split-page-DO1Oyb-r.js} +1 -1
  55. package/dist/assets/{confirm-dialog-BCCreI_f.js → confirm-dialog-CCae9Fr4.js} +1 -1
  56. package/dist/assets/{cose-bilkent-JH36ORCC-NjVgz2Bw.js → cose-bilkent-JH36ORCC-BV8KR4uF.js} +1 -1
  57. package/dist/assets/cynefin-VYW2F7L2-B7rF3JRp.js +1 -0
  58. package/dist/assets/{cynefinDiagram-TSTJHNR4-B_17LDqD.js → cynefinDiagram-TSTJHNR4-Ckd6nU9t.js} +1 -1
  59. package/dist/assets/{dagre-CMe101gk.js → dagre-D_T-FW5i.js} +1 -1
  60. package/dist/assets/{dagre-VKFMJZFB-qo8nLb3B.js → dagre-VKFMJZFB-CyDaQpD4.js} +1 -1
  61. package/dist/assets/desktop-update-config-XSV-DjJW.js +1 -0
  62. package/dist/assets/{diagram-FQU43EPY-Ba3XIklC.js → diagram-FQU43EPY-CghnwS0H.js} +1 -1
  63. package/dist/assets/{diagram-G47NLZAW-CG45TcUx.js → diagram-G47NLZAW-DK4DJ3Kx.js} +1 -1
  64. package/dist/assets/{diagram-NH7WQ7WH-2F2HrwV-.js → diagram-NH7WQ7WH-CUgDkZCJ.js} +1 -1
  65. package/dist/assets/{diagram-OA4YK3LP-CXfWH9NZ.js → diagram-OA4YK3LP-vO2gyH6n.js} +1 -1
  66. package/dist/assets/{diagram-WEI45ONY-CH_4yepc.js → diagram-WEI45ONY-DbSBRDSr.js} +1 -1
  67. package/dist/assets/{dist-IRWcJpfm.js → dist-1N9UpL3c.js} +1 -1
  68. package/dist/assets/{dist-BYSUQaO2.js → dist-C-2H76Cv.js} +1 -1
  69. package/dist/assets/doc-browser-BHFCZIoL.js +1 -0
  70. package/dist/assets/{doc-browser-D8rNrWHk.js → doc-browser-CRlpqMwH.js} +1 -1
  71. package/dist/assets/{doc-browser-context-DjTbIbQc.js → doc-browser-context-C0Bns1J6.js} +1 -1
  72. package/dist/assets/{docx-preview-CoM4JK7_.js → docx-preview-DfxIm9IB.js} +1 -1
  73. package/dist/assets/{ebnfDiagram-CCIWWBDH-BwiG4Ea1.js → ebnfDiagram-CCIWWBDH-W7CAcK7r.js} +1 -1
  74. package/dist/assets/{erDiagram-Q63AITRT-BML3hpnQ.js → erDiagram-Q63AITRT-LA0JWL8-.js} +1 -1
  75. package/dist/assets/eventmodeling-45OFAUF4-DzQLXa3s.js +1 -0
  76. package/dist/assets/flowDiagram-23GEKE2U-C81eexJZ.js +1 -0
  77. package/dist/assets/{form-actions-C8hazgNP.js → form-actions-CMxhPact.js} +1 -1
  78. package/dist/assets/{ganttDiagram-NO4QXBWP-80enfWbD.js → ganttDiagram-NO4QXBWP-_PiTPavJ.js} +1 -1
  79. package/dist/assets/gitGraph-TEB2WS4Q-Dt3ZqpFD.js +1 -0
  80. package/dist/assets/{gitGraphDiagram-IHSO6WYX-CVLqXr-b.js → gitGraphDiagram-IHSO6WYX-DsRB1ZMU.js} +1 -1
  81. package/dist/assets/index-DdYOaKm7.js +109 -0
  82. package/dist/assets/index-DxCGKhAw.css +1 -0
  83. package/dist/assets/info-DKCQHKI2-CCCE7dXC.js +1 -0
  84. package/dist/assets/{infoDiagram-FWYZ7A6U-Dyvs2t0i.js → infoDiagram-FWYZ7A6U-wIRraYx5.js} +1 -1
  85. package/dist/assets/{ishikawaDiagram-FXEZZL3T-sSnxDKfh.js → ishikawaDiagram-FXEZZL3T-CNzsrcrh.js} +1 -1
  86. package/dist/assets/{journeyDiagram-5HDEW3XC-D22V71Mf.js → journeyDiagram-5HDEW3XC-Dy9huHxy.js} +1 -1
  87. package/dist/assets/{kanban-definition-HUTT4EX6-D3ds0p-g.js → kanban-definition-HUTT4EX6-BOCIN365.js} +1 -1
  88. package/dist/assets/{line-CbyFs065.js → line-BxsizOTs.js} +1 -1
  89. package/dist/assets/{linear-4DKtnZBS.js → linear-C_YHf92I.js} +1 -1
  90. package/dist/assets/loader-circle-B-UdXjl4.js +1 -0
  91. package/dist/assets/{map-CcZzEoz6.js → map-CobcjgZ8.js} +1 -1
  92. package/dist/assets/mcp-marketplace-page-BUIGtSda.js +1 -0
  93. package/dist/assets/mcp-marketplace-page-CStb8ml2.js +9 -0
  94. package/dist/assets/{mermaid-parser.core-DPmMOjXD.js → mermaid-parser.core-D7lu2RbA.js} +2 -2
  95. package/dist/assets/{mermaid.core-BSf-ddRa.js → mermaid.core-CbfW2Rk8.js} +3 -3
  96. package/dist/assets/{mindmap-definition-LN4V7U3C-BUTwWPyx.js → mindmap-definition-LN4V7U3C-DUjGXlIC.js} +1 -1
  97. package/dist/assets/model-config-page-BkfUYm95.js +1 -0
  98. package/dist/assets/{navigation-link-kGAcL2N2.js → navigation-link-v3FaWcIr.js} +1 -1
  99. package/dist/assets/{ordinal-BgLYtOZE.js → ordinal-CjoUCHto.js} +1 -1
  100. package/dist/assets/packet-7NZHBO7P-D19yoxFV.js +1 -0
  101. package/dist/assets/{pegDiagram-2B236MQR-BEu_ZBjc.js → pegDiagram-2B236MQR-BQY2DgiP.js} +1 -1
  102. package/dist/assets/pie-RZYD4A2V-hMaJbLrn.js +1 -0
  103. package/dist/assets/{pieDiagram-ENE6RG2P-CAwAwuBd.js → pieDiagram-ENE6RG2P-D18owe4D.js} +1 -1
  104. package/dist/assets/plus-Dw9QFnKr.js +1 -0
  105. package/dist/assets/{privacy-settings-page-v9LTFvJe.js → privacy-settings-page-CYM1Dxwo.js} +1 -1
  106. package/dist/assets/providers-config-page-fxL4I4bz.js +1 -0
  107. package/dist/assets/{quadrantDiagram-ABIIQ3AL-CwE6F-_2.js → quadrantDiagram-ABIIQ3AL-B8F7YoTA.js} +1 -1
  108. package/dist/assets/radar-I7S5WNFK-C8BkvScA.js +1 -0
  109. package/dist/assets/railroad-3IZDKUUU-CozZOitC.js +1 -0
  110. package/dist/assets/railroad-abnf-AHOZXSZD-BqIoFXFi.js +1 -0
  111. package/dist/assets/railroad-ebnf-EBAXGLYW-CD2e3dng.js +1 -0
  112. package/dist/assets/railroad-peg-LSFZ7HO6-DoKlRUOF.js +1 -0
  113. package/dist/assets/{railroadDiagram-RFXS5EU6-xgLekU3B.js → railroadDiagram-RFXS5EU6-RU8GpxN5.js} +1 -1
  114. package/dist/assets/{react-Dwvp5yeX.js → react-Dlz_IxoP.js} +1 -1
  115. package/dist/assets/{refresh-cw-C_oj_h_s.js → refresh-cw-CK4I5UFv.js} +1 -1
  116. package/dist/assets/remote-B0pPgtC6.js +1 -0
  117. package/dist/assets/{requirementDiagram-TGXJPOKE-CyVUzEX5.js → requirementDiagram-TGXJPOKE-ubPOmmNi.js} +1 -1
  118. package/dist/assets/rotate-cw-Z48dmh13.js +1 -0
  119. package/dist/assets/runtime-config-page-C7vksFVD.js +1 -0
  120. package/dist/assets/{sankeyDiagram-HTMAVEWB-JK6bFmYB.js → sankeyDiagram-HTMAVEWB-8L_hXJWW.js} +1 -1
  121. package/dist/assets/{save-BZE6H02N.js → save-Bo37cIFb.js} +1 -1
  122. package/dist/assets/{search-C7Tj40kC.js → search-CZiEm7YD.js} +1 -1
  123. package/dist/assets/search-config-page-DKdEf7m9.js +1 -0
  124. package/dist/assets/{secrets-config-page-B__VLAEQ.js → secrets-config-page-COlMqSfL.js} +2 -2
  125. package/dist/assets/security-config-CitLihXA.js +1 -0
  126. package/dist/assets/{select-fZ5tifar.js → select-BACzapV3.js} +1 -1
  127. package/dist/assets/{sequenceDiagram-DBY2YBRQ-BR5IokXV.js → sequenceDiagram-DBY2YBRQ-Dom6Qor5.js} +1 -1
  128. package/dist/assets/{sizeCapture-X5ZJPWSS-SxG_YnMA.js → sizeCapture-X5ZJPWSS-cZcFSpLJ.js} +1 -1
  129. package/dist/assets/skeleton-B2PCAZJG.js +1 -0
  130. package/dist/assets/{src-vgz8E_-O.js → src-BE9E1xcR.js} +1 -1
  131. package/dist/assets/{stateDiagram-2N3HPSRC-DD4OoYhn.js → stateDiagram-2N3HPSRC-CHrpxmGO.js} +1 -1
  132. package/dist/assets/stateDiagram-v2-6OUMAXLB-b1pZQlDD.js +1 -0
  133. package/dist/assets/{swimlanes-5IMT3BWC-BWkFeyCT.js → swimlanes-5IMT3BWC-D6nJS2xQ.js} +2 -2
  134. package/dist/assets/swimlanesDiagram-G3AALYLV-CowxgWVn.js +8 -0
  135. package/dist/assets/{tag-chip-CwYZcRoe.js → tag-chip-BnamTQVX.js} +1 -1
  136. package/dist/assets/{timeline-definition-FHXFAJF6-VTRPy6KA.js → timeline-definition-FHXFAJF6-DJNn-HQO.js} +1 -1
  137. package/dist/assets/treeView-QDETBFTQ-BQlYkJbz.js +1 -0
  138. package/dist/assets/treemap-6X3UGDF4-DzTOd1bd.js +1 -0
  139. package/dist/assets/{vennDiagram-L72KCM5P-Bvk42Pek.js → vennDiagram-L72KCM5P-CCEzHoXt.js} +1 -1
  140. package/dist/assets/wardley-OPB4EBWU-D80jQc0y.js +1 -0
  141. package/dist/assets/{wardleyDiagram-EHGQE667-DOFjx2s3.js → wardleyDiagram-EHGQE667-BhyVxVXt.js} +1 -1
  142. package/dist/assets/x-BzYEzkLA.js +1 -0
  143. package/dist/assets/{xychartDiagram-FW5EYKEG-DY5yRTUc.js → xychartDiagram-FW5EYKEG-CacW_246.js} +1 -1
  144. package/dist/index.html +70 -22
  145. package/index.html +49 -1
  146. package/package.json +8 -8
  147. package/src/app/index.tsx +1 -3
  148. package/src/app/managers/__tests__/viewport-layout.manager.test.ts +12 -0
  149. package/src/app/managers/viewport-layout.manager.ts +12 -0
  150. package/src/app/stores/viewport-layout.store.ts +23 -11
  151. package/src/features/apps/components/__tests__/app-marketplace-catalog.test.tsx +161 -0
  152. package/src/features/apps/components/app-marketplace-catalog.tsx +36 -7
  153. package/src/features/apps/components/app-marketplace-detail.tsx +29 -6
  154. package/src/features/apps/components/app-marketplace-dialog.tsx +71 -55
  155. package/src/features/apps/components/app-marketplace-operation.tsx +128 -16
  156. package/src/features/apps/components/app-packages-panel.tsx +1 -0
  157. package/src/features/apps/utils/app-marketplace-platform.utils.test.ts +59 -0
  158. package/src/features/apps/utils/app-marketplace-platform.utils.ts +33 -0
  159. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +6 -0
  160. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +6 -2
  161. package/src/features/chat/features/conversation/components/__tests__/session-queued-input-rows.test.tsx +46 -0
  162. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +15 -2
  163. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +5 -3
  164. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +59 -5
  165. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +35 -1
  166. package/src/features/chat/features/conversation/hooks/__tests__/use-session-run-queue.test.tsx +25 -19
  167. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +148 -134
  168. package/src/features/chat/features/conversation/hooks/use-session-pending-input-actions.ts +135 -0
  169. package/src/features/chat/features/conversation/hooks/use-session-run-queue.ts +19 -4
  170. package/src/features/chat/features/conversation/utils/__tests__/session-queued-input.utils.test.ts +52 -2
  171. package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +46 -5
  172. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
  173. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +102 -0
  174. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +117 -1
  175. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +2 -2
  176. package/src/features/chat/features/ncp/hooks/use-ncp-session-message-history.ts +31 -81
  177. package/src/features/chat/features/ncp/hooks/use-ncp-session-seed-loader.ts +134 -0
  178. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +1 -6
  179. package/src/features/panel-apps/components/__tests__/panel-app-list-item.test.tsx +8 -0
  180. package/src/features/panel-apps/components/__tests__/panel-app-main-sidebar-nav.test.tsx +99 -13
  181. package/src/features/panel-apps/components/panel-app-main-sidebar-menu-item.tsx +4 -0
  182. package/src/features/panel-apps/components/panel-app-main-sidebar-nav.tsx +166 -31
  183. package/src/shared/lib/api/utils/ncp-attachments.utils.ts +8 -1
  184. package/src/shared/lib/api/utils/ncp-session.utils.ts +1 -0
  185. package/src/shared/lib/i18n/locales/en-US/chat.json +3 -0
  186. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +11 -1
  187. package/src/shared/lib/i18n/locales/zh-CN/chat.json +3 -0
  188. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +11 -1
  189. package/src/vite-env.d.ts +9 -0
  190. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +0 -1
  191. package/dist/assets/channel-5T3pdR-s.js +0 -1
  192. package/dist/assets/chat-page-BXu3hnOK.js +0 -1
  193. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +0 -1
  194. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +0 -1
  195. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +0 -1
  196. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +0 -1
  197. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +0 -1
  198. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +0 -1
  199. package/dist/assets/desktop-update-config-Blol-Umu.js +0 -1
  200. package/dist/assets/doc-browser-BExPq1Hk.js +0 -1
  201. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +0 -1
  202. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +0 -1
  203. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +0 -1
  204. package/dist/assets/index-Btj8yN4T.css +0 -1
  205. package/dist/assets/index-CHcGqqBe.js +0 -109
  206. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +0 -1
  207. package/dist/assets/loader-circle-C75DUmmm.js +0 -1
  208. package/dist/assets/mcp-marketplace-page-CIMRYgOY.js +0 -9
  209. package/dist/assets/mcp-marketplace-page-D2NlhG1k.js +0 -1
  210. package/dist/assets/model-config-page-ChR-ENjp.js +0 -1
  211. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +0 -1
  212. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +0 -1
  213. package/dist/assets/plus-TzSIMKWW.js +0 -1
  214. package/dist/assets/provider-scoped-model-input-DiSpKSk0.js +0 -1
  215. package/dist/assets/providers-config-page-BvXa03cp.js +0 -1
  216. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +0 -1
  217. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +0 -1
  218. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +0 -1
  219. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +0 -1
  220. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +0 -1
  221. package/dist/assets/remote-BgYduCQR.js +0 -1
  222. package/dist/assets/rotate-cw-BT8HxlcB.js +0 -1
  223. package/dist/assets/runtime-config-page-BuYof2Ty.js +0 -1
  224. package/dist/assets/search-config-page-CQTJOsOf.js +0 -1
  225. package/dist/assets/security-config-CtJqaPVL.js +0 -1
  226. package/dist/assets/settings-2-BHGgNlOH.js +0 -1
  227. package/dist/assets/skeleton-CJLc1CWn.js +0 -1
  228. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +0 -1
  229. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +0 -8
  230. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +0 -1
  231. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +0 -1
  232. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +0 -1
  233. package/dist/assets/x-BMl7cucN.js +0 -1
  234. /package/dist/assets/{array-NADKd9aU.js → array-COEOjKUv.js} +0 -0
  235. /package/dist/assets/{chunk-KEIR6QF5-BuyuzJJL.js → chunk-KEIR6QF5-D7oHAcry.js} +0 -0
  236. /package/dist/assets/{chunk-Y2CYZVJY-D_B2ZRNt.js → chunk-Y2CYZVJY-08IUpgTP.js} +0 -0
  237. /package/dist/assets/{config-hints-BcfVUMij.js → config-hints-BoE41XDI.js} +0 -0
  238. /package/dist/assets/{cytoscape.esm-BANTlgza.js → cytoscape.esm-HifWufkp.js} +0 -0
  239. /package/dist/assets/{defaultLocale-C4_L0aEs.js → defaultLocale-CA-_SsC1.js} +0 -0
  240. /package/dist/assets/{dist-DAPEIJbz.js → dist-C60rvfy8.js} +0 -0
  241. /package/dist/assets/{graphlib-ChqXg0dI.js → graphlib-CGYCNsoJ.js} +0 -0
  242. /package/dist/assets/{host-capabilities-D9C4CBK5.js → host-capabilities-DPNRAjIj.js} +0 -0
  243. /package/dist/assets/{init-D8wZg9xq.js → init-C3plFVsG.js} +0 -0
  244. /package/dist/assets/{jszip.min-CkbBfwAQ.js → jszip.min-CeYs17Gn.js} +0 -0
  245. /package/dist/assets/{katex-TZJFa0J_.js → katex-DRThweSG.js} +0 -0
  246. /package/dist/assets/{middleware-B3k-TSmZ.js → middleware-D05fK5Sb.js} +0 -0
  247. /package/dist/assets/{path-DodfxcX7.js → path-BKtKxQ6Y.js} +0 -0
  248. /package/dist/assets/{rough.esm-CUaa1Rbe.js → rough.esm-DurL7-u7.js} +0 -0
  249. /package/dist/assets/{xlsx-CxByi7bR.js → xlsx-Sg8fZ6nH.js} +0 -0
@@ -1,20 +1,14 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
- import type { NcpMessage, NcpSessionMessagePageInfo } from "@nextclaw/ncp";
3
- import type { NcpConversationSeed } from "@nextclaw/ncp-react";
2
+ import type { NcpMessage } from "@nextclaw/ncp";
4
3
  import {
5
4
  fetchNcpSessionMessageDetail,
6
5
  fetchNcpSessionMessages,
7
6
  type SessionContextWindowView,
8
7
  } from "@/shared/lib/api";
9
-
10
- export const DEFAULT_NCP_SESSION_MESSAGE_LIMIT = 40;
11
-
12
- type NcpConversationSeedWithContextWindow = NcpConversationSeed & {
13
- contextWindow?: SessionContextWindowView | null;
14
- deferredToolPayloads: Record<string, { cursor: string }>;
15
- total: number;
16
- pageInfo: NcpSessionMessagePageInfo;
17
- };
8
+ import {
9
+ useNcpSessionSeedLoader,
10
+ type NcpConversationSeedWithContextWindow,
11
+ } from "@/features/chat/features/ncp/hooks/use-ncp-session-seed-loader";
18
12
 
19
13
  export type SessionMessageToolPayloadState = "summary" | "loading" | "ready" | "error";
20
14
 
@@ -44,48 +38,6 @@ const EMPTY_SESSION_HISTORY_STATE: SessionHistoryState = {
44
38
  messageDetailStates: {},
45
39
  };
46
40
 
47
- function isMissingNcpSessionError(error: unknown): boolean {
48
- return (
49
- error instanceof Error && error.message.includes("ncp session not found:")
50
- );
51
- }
52
-
53
- export async function fetchNcpSessionConversationSeed(
54
- sessionId: string,
55
- signal: AbortSignal,
56
- messageLimit = DEFAULT_NCP_SESSION_MESSAGE_LIMIT,
57
- ): Promise<NcpConversationSeedWithContextWindow> {
58
- signal.throwIfAborted();
59
- try {
60
- const response = await fetchNcpSessionMessages(sessionId, {
61
- limit: messageLimit,
62
- toolPayload: "summary",
63
- signal,
64
- });
65
- signal.throwIfAborted();
66
- return {
67
- messages: response.messages,
68
- status: response.status ?? "idle",
69
- contextWindow: response.contextWindow ?? null,
70
- deferredToolPayloads: response.deferredToolPayloads ?? {},
71
- total: response.total,
72
- pageInfo: response.pageInfo,
73
- };
74
- } catch (error) {
75
- signal.throwIfAborted();
76
- if (!isMissingNcpSessionError(error)) {
77
- throw error;
78
- }
79
- return {
80
- messages: [],
81
- status: "idle",
82
- total: 0,
83
- deferredToolPayloads: {},
84
- pageInfo: { startCursor: null, hasPreviousPage: false },
85
- };
86
- }
87
- }
88
-
89
41
  type UpdateSessionHistoryState = (
90
42
  sessionId: string,
91
43
  update: (current: SessionHistoryState) => SessionHistoryState,
@@ -194,35 +146,33 @@ export function useNcpSessionMessageHistory(params: {
194
146
  sessionId,
195
147
  updateHistoryState,
196
148
  });
197
- const loadSeed = useCallback(
198
- async (targetSessionId: string, signal: AbortSignal) => {
199
- void hydrationRetryVersion;
200
- const seed = await fetchNcpSessionConversationSeed(
201
- targetSessionId,
202
- signal,
203
- messageLimit,
204
- );
205
- if (!signal.aborted) {
206
- updateHistoryState(targetSessionId, (current) => ({
207
- ...current,
208
- contextWindow: seed.contextWindow ?? null,
209
- deferredToolPayloads: seed.deferredToolPayloads,
210
- messageDetails: {},
211
- messageDetailStates: Object.fromEntries(
212
- Object.keys(seed.deferredToolPayloads).map((messageId) => [messageId, "summary"]),
213
- ),
214
- total: seed.total,
215
- cursor: seed.pageInfo.startCursor,
216
- hasPreviousPage: seed.pageInfo.hasPreviousPage,
217
- error: null,
218
- }));
219
- }
220
- return { messages: seed.messages, status: seed.status };
221
- },
222
- [hydrationRetryVersion, messageLimit, updateHistoryState],
223
- );
149
+ const onSeedLoaded = useCallback((
150
+ targetSessionId: string,
151
+ seed: NcpConversationSeedWithContextWindow,
152
+ ) => {
153
+ updateHistoryState(targetSessionId, (current) => ({
154
+ ...current,
155
+ contextWindow: seed.contextWindow ?? null,
156
+ deferredToolPayloads: seed.deferredToolPayloads,
157
+ messageDetails: {},
158
+ messageDetailStates: Object.fromEntries(
159
+ Object.keys(seed.deferredToolPayloads).map((messageId) => [messageId, "summary"]),
160
+ ),
161
+ total: seed.total,
162
+ cursor: seed.pageInfo.startCursor,
163
+ hasPreviousPage: seed.pageInfo.hasPreviousPage,
164
+ error: null,
165
+ }));
166
+ }, [updateHistoryState]);
167
+ const loadSeed = useNcpSessionSeedLoader({
168
+ hydrationRetryVersion,
169
+ messageLimit,
170
+ onSeedLoaded,
171
+ });
224
172
  const loadPreviousMessages = useCallback(
225
- async (prependHistory: (messages: ReadonlyArray<NcpMessage>) => void) => {
173
+ async (
174
+ prependHistory: (messages: ReadonlyArray<NcpMessage>) => void,
175
+ ) => {
226
176
  const history = historyStateRef.current;
227
177
  if (
228
178
  !sessionId ||
@@ -0,0 +1,134 @@
1
+ import { useCallback, useRef } from "react";
2
+ import type { NcpSessionMessagePageInfo } from "@nextclaw/ncp";
3
+ import type { NcpConversationSeed } from "@nextclaw/ncp-react";
4
+ import {
5
+ fetchNcpSessionMessages,
6
+ type NcpSessionMessagesView,
7
+ type SessionContextWindowView,
8
+ } from "@/shared/lib/api";
9
+
10
+ export const DEFAULT_NCP_SESSION_MESSAGE_LIMIT = 20;
11
+
12
+ export type NcpConversationSeedWithContextWindow = NcpConversationSeed & {
13
+ contextWindow?: SessionContextWindowView | null;
14
+ deferredToolPayloads: Record<string, { cursor: string }>;
15
+ total: number;
16
+ pageInfo: NcpSessionMessagePageInfo;
17
+ };
18
+
19
+ function isMissingNcpSessionError(error: unknown): boolean {
20
+ return error instanceof Error && error.message.includes("ncp session not found:");
21
+ }
22
+
23
+ function isRecord(value: unknown): value is Record<string, unknown> {
24
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
25
+ }
26
+
27
+ async function waitForPrefetch<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {
28
+ signal.throwIfAborted();
29
+ return await new Promise<T>((resolve, reject) => {
30
+ const abort = () => reject(new DOMException("Aborted", "AbortError"));
31
+ signal.addEventListener("abort", abort, { once: true });
32
+ void promise.then(resolve, reject).finally(() => {
33
+ signal.removeEventListener("abort", abort);
34
+ });
35
+ });
36
+ }
37
+
38
+ async function takeInitialSessionMessagesPrefetch(params: {
39
+ sessionId: string;
40
+ messageLimit: number;
41
+ signal: AbortSignal;
42
+ }): Promise<NcpSessionMessagesView | null> {
43
+ const { messageLimit, sessionId, signal } = params;
44
+ const prefetch = window.__NEXTCLAW_INITIAL_SESSION_MESSAGES_PREFETCH__;
45
+ if (
46
+ !prefetch ||
47
+ prefetch.consumed ||
48
+ prefetch.sessionId !== sessionId ||
49
+ prefetch.limit !== messageLimit
50
+ ) {
51
+ return null;
52
+ }
53
+ prefetch.consumed = true;
54
+ const result = await waitForPrefetch(prefetch.promise, signal);
55
+ signal.throwIfAborted();
56
+ if (!result?.responseOk || !isRecord(result.payload)) return null;
57
+ const { payload } = result;
58
+ if (payload.ok !== true || !isRecord(payload.data)) return null;
59
+ const { data } = payload;
60
+ if (
61
+ data.sessionId !== sessionId ||
62
+ !Array.isArray(data.messages) ||
63
+ !isRecord(data.pageInfo)
64
+ ) {
65
+ return null;
66
+ }
67
+ return data as NcpSessionMessagesView;
68
+ }
69
+
70
+ export async function fetchNcpSessionConversationSeed(
71
+ sessionId: string,
72
+ signal: AbortSignal,
73
+ messageLimit = DEFAULT_NCP_SESSION_MESSAGE_LIMIT,
74
+ payloadMode: "compact" | "standard" = "compact",
75
+ ): Promise<NcpConversationSeedWithContextWindow> {
76
+ signal.throwIfAborted();
77
+ try {
78
+ const prefetchedResponse = payloadMode === "compact"
79
+ ? await takeInitialSessionMessagesPrefetch({ sessionId, messageLimit, signal })
80
+ : null;
81
+ const response = prefetchedResponse ?? await fetchNcpSessionMessages(sessionId, {
82
+ limit: messageLimit,
83
+ toolPayload: "summary",
84
+ initialPayload: payloadMode === "compact" ? "compact" : undefined,
85
+ signal,
86
+ });
87
+ signal.throwIfAborted();
88
+ return {
89
+ messages: response.messages,
90
+ status: response.status ?? "idle",
91
+ contextWindow: response.contextWindow ?? null,
92
+ deferredToolPayloads: response.deferredToolPayloads ?? {},
93
+ total: response.total,
94
+ pageInfo: response.pageInfo,
95
+ };
96
+ } catch (error) {
97
+ signal.throwIfAborted();
98
+ if (!isMissingNcpSessionError(error)) throw error;
99
+ return {
100
+ messages: [],
101
+ status: "idle",
102
+ total: 0,
103
+ deferredToolPayloads: {},
104
+ pageInfo: { startCursor: null, hasPreviousPage: false },
105
+ };
106
+ }
107
+ }
108
+
109
+ export function useNcpSessionSeedLoader(params: {
110
+ hydrationRetryVersion: number;
111
+ messageLimit: number;
112
+ onSeedLoaded: (
113
+ sessionId: string,
114
+ seed: NcpConversationSeedWithContextWindow,
115
+ ) => void;
116
+ }) {
117
+ const { hydrationRetryVersion, messageLimit, onSeedLoaded } = params;
118
+ const hydratedSessionsRef = useRef(new Set<string>());
119
+ return useCallback(async (sessionId: string, signal: AbortSignal) => {
120
+ void hydrationRetryVersion;
121
+ const payloadMode = hydratedSessionsRef.current.has(sessionId) ? "standard" : "compact";
122
+ const seed = await fetchNcpSessionConversationSeed(
123
+ sessionId,
124
+ signal,
125
+ messageLimit,
126
+ payloadMode,
127
+ );
128
+ if (!signal.aborted) {
129
+ hydratedSessionsRef.current.add(sessionId);
130
+ onSeedLoaded(sessionId, seed);
131
+ }
132
+ return { messages: seed.messages, status: seed.status };
133
+ }, [hydrationRetryVersion, messageLimit, onSeedLoaded]);
134
+ }
@@ -7,7 +7,7 @@ import type {
7
7
  SessionEntryView,
8
8
  ThinkingLevel
9
9
  } from '@/shared/lib/api';
10
- import { API_BASE } from '@/shared/lib/api';
10
+ import { buildNcpAssetContentUrl } from '@/shared/lib/api';
11
11
  import {
12
12
  getSessionProjectName,
13
13
  normalizeSessionProjectRootValue,
@@ -29,11 +29,6 @@ const SESSION_ACTIVITY_PREVIEW_STATUS_KIND_SET = new Set<NonNullable<SessionActi
29
29
  'run-interrupted'
30
30
  ]);
31
31
 
32
- function buildNcpAssetContentUrl(assetUri: string): string {
33
- const query = new URLSearchParams({ uri: assetUri });
34
- return `${API_BASE}/api/ncp/assets/content?${query.toString()}`;
35
- }
36
-
37
32
  function readOptionalString(value: unknown): string | null {
38
33
  if (typeof value !== 'string') {
39
34
  return null;
@@ -1,6 +1,8 @@
1
1
  import { render, screen } from '@testing-library/react';
2
2
  import userEvent from '@testing-library/user-event';
3
3
  import { beforeEach, describe, expect, it, vi } from 'vitest';
4
+ import { viewportLayoutManager } from '@/app/managers/viewport-layout.manager';
5
+ import { useViewportLayoutStore } from '@/app/stores/viewport-layout.store';
4
6
  import { PanelAppListItem } from '@/features/panel-apps/components/panel-app-list-item';
5
7
 
6
8
  const mainSidebarMutation = vi.hoisted(() => ({
@@ -32,6 +34,8 @@ const baseEntry = {
32
34
 
33
35
  describe('PanelAppListItem', () => {
34
36
  beforeEach(() => {
37
+ window.localStorage.clear();
38
+ viewportLayoutManager.resetForTests();
35
39
  mainSidebarMutation.isPending = false;
36
40
  mainSidebarMutation.mutate.mockReset();
37
41
  });
@@ -79,6 +83,7 @@ describe('PanelAppListItem', () => {
79
83
 
80
84
  it('keeps main-sidebar placement in the low-frequency more-actions menu', async () => {
81
85
  const user = userEvent.setup();
86
+ viewportLayoutManager.setMainSidebarAppGroupCollapsed(true);
82
87
  const { rerender } = render(
83
88
  <PanelAppListItem
84
89
  deletePending={false}
@@ -97,6 +102,9 @@ describe('PanelAppListItem', () => {
97
102
  id: 'demo',
98
103
  preferences: { mainSidebar: true },
99
104
  });
105
+ expect(
106
+ useViewportLayoutStore.getState().isMainSidebarAppGroupCollapsed,
107
+ ).toBe(false);
100
108
 
101
109
  rerender(
102
110
  <PanelAppListItem
@@ -2,6 +2,8 @@ import { render, screen } from "@testing-library/react";
2
2
  import userEvent from "@testing-library/user-event";
3
3
  import { MemoryRouter } from "react-router-dom";
4
4
  import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { viewportLayoutManager } from "@/app/managers/viewport-layout.manager";
6
+ import { useViewportLayoutStore } from "@/app/stores/viewport-layout.store";
5
7
  import {
6
8
  getMainSidebarPanelApps,
7
9
  PanelAppMainSidebarNav,
@@ -21,7 +23,9 @@ vi.mock("@/features/panel-apps/hooks/use-panel-apps", () => ({
21
23
  }),
22
24
  }));
23
25
 
24
- function createEntry(overrides: Partial<PanelAppEntryView> = {}): PanelAppEntryView {
26
+ function createEntry(
27
+ overrides: Partial<PanelAppEntryView> = {},
28
+ ): PanelAppEntryView {
25
29
  return {
26
30
  appId: "demo",
27
31
  clientDeclared: false,
@@ -43,6 +47,8 @@ function createEntry(overrides: Partial<PanelAppEntryView> = {}): PanelAppEntryV
43
47
 
44
48
  describe("PanelAppMainSidebarNav", () => {
45
49
  beforeEach(() => {
50
+ window.localStorage.clear();
51
+ viewportLayoutManager.resetForTests();
46
52
  mocks.entries = [];
47
53
  mocks.mutate.mockReset();
48
54
  });
@@ -50,19 +56,32 @@ describe("PanelAppMainSidebarNav", () => {
50
56
  it("projects only manually added apps in persisted order", () => {
51
57
  const entries = [
52
58
  createEntry({ appId: "hidden", title: "Hidden" }),
53
- createEntry({ appId: "second", mainSidebar: true, mainSidebarOrder: 1, title: "Second" }),
54
- createEntry({ appId: "first", mainSidebar: true, mainSidebarOrder: 0, title: "First" }),
59
+ createEntry({
60
+ appId: "second",
61
+ mainSidebar: true,
62
+ mainSidebarOrder: 1,
63
+ title: "Second",
64
+ }),
65
+ createEntry({
66
+ appId: "first",
67
+ mainSidebar: true,
68
+ mainSidebarOrder: 0,
69
+ title: "First",
70
+ }),
55
71
  ];
56
72
 
57
- expect(getMainSidebarPanelApps(entries).map((entry) => entry.appId)).toEqual([
58
- "first",
59
- "second",
60
- ]);
73
+ expect(
74
+ getMainSidebarPanelApps(entries).map((entry) => entry.appId),
75
+ ).toEqual(["first", "second"]);
61
76
  });
62
77
 
63
78
  it("renders stable app routes and hides unbound apps", () => {
64
79
  mocks.entries = [
65
- createEntry({ appId: "publisher.todo", mainSidebar: true, title: "Todo" }),
80
+ createEntry({
81
+ appId: "publisher.todo",
82
+ mainSidebar: true,
83
+ title: "Todo",
84
+ }),
66
85
  createEntry({ appId: "hidden", title: "Hidden" }),
67
86
  ];
68
87
 
@@ -76,12 +95,62 @@ describe("PanelAppMainSidebarNav", () => {
76
95
  expect(link.getAttribute("href")).toBe("/apps/panel/publisher.todo");
77
96
  expect(link.getAttribute("aria-current")).toBe("page");
78
97
  expect(screen.queryByText("Hidden")).toBeNull();
98
+ const groupButton = screen.getByRole("button", { name: "Collapse apps" });
99
+ expect(groupButton.getAttribute("aria-expanded")).toBe("true");
100
+ expect(groupButton.className).toContain("text-[13px]");
101
+ expect(groupButton.className).toContain("font-medium");
102
+ expect(groupButton.className).not.toContain("text-[11px]");
103
+ });
104
+
105
+ it("collapses the ordered app group and persists the device layout preference", async () => {
106
+ const user = userEvent.setup();
107
+ mocks.entries = [
108
+ createEntry({
109
+ appId: "first",
110
+ mainSidebar: true,
111
+ mainSidebarOrder: 0,
112
+ title: "First",
113
+ }),
114
+ createEntry({
115
+ appId: "second",
116
+ mainSidebar: true,
117
+ mainSidebarOrder: 1,
118
+ title: "Second",
119
+ }),
120
+ ];
121
+
122
+ render(
123
+ <MemoryRouter>
124
+ <PanelAppMainSidebarNav isCollapsed={false} />
125
+ </MemoryRouter>,
126
+ );
127
+
128
+ await user.click(screen.getByRole("button", { name: "Collapse apps" }));
129
+
130
+ expect(screen.queryByRole("link", { name: "First" })).toBeNull();
131
+ expect(screen.queryByRole("link", { name: "Second" })).toBeNull();
132
+ expect(
133
+ screen
134
+ .getByRole("button", { name: "Expand apps" })
135
+ .getAttribute("aria-expanded"),
136
+ ).toBe("false");
137
+ expect(
138
+ useViewportLayoutStore.getState().isMainSidebarAppGroupCollapsed,
139
+ ).toBe(true);
140
+ const persisted = JSON.parse(
141
+ window.localStorage.getItem("nextclaw.app.viewport-layout") ?? "{}",
142
+ ) as { state?: { isMainSidebarAppGroupCollapsed?: boolean } };
143
+ expect(persisted.state?.isMainSidebarAppGroupCollapsed).toBe(true);
79
144
  });
80
145
 
81
146
  it("removes an expanded entry from its hover actions without nesting the button in the link", async () => {
82
147
  const user = userEvent.setup();
83
148
  mocks.entries = [
84
- createEntry({ appId: "publisher.todo", mainSidebar: true, title: "Todo" }),
149
+ createEntry({
150
+ appId: "publisher.todo",
151
+ mainSidebar: true,
152
+ title: "Todo",
153
+ }),
85
154
  ];
86
155
 
87
156
  render(
@@ -98,7 +167,9 @@ describe("PanelAppMainSidebarNav", () => {
98
167
  expect(menuButton.className).toContain("group-hover/panel-app:opacity-100");
99
168
 
100
169
  await user.click(menuButton);
101
- await user.click(screen.getByRole("button", { name: "Remove from main sidebar" }));
170
+ await user.click(
171
+ screen.getByRole("button", { name: "Remove from main sidebar" }),
172
+ );
102
173
 
103
174
  expect(mocks.mutate).toHaveBeenCalledWith({
104
175
  id: "demo",
@@ -107,9 +178,19 @@ describe("PanelAppMainSidebarNav", () => {
107
178
  expect(link.getAttribute("aria-current")).toBe("page");
108
179
  });
109
180
 
110
- it("keeps the collapsed rail as one navigation target", () => {
181
+ it("aggregates collapsed-rail apps behind one navigation menu", async () => {
182
+ const user = userEvent.setup();
111
183
  mocks.entries = [
112
- createEntry({ appId: "publisher.todo", mainSidebar: true, title: "Todo" }),
184
+ createEntry({
185
+ appId: "publisher.todo",
186
+ mainSidebar: true,
187
+ title: "Todo",
188
+ }),
189
+ createEntry({
190
+ appId: "publisher.notes",
191
+ mainSidebar: true,
192
+ title: "Notes",
193
+ }),
113
194
  ];
114
195
 
115
196
  render(
@@ -118,8 +199,13 @@ describe("PanelAppMainSidebarNav", () => {
118
199
  </MemoryRouter>,
119
200
  );
120
201
 
202
+ expect(screen.queryByRole("link", { name: "Todo" })).toBeNull();
203
+ expect(
204
+ screen.queryByRole("button", { name: "More panel app actions: Todo" }),
205
+ ).toBeNull();
206
+ await user.click(screen.getByRole("button", { name: "Apps" }));
121
207
  expect(screen.getByRole("link", { name: "Todo" })).toBeTruthy();
122
- expect(screen.queryByRole("button", { name: "More panel app actions: Todo" })).toBeNull();
208
+ expect(screen.getByRole("link", { name: "Notes" })).toBeTruthy();
123
209
  });
124
210
 
125
211
  it("renders no section when no app was manually added", () => {
@@ -1,4 +1,5 @@
1
1
  import { PanelLeft, PanelLeftDashed } from 'lucide-react';
2
+ import { viewportLayoutManager } from '@/app/managers/viewport-layout.manager';
2
3
  import { useUpdatePanelAppPreferences } from '@/features/panel-apps/hooks/use-panel-apps';
3
4
  import type { PanelAppEntryView } from '@/shared/lib/api';
4
5
  import { t } from '@/shared/lib/i18n';
@@ -24,6 +25,9 @@ export function PanelAppMainSidebarMenuItem({
24
25
  className="flex w-full items-center gap-2 rounded-lg px-2.5 py-2 text-left text-sm text-muted-foreground transition-colors hover:bg-[var(--interaction-hover)] hover:text-accent-foreground disabled:cursor-not-allowed disabled:opacity-50"
25
26
  onClick={() => {
26
27
  onSelect?.();
28
+ if (!entry.mainSidebar) {
29
+ viewportLayoutManager.setMainSidebarAppGroupCollapsed(false);
30
+ }
27
31
  updatePreferences.mutate({
28
32
  id: entry.id,
29
33
  preferences: { mainSidebar: !entry.mainSidebar },