@nextclaw/ui 0.15.9 → 0.15.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (360) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-BYpK0F5F.js → abnfDiagram-VRR7QNED-M4g_AyR8.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-D87ywFdC.js → aiden0z-pptx-renderer.es-Bd99vut2.js} +1 -1
  4. package/dist/assets/api-pbx14YAL.js +13 -0
  5. package/dist/assets/app-presenter-provider-Q6u2xBAn.js +106 -0
  6. package/dist/assets/appearance-settings-page-D9wJdf-0.js +1 -0
  7. package/dist/assets/{arc-DFELHWM2.js → arc-IO7WNWOB.js} +1 -1
  8. package/dist/assets/architecture-TIHT7OUA-Cii4JyA2.js +1 -0
  9. package/dist/assets/{architectureDiagram-ZJ3FMSHR-CvRhdovL.js → architectureDiagram-ZJ3FMSHR-Dc-lvdUh.js} +1 -1
  10. package/dist/assets/{blockDiagram-677ZJIJ3-DVYNkbR_.js → blockDiagram-677ZJIJ3-CcsoWpIY.js} +1 -1
  11. package/dist/assets/{book-open-w3160PZf.js → book-open-DZyssdCc.js} +1 -1
  12. package/dist/assets/{c4Diagram-LMCZKHZV-B1w-iwRe.js → c4Diagram-LMCZKHZV-Bsrtg6b2.js} +1 -1
  13. package/dist/assets/card-D7l-gWkY.js +1 -0
  14. package/dist/assets/channel-BSKkTPe8.js +1 -0
  15. package/dist/assets/channels-list-page-B3qW3T-B.js +8 -0
  16. package/dist/assets/chat-page-DUI_Jm6r.js +1 -0
  17. package/dist/assets/chunk-2Q5K7J3B-CDMOR1xZ.js +1 -0
  18. package/dist/assets/{chunk-32BRIVSS-kG3lg0bC.js → chunk-32BRIVSS-Bt08t8oG.js} +1 -1
  19. package/dist/assets/{chunk-52WLFC77-BLeB7iGk.js → chunk-52WLFC77-Ddx68Xzx.js} +1 -1
  20. package/dist/assets/{chunk-5FCAYU7R-BQga1Rr7.js → chunk-5FCAYU7R-B7p9_q55.js} +1 -1
  21. package/dist/assets/{chunk-5HE753X5-DU81hkcF.js → chunk-5HE753X5-D52KvdTm.js} +1 -1
  22. package/dist/assets/{chunk-5JV3BV7I-C2bAx0m7.js → chunk-5JV3BV7I-C329h8nI.js} +1 -1
  23. package/dist/assets/{chunk-5TONJI2A-BExIIgWY.js → chunk-5TONJI2A-BZyUBxWt.js} +1 -1
  24. package/dist/assets/{chunk-5VM5RSS4-C3XB05yh.js → chunk-5VM5RSS4-BUiYXZmu.js} +1 -1
  25. package/dist/assets/{chunk-7BUUIJ7U-JiyRtrGu.js → chunk-7BUUIJ7U-B7o-mn4r.js} +1 -1
  26. package/dist/assets/{chunk-BIQX33UG-BZRV20pI.js → chunk-BIQX33UG-Bey9GoRP.js} +1 -1
  27. package/dist/assets/{chunk-C7G6YPKG-AsyDSQ9j.js → chunk-C7G6YPKG-C6tosszM.js} +1 -1
  28. package/dist/assets/{chunk-CQNSW5MT-iN-fiXVA.js → chunk-CQNSW5MT-BNrdkgGQ.js} +1 -1
  29. package/dist/assets/{chunk-CYSBUYHQ-DhC_TRzI.js → chunk-CYSBUYHQ-DHODFeyB.js} +1 -1
  30. package/dist/assets/{chunk-EMLP6XTP-QFLK8R6d.js → chunk-EMLP6XTP-BcmLjrcq.js} +1 -1
  31. package/dist/assets/{chunk-EX3LRPZG-DzUwhkCz.js → chunk-EX3LRPZG-r-_eEtJI.js} +1 -1
  32. package/dist/assets/{chunk-FWX5IMBZ-DFelpZBy.js → chunk-FWX5IMBZ-DOObDWbf.js} +2 -2
  33. package/dist/assets/{chunk-HOUHSVGY-BMPwFEwN.js → chunk-HOUHSVGY-Bd0FnbWZ.js} +1 -1
  34. package/dist/assets/{chunk-ICXQ74PX-DH8NJMok.js → chunk-ICXQ74PX-ntNwAm4_.js} +1 -1
  35. package/dist/assets/{chunk-JG7HCLWE-D-95AsO4.js → chunk-JG7HCLWE-YtRpblZN.js} +1 -1
  36. package/dist/assets/{chunk-JWPE2WC7-DIaTv8kx.js → chunk-JWPE2WC7-De5-D9ta.js} +1 -1
  37. package/dist/assets/{chunk-MOJQB5TN-BPFLzWgj.js → chunk-MOJQB5TN-CdvKhyp2.js} +1 -1
  38. package/dist/assets/{chunk-MOZMSUNE-DR_fzxG0.js → chunk-MOZMSUNE-DJMMN_lu.js} +1 -1
  39. package/dist/assets/{chunk-OGEWGWER-TIcEoPPH.js → chunk-OGEWGWER-DOFWVw3O.js} +1 -1
  40. package/dist/assets/{chunk-OSBZ3O6U-BALNvSfG.js → chunk-OSBZ3O6U-DZkrIN9r.js} +1 -1
  41. package/dist/assets/{chunk-PUDLZKDR-I-EXhf_k.js → chunk-PUDLZKDR-D1wma2Yf.js} +1 -1
  42. package/dist/assets/{chunk-Q4XR5HBZ-uvIGstBS.js → chunk-Q4XR5HBZ-CjPy94Lz.js} +1 -1
  43. package/dist/assets/{chunk-QBLGF6JB-r-TjLdxa.js → chunk-QBLGF6JB-Hy1bEgS-.js} +1 -1
  44. package/dist/assets/{chunk-R7FJI6CG-N-1LDgvy.js → chunk-R7FJI6CG-CUPIHyh-.js} +1 -1
  45. package/dist/assets/{chunk-RYQCIY6F-jxNASx65.js → chunk-RYQCIY6F-D5Ua4pcv.js} +1 -1
  46. package/dist/assets/{chunk-U6XO7XAA-Cphbuc7q.js → chunk-U6XO7XAA-DaUQcqb7.js} +1 -1
  47. package/dist/assets/{chunk-V7JOEXUC-BLEuBf1s.js → chunk-V7JOEXUC-DX2zLfQe.js} +1 -1
  48. package/dist/assets/chunk-VAUOI2AC-BRvqNW76.js +1 -0
  49. package/dist/assets/{chunk-VR4S4FIN-BAUbR0ke.js → chunk-VR4S4FIN-CFA-WQik.js} +1 -1
  50. package/dist/assets/{chunk-WYO6CB5R-Do6SJrnY.js → chunk-WYO6CB5R-Bk2HeDvJ.js} +2 -2
  51. package/dist/assets/chunk-XXDRQBXY-CwX_mqJ3.js +1 -0
  52. package/dist/assets/{chunk-YOTPTUD7-CQqvMTCr.js → chunk-YOTPTUD7-DUG4FpBN.js} +1 -1
  53. package/dist/assets/{chunk-ZGVPDNZ5-B7kWWaXv.js → chunk-ZGVPDNZ5-70JdIGo0.js} +1 -1
  54. package/dist/assets/{chunk-ZIRB5QZD-DTU0cDNh.js → chunk-ZIRB5QZD-XZdgBt5L.js} +1 -1
  55. package/dist/assets/classDiagram-OUVF2IWQ-RzbGxG77.js +1 -0
  56. package/dist/assets/classDiagram-v2-EOCWNBFH-C_gWB7WT.js +1 -0
  57. package/dist/assets/config-split-page-DFEKStD7.js +1 -0
  58. package/dist/assets/confirm-dialog-7wpxfMCp.js +5 -0
  59. package/dist/assets/{cose-bilkent-JH36ORCC-B9LrtV-q.js → cose-bilkent-JH36ORCC-B-PQoGX-.js} +1 -1
  60. package/dist/assets/cynefin-VYW2F7L2-fSLIS0X3.js +1 -0
  61. package/dist/assets/{cynefinDiagram-TSTJHNR4-BfjvW4su.js → cynefinDiagram-TSTJHNR4-BoLCPEM0.js} +1 -1
  62. package/dist/assets/{dagre-DkiXZKkL.js → dagre-D7Ie4eXr.js} +1 -1
  63. package/dist/assets/{dagre-VKFMJZFB-B1pQKQSY.js → dagre-VKFMJZFB-CKRrwZA-.js} +1 -1
  64. package/dist/assets/desktop-update-config-DP6IbxBO.js +1 -0
  65. package/dist/assets/{diagram-FQU43EPY-cB4EZdaP.js → diagram-FQU43EPY-CASL1J3_.js} +1 -1
  66. package/dist/assets/{diagram-G47NLZAW-CtW8im1B.js → diagram-G47NLZAW-CvLPBIzf.js} +1 -1
  67. package/dist/assets/{diagram-NH7WQ7WH-BDCtrCCm.js → diagram-NH7WQ7WH-Ba6fGdYP.js} +1 -1
  68. package/dist/assets/{diagram-OA4YK3LP-DEMJ42Gt.js → diagram-OA4YK3LP-DkpfgEjM.js} +1 -1
  69. package/dist/assets/{diagram-WEI45ONY-CdCcxSYU.js → diagram-WEI45ONY-BP5UMVsN.js} +1 -1
  70. package/dist/assets/{dist-DEJlYi0E.js → dist-B9UN0HrS.js} +1 -1
  71. package/dist/assets/doc-browser-D7klPMHs.js +1 -0
  72. package/dist/assets/{doc-browser-context-CpvHiuQG.js → doc-browser-context-Bw9m83Jp.js} +1 -1
  73. package/dist/assets/doc-browser-wgEeLkn1.js +1 -0
  74. package/dist/assets/{docx-preview-zis3U0Fa.js → docx-preview-D0tp3a9T.js} +1 -1
  75. package/dist/assets/{ebnfDiagram-CCIWWBDH-DBZETBwE.js → ebnfDiagram-CCIWWBDH-TJKbUcmc.js} +1 -1
  76. package/dist/assets/{erDiagram-Q63AITRT-y2zDXprP.js → erDiagram-Q63AITRT-DzGPKkKH.js} +1 -1
  77. package/dist/assets/eventmodeling-45OFAUF4-XVcQGgha.js +1 -0
  78. package/dist/assets/eye-IVMB0Rrz.js +1 -0
  79. package/dist/assets/flowDiagram-23GEKE2U-BiFSctqz.js +1 -0
  80. package/dist/assets/{form-actions-5OsDRt0g.js → form-actions-DrFAgO58.js} +1 -1
  81. package/dist/assets/{ganttDiagram-NO4QXBWP-D2DP344e.js → ganttDiagram-NO4QXBWP-JhJzsGp2.js} +1 -1
  82. package/dist/assets/gitGraph-TEB2WS4Q-C014uXqS.js +1 -0
  83. package/dist/assets/{gitGraphDiagram-IHSO6WYX-D37uiwA2.js → gitGraphDiagram-IHSO6WYX-DZ1CKMj8.js} +1 -1
  84. package/dist/assets/index-D297NGOb.js +2 -0
  85. package/dist/assets/index-DqZoodZL.css +1 -0
  86. package/dist/assets/info-DKCQHKI2-CQ2qjGhU.js +1 -0
  87. package/dist/assets/{infoDiagram-FWYZ7A6U-CG-WAIRi.js → infoDiagram-FWYZ7A6U-BNXNKfRQ.js} +1 -1
  88. package/dist/assets/{ishikawaDiagram-FXEZZL3T-WeCTnW0f.js → ishikawaDiagram-FXEZZL3T-DoiEfjNw.js} +1 -1
  89. package/dist/assets/{journeyDiagram-5HDEW3XC-DUFBW9Td.js → journeyDiagram-5HDEW3XC-D6oK2HSy.js} +1 -1
  90. package/dist/assets/{kanban-definition-HUTT4EX6-B0lxgMpg.js → kanban-definition-HUTT4EX6-B2UnLOH-.js} +1 -1
  91. package/dist/assets/{line-B78frP_c.js → line-BM7PoxY5.js} +1 -1
  92. package/dist/assets/{linear-C6lVEcb2.js → linear-B3DiqMjk.js} +1 -1
  93. package/dist/assets/loader-circle-DDgFo4Kv.js +1 -0
  94. package/dist/assets/{logo-badge-TdPa95AC.js → logo-badge-DoZOVuPr.js} +1 -1
  95. package/dist/assets/{map-Bdri5UL5.js → map-BAAdHTqR.js} +1 -1
  96. package/dist/assets/mcp-marketplace-page-BtNExfMn.js +1 -0
  97. package/dist/assets/{mcp-marketplace-page-cm3ifaKu.js → mcp-marketplace-page-C151EGP-.js} +2 -2
  98. package/dist/assets/{mermaid-parser.core-Cue18vLo.js → mermaid-parser.core-BeMviefY.js} +2 -2
  99. package/dist/assets/{mermaid.core-CXwWRh5i.js → mermaid.core-D0Q77r9I.js} +3 -3
  100. package/dist/assets/{mindmap-definition-LN4V7U3C-CFxz0P0-.js → mindmap-definition-LN4V7U3C-BQ7r7bR8.js} +1 -1
  101. package/dist/assets/model-config-page-BmfQzc52.js +1 -0
  102. package/dist/assets/navigation-link-CJw6FLOj.js +1 -0
  103. package/dist/assets/{ordinal-Cbd8t4LB.js → ordinal-Bt2N4IkA.js} +1 -1
  104. package/dist/assets/packet-7NZHBO7P-6IFyydij.js +1 -0
  105. package/dist/assets/{pegDiagram-2B236MQR-B_Qt9zyT.js → pegDiagram-2B236MQR-B-C9-LGz.js} +1 -1
  106. package/dist/assets/pie-RZYD4A2V-spXQC_wr.js +1 -0
  107. package/dist/assets/{pieDiagram-ENE6RG2P-CRQdSuwS.js → pieDiagram-ENE6RG2P-CtirZLtn.js} +1 -1
  108. package/dist/assets/plus-CYxLSfLh.js +1 -0
  109. package/dist/assets/{popover-EulxhA0q.js → popover-DitLJE6k.js} +1 -1
  110. package/dist/assets/provider-scoped-model-input-CuAtduIc.js +1 -0
  111. package/dist/assets/providers-config-page-CrmuY4DC.js +1 -0
  112. package/dist/assets/{quadrantDiagram-ABIIQ3AL-D5kPpttq.js → quadrantDiagram-ABIIQ3AL-DiBWAzDU.js} +1 -1
  113. package/dist/assets/radar-I7S5WNFK-DWVA8lvY.js +1 -0
  114. package/dist/assets/railroad-3IZDKUUU-Ce51u8y1.js +1 -0
  115. package/dist/assets/railroad-abnf-AHOZXSZD-D7i7cqI_.js +1 -0
  116. package/dist/assets/railroad-ebnf-EBAXGLYW-BEg2hcPs.js +1 -0
  117. package/dist/assets/railroad-peg-LSFZ7HO6-CxROSzbi.js +1 -0
  118. package/dist/assets/{railroadDiagram-RFXS5EU6-DGh_TGlQ.js → railroadDiagram-RFXS5EU6-CP6b3PBI.js} +1 -1
  119. package/dist/assets/{react-Bb0EYsLS.js → react-CTDaaQ9V.js} +1 -1
  120. package/dist/assets/{refresh-cw-CexL11m1.js → refresh-cw-E5qTlQY6.js} +1 -1
  121. package/dist/assets/remote-YzPpOPmK.js +1 -0
  122. package/dist/assets/{requirementDiagram-TGXJPOKE-EmFph0H_.js → requirementDiagram-TGXJPOKE-D8YV0-Jz.js} +1 -1
  123. package/dist/assets/rotate-cw-BXeiDT6C.js +1 -0
  124. package/dist/assets/runtime-config-page-1fPJJvo5.js +1 -0
  125. package/dist/assets/{sankeyDiagram-HTMAVEWB-BXWB5Ugi.js → sankeyDiagram-HTMAVEWB-C2vA4ojE.js} +1 -1
  126. package/dist/assets/{save-DtccrZld.js → save-CTiZvAd2.js} +1 -1
  127. package/dist/assets/{search-BArc_WOX.js → search-CztGmpnR.js} +1 -1
  128. package/dist/assets/search-config-page-D3jVALZU.js +1 -0
  129. package/dist/assets/secrets-config-page-CxWLQCjZ.js +3 -0
  130. package/dist/assets/security-config-DEyj4WBh.js +1 -0
  131. package/dist/assets/{select-C9FB0jlt.js → select-BPVI2dQ_.js} +1 -1
  132. package/dist/assets/{sequenceDiagram-DBY2YBRQ-ClKnO7v0.js → sequenceDiagram-DBY2YBRQ-B9LBmpUo.js} +1 -1
  133. package/dist/assets/settings-2-Cd7QaBsq.js +1 -0
  134. package/dist/assets/{sizeCapture-X5ZJPWSS-DSo-t5ou.js → sizeCapture-X5ZJPWSS-9UIfya9-.js} +1 -1
  135. package/dist/assets/skeleton-CUpAXGoe.js +1 -0
  136. package/dist/assets/{src-Dad2ZbCt.js → src-DKf77Yzo.js} +1 -1
  137. package/dist/assets/{stateDiagram-2N3HPSRC-BvAalBfz.js → stateDiagram-2N3HPSRC-oTW44vds.js} +1 -1
  138. package/dist/assets/stateDiagram-v2-6OUMAXLB-Bui4RXQK.js +1 -0
  139. package/dist/assets/{swimlanes-5IMT3BWC-DdVqVa1a.js → swimlanes-5IMT3BWC-Cf7lcsay.js} +2 -2
  140. package/dist/assets/swimlanesDiagram-G3AALYLV-DTwUsCBz.js +8 -0
  141. package/dist/assets/{tabs-D_mjyOOf.js → tabs-DrUmYwEQ.js} +1 -1
  142. package/dist/assets/{tag-chip-C011rd9q.js → tag-chip-CmViAZKK.js} +1 -1
  143. package/dist/assets/{timeline-definition-FHXFAJF6-D4ar8IQY.js → timeline-definition-FHXFAJF6-DoCuWKDJ.js} +1 -1
  144. package/dist/assets/{trash-2-bDhfsO1O.js → trash-2-DMRJ63AB.js} +1 -1
  145. package/dist/assets/treeView-QDETBFTQ-D5zsTiuL.js +1 -0
  146. package/dist/assets/treemap-6X3UGDF4-CR3dxqNs.js +1 -0
  147. package/dist/assets/{use-config-BNrlfEck.js → use-config-gpXy2Iyc.js} +1 -1
  148. package/dist/assets/use-confirm-dialog-Du33bHPz.js +1 -0
  149. package/dist/assets/{use-viewport-layout-P0q6Nt_N.js → use-viewport-layout-D80O1d90.js} +1 -1
  150. package/dist/assets/{vennDiagram-L72KCM5P-CDXJ_i3p.js → vennDiagram-L72KCM5P-CpU0pzGe.js} +1 -1
  151. package/dist/assets/wardley-OPB4EBWU-LatRT_Lx.js +1 -0
  152. package/dist/assets/{wardleyDiagram-EHGQE667-D-dSuT_7.js → wardleyDiagram-EHGQE667-CkN6nn6-.js} +1 -1
  153. package/dist/assets/x-CsLMuNFe.js +1 -0
  154. package/dist/assets/{xychartDiagram-FW5EYKEG-C9JCzsZF.js → xychartDiagram-FW5EYKEG-DATpapm2.js} +1 -1
  155. package/dist/index.html +31 -32
  156. package/dist/runtime-icons/opencode.svg +16 -0
  157. package/package.json +10 -9
  158. package/public/runtime-icons/opencode.svg +16 -0
  159. package/src/app/components/layout/__tests__/settings-entry-page.test.tsx +6 -4
  160. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +24 -12
  161. package/src/app/components/layout/sidebar-items.tsx +11 -2
  162. package/src/app/components/layout/sidebar.tsx +33 -234
  163. package/src/app/configs/app-navigation.config.ts +10 -24
  164. package/src/app/index.tsx +0 -5
  165. package/src/features/channels/components/config/channel-form.tsx +26 -26
  166. package/src/features/channels/components/config/weixin-channel-auth-section.tsx +6 -7
  167. package/src/features/channels/pages/__tests__/channels-list-page.test.tsx +2 -2
  168. package/src/features/channels/pages/channels-list-page.tsx +33 -32
  169. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +68 -7
  170. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +27 -0
  171. package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +0 -8
  172. package/src/features/chat/components/conversation/chat-conversation-content.tsx +65 -21
  173. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +36 -12
  174. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +0 -7
  175. package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +0 -1
  176. package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +17 -8
  177. package/src/features/chat/components/layout/chat-page-shell.tsx +2 -2
  178. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +2 -23
  179. package/src/features/chat/components/layout/chat-sidebar.tsx +3 -12
  180. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +155 -93
  181. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +126 -93
  182. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +8 -0
  183. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +27 -13
  184. package/src/features/chat/features/message/components/__tests__/chat-inline-file-preview.test.tsx +15 -9
  185. package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +127 -49
  186. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +187 -20
  187. package/src/features/chat/features/message/components/chat-inline-content-surface.tsx +55 -0
  188. package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +49 -71
  189. package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +65 -55
  190. package/src/features/chat/features/message/components/chat-message-list.container.tsx +275 -267
  191. package/src/features/chat/features/message/hooks/__tests__/use-chat-message-virtualizer.test.tsx +163 -0
  192. package/src/features/chat/features/message/hooks/use-chat-message-virtualizer.ts +68 -0
  193. package/src/features/chat/features/message/utils/__tests__/chat-message-execution-summary.utils.test.ts +188 -0
  194. package/src/features/chat/features/message/utils/__tests__/chat-message-texts.utils.test.ts +14 -1
  195. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +33 -0
  196. package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +24 -9
  197. package/src/features/chat/features/message/utils/chat-message-execution-summary.utils.ts +178 -0
  198. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +44 -0
  199. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +194 -0
  200. package/src/features/chat/features/message/utils/chat-message.utils.ts +5 -0
  201. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +151 -13
  202. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +44 -70
  203. package/src/features/chat/features/ncp/hooks/use-ncp-session-message-history.ts +197 -0
  204. package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +21 -3
  205. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +7 -7
  206. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +2 -2
  207. package/src/features/chat/features/session/components/session-context-icon.tsx +1 -1
  208. package/src/features/chat/features/session-type/components/__tests__/chat-session-type-option-item.test.tsx +5 -1
  209. package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +1 -1
  210. package/src/features/chat/features/workspace/components/__tests__/workspace-file-content-preview.test.tsx +32 -10
  211. package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +5 -15
  212. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +62 -9
  213. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +6 -1
  214. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +1 -25
  215. package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +190 -109
  216. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +9 -10
  217. package/src/features/chat/managers/chat-session-list.manager.ts +68 -27
  218. package/src/features/cron/components/__tests__/cron-config.test.tsx +260 -0
  219. package/src/features/cron/components/cron-config.tsx +392 -388
  220. package/src/features/cron/components/cron-job-detail-dialog.tsx +210 -0
  221. package/src/features/cron/components/cron-job-row.tsx +271 -0
  222. package/src/features/cron/components/cron-task-discovery.tsx +157 -0
  223. package/src/features/cron/hooks/use-cron-jobs.ts +3 -1
  224. package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +3 -2
  225. package/src/features/marketplace/components/marketplace-external-skill-source-action.tsx +8 -6
  226. package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +26 -40
  227. package/src/features/pwa/components/pwa-install-entry.tsx +45 -58
  228. package/src/features/remote/components/remote-access-page.tsx +128 -208
  229. package/src/features/settings/components/config/provider-auth-section.tsx +32 -17
  230. package/src/features/settings/components/config/provider-form.tsx +6 -6
  231. package/src/features/settings/components/config/provider-models-section.tsx +52 -46
  232. package/src/features/settings/components/config/secrets-config-form.tsx +78 -84
  233. package/src/features/settings/index.ts +0 -1
  234. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +35 -1
  235. package/src/features/settings/pages/__tests__/model-config-page.test.tsx +18 -1
  236. package/src/features/settings/pages/__tests__/search-config-page.test.tsx +8 -0
  237. package/src/features/settings/pages/__tests__/secrets-config-page.test.tsx +18 -17
  238. package/src/features/settings/pages/appearance-settings-page.tsx +87 -64
  239. package/src/features/settings/pages/model-config-page.tsx +75 -70
  240. package/src/features/settings/pages/providers-config-page.tsx +107 -73
  241. package/src/features/settings/pages/search-config-page.tsx +159 -152
  242. package/src/features/settings/pages/secrets-config-page.tsx +6 -1
  243. package/src/features/system-status/components/config/runtime-agent-list-card.tsx +23 -22
  244. package/src/features/system-status/components/config/runtime-binding-list-card.tsx +23 -22
  245. package/src/features/system-status/components/config/runtime-config-editor.tsx +17 -7
  246. package/src/features/system-status/components/config/runtime-config-overview.tsx +0 -3
  247. package/src/features/system-status/components/config/runtime-entry-list-card.tsx +24 -23
  248. package/src/features/system-status/components/config/runtime-settings-card.tsx +59 -52
  249. package/src/features/system-status/components/desktop-update-config.tsx +117 -112
  250. package/src/features/system-status/components/runtime-control-card.tsx +62 -59
  251. package/src/features/system-status/components/runtime-presence-card.tsx +79 -98
  252. package/src/features/system-status/components/runtime-security-card.tsx +74 -62
  253. package/src/features/system-status/components/security-config.tsx +3 -4
  254. package/src/features/system-status/pages/runtime-config-page.tsx +6 -1
  255. package/src/platforms/desktop/components/desktop-app-shell.tsx +1 -1
  256. package/src/platforms/mobile/components/mobile-settings-shell.tsx +2 -2
  257. package/src/shared/components/__tests__/config-split-page.test.tsx +15 -3
  258. package/src/shared/components/__tests__/settings-page.test.tsx +31 -0
  259. package/src/shared/components/actions/__tests__/navigation-link.test.tsx +47 -0
  260. package/src/shared/components/actions/navigation-link.tsx +116 -0
  261. package/src/shared/components/common/__tests__/brand-header.test.tsx +5 -5
  262. package/src/shared/components/common/__tests__/searchable-model-input.test.tsx +24 -0
  263. package/src/shared/components/common/brand-header.tsx +12 -10
  264. package/src/shared/components/common/searchable-model-input.tsx +9 -10
  265. package/src/shared/components/config-split-page.tsx +70 -65
  266. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +6 -7
  267. package/src/shared/components/settings/setting-row.tsx +61 -30
  268. package/src/shared/components/settings/settings-page.tsx +34 -0
  269. package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +23 -2
  270. package/src/shared/components/ui/actions/icon-action-button.tsx +9 -2
  271. package/src/shared/components/ui/overlays/sheet.tsx +142 -0
  272. package/src/shared/lib/api/ncp-session.types.ts +8 -2
  273. package/src/shared/lib/api/types.ts +22 -1
  274. package/src/shared/lib/api/utils/config.utils.ts +2 -1
  275. package/src/shared/lib/api/utils/ncp-session.utils.ts +25 -11
  276. package/src/shared/lib/i18n/locales/en-US/chat.json +32 -1
  277. package/src/shared/lib/i18n/locales/en-US/core.json +3 -3
  278. package/src/shared/lib/i18n/locales/en-US/cron.json +87 -17
  279. package/src/shared/lib/i18n/locales/zh-CN/chat.json +34 -3
  280. package/src/shared/lib/i18n/locales/zh-CN/core.json +6 -6
  281. package/src/shared/lib/i18n/locales/zh-CN/cron.json +80 -10
  282. package/src/shared/lib/transport/__tests__/remote.transport.test.ts +16 -4
  283. package/dist/assets/api-D0U10BcF.js +0 -13
  284. package/dist/assets/app-presenter-provider-Dffdm_Ek.js +0 -106
  285. package/dist/assets/appearance-settings-page-sfusZc2d.js +0 -1
  286. package/dist/assets/architecture-TIHT7OUA-ClwVFq-K.js +0 -1
  287. package/dist/assets/channel-BEHOSFc5.js +0 -1
  288. package/dist/assets/channels-list-page-Dkfe87Zq.js +0 -8
  289. package/dist/assets/chat-page-CeapbQnG.js +0 -1
  290. package/dist/assets/chunk-2Q5K7J3B-DrIhesSX.js +0 -1
  291. package/dist/assets/chunk-VAUOI2AC-Dpcz6BhT.js +0 -1
  292. package/dist/assets/chunk-XXDRQBXY-C2QjjwYX.js +0 -1
  293. package/dist/assets/classDiagram-OUVF2IWQ-DESMaY3_.js +0 -1
  294. package/dist/assets/classDiagram-v2-EOCWNBFH-DwOlByYH.js +0 -1
  295. package/dist/assets/config-split-page-CnQI8cKT.js +0 -1
  296. package/dist/assets/confirm-dialog-RZUWA_j9.js +0 -5
  297. package/dist/assets/cynefin-VYW2F7L2-CzjeeOq9.js +0 -1
  298. package/dist/assets/desktop-update-config-CbSYQMYz.js +0 -1
  299. package/dist/assets/doc-browser-DwXHr-mm.js +0 -1
  300. package/dist/assets/doc-browser-jVTq8iY_2.js +0 -1
  301. package/dist/assets/download-BP7ZxrwI.js +0 -1
  302. package/dist/assets/eventmodeling-45OFAUF4-C38BqG8w.js +0 -1
  303. package/dist/assets/external-link-DvHCEe3h.js +0 -1
  304. package/dist/assets/eye-C--6dxv_.js +0 -1
  305. package/dist/assets/flowDiagram-23GEKE2U-BoKk8RRq.js +0 -1
  306. package/dist/assets/folder-BfeO1zCY.js +0 -1
  307. package/dist/assets/gitGraph-TEB2WS4Q-IsQHHJuS.js +0 -1
  308. package/dist/assets/index-BlKbckX4.css +0 -1
  309. package/dist/assets/index-DoZR11Nh.js +0 -2
  310. package/dist/assets/info-DKCQHKI2-ofy2qlHI.js +0 -1
  311. package/dist/assets/loader-circle-BWdgwVnn.js +0 -1
  312. package/dist/assets/mcp-marketplace-page-XnRWiC8c.js +0 -1
  313. package/dist/assets/model-config-page-CXnqSbSI.js +0 -1
  314. package/dist/assets/packet-7NZHBO7P-Rfopfa1o.js +0 -1
  315. package/dist/assets/pie-RZYD4A2V-CJR1xKEv.js +0 -1
  316. package/dist/assets/plus-BeApS21t.js +0 -1
  317. package/dist/assets/provider-scoped-model-input-ll_Btc9h.js +0 -1
  318. package/dist/assets/providers-config-page-Bcuhqkdl.js +0 -1
  319. package/dist/assets/radar-I7S5WNFK-CDg8Z-1r.js +0 -1
  320. package/dist/assets/railroad-3IZDKUUU-BHnNmjxO.js +0 -1
  321. package/dist/assets/railroad-abnf-AHOZXSZD-DGtNcusW.js +0 -1
  322. package/dist/assets/railroad-ebnf-EBAXGLYW-BRFtsQmm.js +0 -1
  323. package/dist/assets/railroad-peg-LSFZ7HO6-Bj5IypCA.js +0 -1
  324. package/dist/assets/remote-CqKpuOP9.js +0 -1
  325. package/dist/assets/rotate-cw-DHXoiqFB.js +0 -1
  326. package/dist/assets/runtime-config-page-D-Aqxizs.js +0 -1
  327. package/dist/assets/search-config-page-XTKf5rc0.js +0 -1
  328. package/dist/assets/secrets-config-page-Cx-qptcj.js +0 -3
  329. package/dist/assets/security-config-DWGFM2u9.js +0 -1
  330. package/dist/assets/setting-row-DFvioO2a.js +0 -1
  331. package/dist/assets/settings-2-RMWqf-ey.js +0 -1
  332. package/dist/assets/skeleton-DQ5WhuDG.js +0 -1
  333. package/dist/assets/stateDiagram-v2-6OUMAXLB-CfMU38yc.js +0 -1
  334. package/dist/assets/swimlanesDiagram-G3AALYLV-D9Zb9X57.js +0 -8
  335. package/dist/assets/treeView-QDETBFTQ-Uy7M0WvH.js +0 -1
  336. package/dist/assets/treemap-6X3UGDF4-BJfGV7KX.js +0 -1
  337. package/dist/assets/use-confirm-dialog-CTNA1PC0.js +0 -1
  338. package/dist/assets/wardley-OPB4EBWU-q263iF3K.js +0 -1
  339. package/dist/assets/x-qvh05iyu.js +0 -1
  340. package/src/features/chat/components/conversation/chat-conversation-skeleton.tsx +0 -86
  341. package/src/features/settings/pages/__tests__/language-settings-page.test.tsx +0 -36
  342. package/src/features/settings/pages/language-settings-page.tsx +0 -56
  343. package/src/shared/components/actions/action-link.tsx +0 -49
  344. /package/dist/assets/{array-BqYdCIE6.js → array-CAkJT3no.js} +0 -0
  345. /package/dist/assets/{chunk-KEIR6QF5-A3jEqwzn.js → chunk-KEIR6QF5-BgdDoWEH.js} +0 -0
  346. /package/dist/assets/{chunk-Y2CYZVJY-BGrd8tLy.js → chunk-Y2CYZVJY-pTllD0P3.js} +0 -0
  347. /package/dist/assets/{config-hints-LxG80Uj5.js → config-hints-S3g-UVZb.js} +0 -0
  348. /package/dist/assets/{cytoscape.esm-CRW8q92n.js → cytoscape.esm-DmURt-5G.js} +0 -0
  349. /package/dist/assets/{defaultLocale-Cdv9x6q7.js → defaultLocale-BKy_KP3O.js} +0 -0
  350. /package/dist/assets/{dist-dw-gAfM_.js → dist-IgNNB7K9.js} +0 -0
  351. /package/dist/assets/{graphlib-BcNFQr5y.js → graphlib-xw_TBHyH.js} +0 -0
  352. /package/dist/assets/{host-capabilities-CFbAfrrA.js → host-capabilities-B-TtABsP.js} +0 -0
  353. /package/dist/assets/{init-D8TjIqfm.js → init-C9xlLIAu.js} +0 -0
  354. /package/dist/assets/{jszip.min-CP5uPNQa.js → jszip.min-Das2IAnb.js} +0 -0
  355. /package/dist/assets/{katex-wEmrEFTg.js → katex-DSy1eD2d.js} +0 -0
  356. /package/dist/assets/{middleware-CAXxl-af.js → middleware-Df-a4x-q.js} +0 -0
  357. /package/dist/assets/{path-DzBnMI1r.js → path-DB0USYHi.js} +0 -0
  358. /package/dist/assets/{provider-models-BoUhFo9R.js → provider-models-CXUgDxGR.js} +0 -0
  359. /package/dist/assets/{rough.esm-wJ1d3nQl.js → rough.esm-B1B52ZQS.js} +0 -0
  360. /package/dist/assets/{xlsx-DUqJhg5u.js → xlsx-DfjC_iml.js} +0 -0
@@ -1,345 +1,194 @@
1
- import { useMemo, useState } from "react";
2
- import type { CronJobView } from "@/shared/lib/api";
3
- import { useConfirmDialog } from "@/shared/hooks/use-confirm-dialog";
1
+ import { useDeferredValue, useRef, useState } from "react";
2
+ import {
3
+ ChevronLeft,
4
+ ChevronRight,
5
+ CircleAlert,
6
+ CircleCheck,
7
+ ListTodo,
8
+ RefreshCw,
9
+ Search,
10
+ } from "lucide-react";
11
+
12
+ import { PageLayout } from "@/app/components/layout/page-layout";
13
+ import { usePresenter } from "@/features/chat";
14
+ import { CronJobDetailDialog } from "@/features/cron/components/cron-job-detail-dialog";
15
+ import { CronJobRow } from "@/features/cron/components/cron-job-row";
16
+ import {
17
+ CronTaskComposer,
18
+ CronTemplateGallery,
19
+ } from "@/features/cron/components/cron-task-discovery";
4
20
  import {
5
21
  useCronJobs,
6
22
  useDeleteCronJob,
7
- useToggleCronJob,
8
23
  useRunCronJob,
24
+ useToggleCronJob,
9
25
  } from "@/features/cron/hooks/use-cron-jobs";
10
26
  import { Button } from "@/shared/components/ui/button";
11
27
  import { Input } from "@/shared/components/ui/input";
12
- import { Switch } from "@/shared/components/ui/switch";
13
- import {
14
- Select,
15
- SelectContent,
16
- SelectItem,
17
- SelectTrigger,
18
- SelectValue,
19
- } from "@/shared/components/ui/select";
20
- import {
21
- Popover,
22
- PopoverContent,
23
- PopoverTrigger,
24
- } from "@/shared/components/ui/popover";
25
- import {
26
- Tooltip,
27
- TooltipContent,
28
- TooltipProvider,
29
- TooltipTrigger,
30
- } from "@/shared/components/ui/tooltip";
31
- import {
32
- describeCronSchedule,
33
- describeCronSession,
34
- formatCronDate,
35
- formatRelativeTime,
36
- } from "@/shared/lib/cron";
37
- import { cn } from "@/shared/lib/utils";
28
+ import type {
29
+ CronJobView,
30
+ CronListStatus,
31
+ CronListSummaryView,
32
+ } from "@/shared/lib/api";
33
+ import { describeCronSession } from "@/shared/lib/cron";
34
+ import { useConfirmDialog } from "@/shared/hooks/use-confirm-dialog";
38
35
  import { t } from "@/shared/lib/i18n";
39
- import { PageLayout, PageHeader } from "@/app/components/layout/page-layout";
40
- import {
41
- Search,
42
- RefreshCw,
43
- Trash2,
44
- Play,
45
- MoreVertical,
46
- ChevronDown,
47
- ChevronRight,
48
- Clock,
49
- CalendarClock,
50
- ListTodo,
51
- AlertCircle,
52
- } from "lucide-react";
53
-
54
- type StatusFilter = "all" | "enabled" | "disabled";
36
+ import { cn } from "@/shared/lib/utils";
55
37
 
56
- function matchQuery(job: CronJobView, query: string): boolean {
57
- const q = query.trim().toLowerCase();
58
- if (!q) return true;
59
- const haystack = [
60
- job.id,
61
- job.name,
62
- job.payload.message,
63
- job.payload.sessionId ?? "",
64
- ]
65
- .join(" ")
66
- .toLowerCase();
67
- return haystack.includes(q);
68
- }
38
+ const CRON_PAGE_SIZE = 10;
69
39
 
70
- function filterByStatus(job: CronJobView, status: StatusFilter): boolean {
71
- if (status === "all") return true;
72
- if (status === "enabled") return job.enabled;
73
- return !job.enabled;
74
- }
40
+ function CronSummaryStrip({
41
+ summary,
42
+ }: {
43
+ readonly summary: CronListSummaryView;
44
+ }) {
45
+ const { attention, disabled, enabled, total } = summary;
46
+ const metrics = [
47
+ { label: t("cronSummaryTotal"), value: total },
48
+ { label: t("cronSummaryEnabled"), value: enabled },
49
+ { label: t("cronSummaryPaused"), value: disabled },
50
+ ];
51
+ const AttentionIcon = attention > 0 ? CircleAlert : CircleCheck;
75
52
 
76
- function StatusBadge({ job }: { job: CronJobView }) {
77
53
  return (
78
- <span
79
- className={cn(
80
- "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium",
81
- job.enabled
82
- ? "bg-emerald-50 text-emerald-700"
83
- : "bg-muted text-muted-foreground",
84
- )}
54
+ <section
55
+ aria-label={t("cronSummaryLabel")}
56
+ className="grid grid-cols-3 overflow-hidden rounded-2xl bg-border/60 sm:grid-cols-[minmax(220px,1.35fr)_repeat(3,minmax(100px,0.55fr))] sm:gap-px"
85
57
  >
86
- <span
87
- className={cn(
88
- "h-1.5 w-1.5 rounded-full",
89
- job.enabled ? "bg-emerald-500" : "bg-muted-foreground/50",
90
- )}
91
- />
92
- {job.enabled ? t("enabled") : t("disabled")}
93
- </span>
58
+ <div className="col-span-3 flex items-center gap-3 bg-muted/40 px-4 py-3.5 sm:col-span-1">
59
+ <span
60
+ className={cn(
61
+ "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl",
62
+ attention > 0
63
+ ? "bg-amber-50 text-amber-600"
64
+ : "bg-emerald-50 text-emerald-600",
65
+ )}
66
+ >
67
+ <AttentionIcon className="h-4 w-4" aria-hidden="true" />
68
+ </span>
69
+ <span className="min-w-0">
70
+ <strong className="block truncate text-xs font-semibold text-foreground">
71
+ {attention > 0
72
+ ? `${attention} ${t("cronAttentionSummary")}`
73
+ : t("cronAllHealthy")}
74
+ </strong>
75
+ <span className="mt-1 block truncate text-[11px] text-muted-foreground">
76
+ {attention > 0
77
+ ? t("cronAttentionSummaryHint")
78
+ : t("cronAllHealthyHint")}
79
+ </span>
80
+ </span>
81
+ </div>
82
+ {metrics.map((metric) => (
83
+ <div key={metric.label} className="bg-muted/40 px-4 py-3.5 sm:px-5">
84
+ <div className="text-lg font-semibold tabular-nums text-foreground">
85
+ {metric.value}
86
+ </div>
87
+ <div className="mt-1 text-[11px] text-muted-foreground">
88
+ {metric.label}
89
+ </div>
90
+ </div>
91
+ ))}
92
+ </section>
94
93
  );
95
94
  }
96
95
 
97
- function CronJobCard(props: {
98
- job: CronJobView;
99
- onDelete: (job: CronJobView) => void;
100
- onRun: (job: CronJobView) => void;
101
- onToggle: (job: CronJobView, nextEnabled: boolean) => void;
96
+ function CronPagination({
97
+ page,
98
+ total,
99
+ disabled,
100
+ onPageChange,
101
+ }: {
102
+ readonly page: number;
103
+ readonly total: number;
104
+ readonly disabled: boolean;
105
+ readonly onPageChange: (page: number) => void;
102
106
  }) {
103
- const { job, onDelete, onRun, onToggle } = props;
104
- const [expanded, setExpanded] = useState(false);
105
- const [menuOpen, setMenuOpen] = useState(false);
106
-
107
- const handleCardClick = (e: React.MouseEvent) => {
108
- const target = e.target as HTMLElement;
109
- if (target.closest("[data-no-expand]")) return;
110
- setExpanded(!expanded);
111
- };
107
+ const totalPages = Math.ceil(total / CRON_PAGE_SIZE);
108
+ if (totalPages <= 1) {
109
+ return null;
110
+ }
111
+ const rangeStart = page * CRON_PAGE_SIZE + 1;
112
+ const rangeEnd = Math.min(total, rangeStart + CRON_PAGE_SIZE - 1);
112
113
 
113
114
  return (
114
- <div
115
- className={cn(
116
- "group overflow-hidden rounded-xl border border-border/70 bg-card transition-colors cursor-pointer",
117
- expanded ? "bg-muted/20" : "hover:bg-muted/30",
118
- )}
119
- onClick={handleCardClick}
115
+ <nav
116
+ aria-label={t("cronPaginationLabel")}
117
+ className="flex items-center justify-between gap-3 border-b border-border/60 py-3 text-xs text-muted-foreground"
120
118
  >
121
- <div className="px-3 py-2.5">
122
- <div className="flex items-center gap-2.5">
123
- <div className="min-w-0 flex-1">
124
- <div className="flex items-center gap-2">
125
- {expanded ? (
126
- <ChevronDown className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
127
- ) : (
128
- <ChevronRight className="h-3.5 w-3.5 text-muted-foreground/50 shrink-0 group-hover:text-muted-foreground transition-colors" />
129
- )}
130
- <span className="text-sm font-medium text-foreground truncate">
131
- {job.name || job.id}
132
- </span>
133
- <StatusBadge job={job} />
134
- {job.deleteAfterRun ? (
135
- <span className="rounded-full bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground">
136
- {t("cronOneShot")}
137
- </span>
138
- ) : null}
139
- </div>
140
- </div>
141
-
142
- <div className="flex items-center gap-1 shrink-0" data-no-expand>
143
- <Switch
144
- checked={job.enabled}
145
- onCheckedChange={(checked) => onToggle(job, checked)}
146
- />
147
- <Popover open={menuOpen} onOpenChange={setMenuOpen}>
148
- <PopoverTrigger asChild>
149
- <Button
150
- variant="ghost"
151
- size="icon"
152
- className="h-7 w-7 text-muted-foreground hover:text-foreground hover:bg-muted"
153
- aria-label={t("cronMoreActions")}
154
- >
155
- <MoreVertical className="h-4 w-4" />
156
- </Button>
157
- </PopoverTrigger>
158
- <PopoverContent align="end" sideOffset={4} className="w-40 p-1">
159
- <button
160
- type="button"
161
- onClick={() => {
162
- setMenuOpen(false);
163
- onRun(job);
164
- }}
165
- className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-foreground transition-colors hover:bg-muted"
166
- >
167
- <Play className="h-3.5 w-3.5 text-muted-foreground" />
168
- {t("cronRunNow")}
169
- </button>
170
- <button
171
- type="button"
172
- onClick={() => {
173
- setMenuOpen(false);
174
- onDelete(job);
175
- }}
176
- className="flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm text-red-600 transition-colors hover:bg-red-50"
177
- >
178
- <Trash2 className="h-3.5 w-3.5" />
179
- {t("delete")}
180
- </button>
181
- </PopoverContent>
182
- </Popover>
183
- </div>
184
- </div>
185
-
186
- <div className="mt-1.5 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground pl-6">
187
- <span className="flex items-center gap-1">
188
- <CalendarClock className="h-3 w-3 text-muted-foreground/70" />
189
- <span className="font-mono text-foreground/80">
190
- {describeCronSchedule(job)}
191
- </span>
192
- </span>
193
- {job.state.nextRunAt ? (
194
- <span className="flex items-center gap-1">
195
- <Clock className="h-3 w-3 text-muted-foreground/70" />
196
- <span>{formatRelativeTime(job.state.nextRunAt)}</span>
197
- </span>
198
- ) : null}
199
- {job.state.lastRunAt ? (
200
- <span className="flex items-center gap-1" data-no-expand>
201
- <span className="text-border">•</span>
202
- {job.state.lastStatus === "error" && job.state.lastError ? (
203
- <TooltipProvider delayDuration={200}>
204
- <Tooltip>
205
- <TooltipTrigger asChild>
206
- <span className="inline-flex items-center gap-0.5 text-orange-500 cursor-help">
207
- <AlertCircle className="h-3 w-3" />
208
- </span>
209
- </TooltipTrigger>
210
- <TooltipContent
211
- side="top"
212
- align="start"
213
- className="max-w-xs text-xs bg-red-50 text-red-700 border-red-200"
214
- >
215
- {job.state.lastError}
216
- </TooltipContent>
217
- </Tooltip>
218
- </TooltipProvider>
219
- ) : (
220
- <span
221
- className={cn(
222
- job.state.lastStatus === "ok"
223
- ? "text-emerald-500"
224
- : "text-muted-foreground/70",
225
- )}
226
- >
227
- {job.state.lastStatus === "ok" ? "✓" : ""}
228
- </span>
229
- )}
230
- <span>{formatRelativeTime(job.state.lastRunAt)}</span>
231
- </span>
232
- ) : null}
233
- </div>
234
-
235
- <div className="mt-2 pl-6">
236
- <div
237
- className={cn(
238
- "text-sm text-muted-foreground break-words",
239
- !expanded && "line-clamp-2",
240
- )}
241
- >
242
- {job.payload.message}
243
- </div>
244
- </div>
245
-
246
- {expanded ? (
247
- <div className="mt-3 ml-6 space-y-3 border-t border-border/60 pt-3">
248
- <div className="grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-2 text-xs">
249
- <div>
250
- <div className="text-[11px] text-muted-foreground/70 mb-0.5">
251
- {t("cronSessionLabel")}
252
- </div>
253
- <div
254
- className="text-foreground/80 font-mono text-[11px] truncate"
255
- title={describeCronSession(job)}
256
- >
257
- {describeCronSession(job)}
258
- </div>
259
- </div>
260
- <div>
261
- <div className="text-[11px] text-muted-foreground/70 mb-0.5">
262
- {t("cronNextRun")}
263
- </div>
264
- <div className="text-foreground/80">
265
- {formatCronDate(job.state.nextRunAt)}
266
- </div>
267
- </div>
268
- <div>
269
- <div className="text-[11px] text-muted-foreground/70 mb-0.5">
270
- {t("cronLastRun")}
271
- </div>
272
- <div className="text-foreground/80">
273
- {formatCronDate(job.state.lastRunAt)}
274
- </div>
275
- </div>
276
- <div>
277
- <div className="text-[11px] text-muted-foreground/70 mb-0.5">
278
- {t("cronLastStatus")}
279
- </div>
280
- <div
281
- className={cn(
282
- "inline-flex items-center gap-1 font-medium",
283
- job.state.lastStatus === "ok"
284
- ? "text-emerald-600"
285
- : job.state.lastStatus === "error"
286
- ? "text-red-600"
287
- : job.state.lastStatus === "skipped"
288
- ? "text-amber-600"
289
- : "text-muted-foreground",
290
- )}
291
- >
292
- {job.state.lastStatus ?? "-"}
293
- </div>
294
- </div>
295
- <div>
296
- <div className="text-[11px] text-muted-foreground/70 mb-0.5">
297
- {t("cronCreatedAt")}
298
- </div>
299
- <div className="text-foreground/80">
300
- {formatCronDate(job.createdAt)}
301
- </div>
302
- </div>
303
- <div>
304
- <div className="text-[11px] text-muted-foreground/70 mb-0.5">
305
- {t("cronId")}
306
- </div>
307
- <div
308
- className="text-foreground/80 font-mono text-[11px] truncate"
309
- title={job.id}
310
- >
311
- {job.id}
312
- </div>
313
- </div>
314
- </div>
315
-
316
- {job.state.lastError ? (
317
- <div className="rounded-lg bg-red-50/80 border border-red-100 px-3 py-2 text-xs text-red-600 break-words">
318
- {job.state.lastError}
319
- </div>
320
- ) : null}
321
- </div>
322
- ) : null}
119
+ <span className="tabular-nums">
120
+ {rangeStart}–{rangeEnd} / {total}
121
+ </span>
122
+ <div className="flex items-center gap-1.5">
123
+ <Button
124
+ type="button"
125
+ variant="ghost"
126
+ size="icon"
127
+ className="h-8 w-8"
128
+ disabled={disabled || page === 0}
129
+ onClick={() => onPageChange(page - 1)}
130
+ aria-label={t("cronPreviousPage")}
131
+ >
132
+ <ChevronLeft className="h-4 w-4" />
133
+ </Button>
134
+ <span className="min-w-14 text-center tabular-nums text-foreground">
135
+ {page + 1} / {totalPages}
136
+ </span>
137
+ <Button
138
+ type="button"
139
+ variant="ghost"
140
+ size="icon"
141
+ className="h-8 w-8"
142
+ disabled={disabled || page >= totalPages - 1}
143
+ onClick={() => onPageChange(page + 1)}
144
+ aria-label={t("cronNextPage")}
145
+ >
146
+ <ChevronRight className="h-4 w-4" />
147
+ </Button>
323
148
  </div>
324
- </div>
149
+ </nav>
325
150
  );
326
151
  }
327
152
 
328
153
  export function CronConfig() {
154
+ const [composerPrompt, setComposerPrompt] = useState("");
329
155
  const [query, setQuery] = useState("");
330
- const [status, setStatus] = useState<StatusFilter>("all");
331
- const cronQuery = useCronJobs({ all: true });
156
+ const [status, setStatus] = useState<CronListStatus>("all");
157
+ const [page, setPage] = useState(0);
158
+ const [expandedJobId, setExpandedJobId] = useState<string | null>(null);
159
+ const [selectedJobId, setSelectedJobId] = useState<string | null>(null);
160
+ const deferredQuery = useDeferredValue(query.trim());
161
+ const composerRef = useRef<HTMLInputElement>(null);
162
+ const presenter = usePresenter();
163
+ const cronQuery = useCronJobs({
164
+ all: true,
165
+ limit: CRON_PAGE_SIZE,
166
+ offset: page * CRON_PAGE_SIZE,
167
+ query: deferredQuery,
168
+ status,
169
+ });
332
170
  const deleteCronJob = useDeleteCronJob();
333
171
  const toggleCronJob = useToggleCronJob();
334
172
  const runCronJob = useRunCronJob();
335
173
  const { confirm, ConfirmDialog } = useConfirmDialog();
174
+ const jobs = cronQuery.data?.jobs ?? [];
175
+ const total = cronQuery.data?.total ?? 0;
176
+ const summary = cronQuery.data?.summary;
177
+ const selectedJob = jobs.find((job) => job.id === selectedJobId) ?? null;
336
178
 
337
- const jobs = useMemo(() => {
338
- const data = cronQuery.data?.jobs ?? [];
339
- return data
340
- .filter((job) => matchQuery(job, query))
341
- .filter((job) => filterByStatus(job, status));
342
- }, [cronQuery.data, query, status]);
179
+ const startDraftConversation = () => {
180
+ const prompt = composerPrompt.trim();
181
+ if (!prompt) {
182
+ composerRef.current?.focus();
183
+ return;
184
+ }
185
+ presenter.chatSessionListManager.createSession({ prompt });
186
+ };
187
+
188
+ const handleTemplateSelect = (prompt: string) => {
189
+ setComposerPrompt(prompt);
190
+ composerRef.current?.focus();
191
+ };
343
192
 
344
193
  const handleDelete = async (job: CronJobView) => {
345
194
  const confirmed = await confirm({
@@ -348,12 +197,24 @@ export function CronConfig() {
348
197
  variant: "destructive",
349
198
  confirmLabel: t("delete"),
350
199
  });
351
- if (!confirmed) return;
200
+ if (!confirmed) {
201
+ return;
202
+ }
203
+ setExpandedJobId(null);
204
+ setSelectedJobId(null);
205
+ if (jobs.length === 1 && page > 0) {
206
+ setPage(page - 1);
207
+ }
352
208
  deleteCronJob.mutate({ id: job.id });
353
209
  };
354
210
 
355
- const handleToggle = (job: CronJobView, nextEnabled: boolean) => {
356
- toggleCronJob.mutate({ id: job.id, enabled: nextEnabled });
211
+ const handleToggle = (job: CronJobView, enabled: boolean) => {
212
+ const leavesCurrentFilter =
213
+ (status === "enabled" && !enabled) || (status === "disabled" && enabled);
214
+ if (leavesCurrentFilter && jobs.length === 1 && page > 0) {
215
+ setPage(page - 1);
216
+ }
217
+ toggleCronJob.mutate({ id: job.id, enabled });
357
218
  };
358
219
 
359
220
  const handleRun = async (job: CronJobView) => {
@@ -364,98 +225,241 @@ export function CronConfig() {
364
225
  description: job.name ? `${job.name} (${job.id})` : job.id,
365
226
  confirmLabel: t("cronRunNow"),
366
227
  });
367
- if (!confirmed) return;
228
+ if (!confirmed) {
229
+ return;
230
+ }
368
231
  }
369
232
  runCronJob.mutate({ id: job.id, force });
370
233
  };
371
234
 
235
+ const handleOpenSession = (job: CronJobView) => {
236
+ presenter.chatSessionListManager.selectSession(describeCronSession(job));
237
+ };
238
+
239
+ const handlePageChange = (nextPage: number) => {
240
+ setExpandedJobId(null);
241
+ setPage(nextPage);
242
+ };
243
+
244
+ const handleToggleExpanded = (job: CronJobView) => {
245
+ setExpandedJobId((currentId) => (currentId === job.id ? null : job.id));
246
+ };
247
+
248
+ const hasTasks = (summary?.total ?? 0) > 0;
249
+
372
250
  return (
373
- <PageLayout fullHeight>
374
- <PageHeader
375
- title={t("cronPageTitle")}
376
- description={t("cronPageDescription")}
377
- actions={
251
+ <PageLayout className="space-y-7">
252
+ <header className="flex items-start justify-between gap-6 pt-2">
253
+ <div>
254
+ <div className="mb-2 text-[11px] font-semibold uppercase tracking-[0.12em] text-primary">
255
+ {t("cronWorkspaceEyebrow")}
256
+ </div>
257
+ <h1 className="text-3xl font-semibold tracking-[-0.035em] text-foreground sm:text-4xl">
258
+ {t("cronPageTitle")}
259
+ </h1>
260
+ <p className="mt-2 text-sm text-muted-foreground">
261
+ {t("cronPageDescription")}
262
+ </p>
263
+ </div>
264
+ <div className="pt-5">
378
265
  <Button
379
266
  variant="ghost"
380
267
  size="icon"
381
- className="h-8 w-8 rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted"
382
- onClick={() => cronQuery.refetch()}
268
+ className="h-8 w-8"
269
+ onClick={() => {
270
+ if (page === 0) {
271
+ setExpandedJobId(null);
272
+ void cronQuery.refetch();
273
+ } else {
274
+ handlePageChange(0);
275
+ }
276
+ }}
277
+ aria-label={t("refresh")}
383
278
  >
384
279
  <RefreshCw
385
280
  className={cn("h-4 w-4", cronQuery.isFetching && "animate-spin")}
386
281
  />
387
282
  </Button>
388
- }
389
- />
283
+ </div>
284
+ </header>
390
285
 
391
- <div className="mb-4">
392
- <div className="flex flex-wrap gap-2.5 items-center">
393
- <div className="relative flex-1 min-w-[240px]">
394
- <Search className="pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/70" />
395
- <Input
396
- value={query}
397
- onChange={(e) => setQuery(e.target.value)}
398
- placeholder={t("cronSearchPlaceholder")}
399
- className="pl-9"
400
- />
401
- </div>
402
- <div className="min-w-[140px]">
403
- <Select
404
- value={status}
405
- onValueChange={(value) => setStatus(value as StatusFilter)}
406
- >
407
- <SelectTrigger className="w-full">
408
- <SelectValue placeholder={t("cronStatusLabel")} />
409
- </SelectTrigger>
410
- <SelectContent>
411
- <SelectItem value="all">{t("cronStatusAll")}</SelectItem>
412
- <SelectItem value="enabled">
413
- {t("cronStatusEnabled")}
414
- </SelectItem>
415
- <SelectItem value="disabled">
416
- {t("cronStatusDisabled")}
417
- </SelectItem>
418
- </SelectContent>
419
- </Select>
420
- </div>
421
- <div className="text-xs text-muted-foreground ml-auto tabular-nums">
422
- {jobs.length} / {cronQuery.data?.total ?? 0}
423
- </div>
286
+ {hasTasks ? (
287
+ <CronTaskComposer
288
+ ref={composerRef}
289
+ value={composerPrompt}
290
+ onChange={setComposerPrompt}
291
+ onSubmit={startDraftConversation}
292
+ />
293
+ ) : null}
294
+
295
+ {cronQuery.isLoading ? (
296
+ <div className="flex flex-col items-center justify-center py-20 text-muted-foreground">
297
+ <RefreshCw className="mb-3 h-5 w-5 animate-spin" />
298
+ <span className="text-sm">{t("cronLoading")}</span>
424
299
  </div>
425
- </div>
300
+ ) : hasTasks ? (
301
+ <>
302
+ {summary ? <CronSummaryStrip summary={summary} /> : null}
426
303
 
427
- <div className="flex-1 overflow-auto custom-scrollbar">
428
- {cronQuery.isLoading ? (
429
- <div className="flex flex-col items-center justify-center py-16 text-muted-foreground">
430
- <RefreshCw className="h-5 w-5 animate-spin mb-3" />
431
- <span className="text-sm">{t("cronLoading")}</span>
432
- </div>
433
- ) : jobs.length === 0 ? (
434
- <div className="flex flex-col items-center justify-center py-16 text-muted-foreground">
435
- <div className="mb-3 flex h-10 w-10 items-center justify-center rounded-xl bg-muted">
436
- <ListTodo className="h-5 w-5 text-muted-foreground/70" />
304
+ <section className="pt-1">
305
+ <div className="mb-3 flex items-end justify-between gap-4">
306
+ <div>
307
+ <h2 className="text-base font-semibold text-foreground">
308
+ {t("cronMyTasks")}
309
+ </h2>
310
+ <p className="mt-0.5 text-xs text-muted-foreground">
311
+ {total} {t("cronTaskCountUnit")}
312
+ </p>
313
+ </div>
437
314
  </div>
438
- <span className="text-sm font-medium text-foreground">
439
- {t("cronEmpty")}
440
- </span>
441
- <span className="text-xs text-muted-foreground mt-1">
442
- {t("cronEmptyGuide")}
315
+ <div className="mb-2 flex flex-wrap items-center justify-between gap-2.5">
316
+ <div className="relative min-w-[220px] flex-1">
317
+ <Search className="pointer-events-none absolute left-3 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/70" />
318
+ <Input
319
+ value={query}
320
+ onChange={(event) => {
321
+ setQuery(event.target.value);
322
+ handlePageChange(0);
323
+ }}
324
+ placeholder={t("cronSearchPlaceholder")}
325
+ className="pl-9"
326
+ />
327
+ </div>
328
+ <div
329
+ className="flex flex-wrap items-center gap-1"
330
+ aria-label={t("cronStatusLabel")}
331
+ >
332
+ {(
333
+ [
334
+ ["all", "cronStatusAll"],
335
+ ["enabled", "cronStatusEnabled"],
336
+ ["disabled", "cronStatusDisabled"],
337
+ ["attention", "cronStatusAttention"],
338
+ ] as const
339
+ ).map(([value, label]) => (
340
+ <button
341
+ key={value}
342
+ type="button"
343
+ aria-pressed={status === value}
344
+ onClick={() => {
345
+ setStatus(value);
346
+ handlePageChange(0);
347
+ }}
348
+ className={cn(
349
+ "rounded-lg px-2.5 py-2 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
350
+ status === value && "bg-muted text-foreground",
351
+ )}
352
+ >
353
+ {t(label)}
354
+ </button>
355
+ ))}
356
+ </div>
357
+ </div>
358
+
359
+ {jobs.length > 0 ? (
360
+ <>
361
+ <div
362
+ aria-busy={cronQuery.isFetching}
363
+ className={cn(
364
+ "border-y border-border/60 transition-opacity",
365
+ cronQuery.isFetching && "opacity-60",
366
+ )}
367
+ >
368
+ {jobs.map((job) => (
369
+ <CronJobRow
370
+ key={job.id}
371
+ expanded={expandedJobId === job.id}
372
+ job={job}
373
+ onDelete={handleDelete}
374
+ onEdit={(item) => setSelectedJobId(item.id)}
375
+ onRun={handleRun}
376
+ onToggleExpanded={handleToggleExpanded}
377
+ onToggle={handleToggle}
378
+ />
379
+ ))}
380
+ </div>
381
+ <CronPagination
382
+ page={page}
383
+ total={total}
384
+ disabled={cronQuery.isFetching}
385
+ onPageChange={handlePageChange}
386
+ />
387
+ </>
388
+ ) : (
389
+ <div className="flex flex-col items-center justify-center py-12 text-center text-muted-foreground">
390
+ <Search className="mb-3 h-5 w-5" />
391
+ <span className="text-sm font-medium text-foreground">
392
+ {t("cronNoMatchingTasks")}
393
+ </span>
394
+ <span className="mt-1 text-xs">
395
+ {t("cronNoMatchingTasksHint")}
396
+ </span>
397
+ </div>
398
+ )}
399
+ </section>
400
+
401
+ <section className="pt-2">
402
+ <div className="mb-3">
403
+ <h2 className="text-base font-semibold text-foreground">
404
+ {t("cronNeedInspiration")}
405
+ </h2>
406
+ <p className="mt-0.5 text-xs text-muted-foreground">
407
+ {t("cronTemplateSelectionHint")}
408
+ </p>
409
+ </div>
410
+ <CronTemplateGallery compact onSelect={handleTemplateSelect} />
411
+ </section>
412
+ </>
413
+ ) : (
414
+ <>
415
+ <section className="flex min-h-[270px] flex-col items-center justify-center rounded-3xl bg-muted/25 px-4 py-10 text-center sm:px-8">
416
+ <span className="mb-4 flex h-11 w-11 items-center justify-center rounded-2xl bg-card shadow-sm">
417
+ <ListTodo className="h-5 w-5 text-primary" />
443
418
  </span>
444
- </div>
445
- ) : (
446
- <div className="space-y-2">
447
- {jobs.map((job) => (
448
- <CronJobCard
449
- key={job.id}
450
- job={job}
451
- onDelete={handleDelete}
452
- onRun={handleRun}
453
- onToggle={handleToggle}
454
- />
455
- ))}
456
- </div>
457
- )}
458
- </div>
419
+ <h2 className="text-xl font-semibold text-foreground">
420
+ {t("cronEmptyHeroTitle")}
421
+ </h2>
422
+ <p className="mb-6 mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">
423
+ {t("cronEmptyHeroDescription")}
424
+ </p>
425
+ <CronTaskComposer
426
+ ref={composerRef}
427
+ prominent
428
+ value={composerPrompt}
429
+ onChange={setComposerPrompt}
430
+ onSubmit={startDraftConversation}
431
+ />
432
+ <p className="mt-3 text-xs text-muted-foreground">
433
+ {t("cronComposerHint")}
434
+ </p>
435
+ </section>
436
+ <section>
437
+ <div className="mb-3">
438
+ <h2 className="text-base font-semibold text-foreground">
439
+ {t("cronStartFromResult")}
440
+ </h2>
441
+ <p className="mt-0.5 text-xs text-muted-foreground">
442
+ {t("cronTemplateSelectionHint")}
443
+ </p>
444
+ </div>
445
+ <CronTemplateGallery onSelect={handleTemplateSelect} />
446
+ </section>
447
+ </>
448
+ )}
449
+
450
+ <CronJobDetailDialog
451
+ job={selectedJob}
452
+ open={Boolean(selectedJob)}
453
+ onOpenChange={(open) => {
454
+ if (!open) {
455
+ setSelectedJobId(null);
456
+ }
457
+ }}
458
+ onDelete={handleDelete}
459
+ onOpenSession={handleOpenSession}
460
+ onRun={handleRun}
461
+ onToggle={handleToggle}
462
+ />
459
463
  <ConfirmDialog />
460
464
  </PageLayout>
461
465
  );