@nextclaw/ui 0.12.9 → 0.12.10

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 (178) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/assets/ChannelsList-M9FTK1Ak.js +8 -0
  3. package/dist/assets/DocBrowser-CH7-GxlL.js +1 -0
  4. package/dist/assets/{DocBrowser-6ReNjvzF.js → DocBrowser-DMfr0Oow.js} +1 -1
  5. package/dist/assets/{DocBrowserContext-B6SpA7Qs.js → DocBrowserContext-BXydqby-.js} +1 -1
  6. package/dist/assets/{LogoBadge-ByNLYg65.js → LogoBadge-hO7tY7hE.js} +1 -1
  7. package/dist/assets/ModelConfig-CNIgLf0e.js +1 -0
  8. package/dist/assets/{ProviderScopedModelInput-Da7khnBA.js → ProviderScopedModelInput-B3HWP4oz.js} +1 -1
  9. package/dist/assets/ProvidersList-CHjMnRhX.js +1 -0
  10. package/dist/assets/RuntimeConfig-psp8nMSG.js +1 -0
  11. package/dist/assets/SearchConfig-CSoKip1f.js +1 -0
  12. package/dist/assets/{SecretsConfig-D281Rotl.js → SecretsConfig-MEt6MjuD.js} +2 -2
  13. package/dist/assets/SessionsConfig-DifCiXwR.js +2 -0
  14. package/dist/assets/{app-query-client-VnFElj4E.js → app-query-client-9jNewezV.js} +1 -1
  15. package/dist/assets/{book-open-BdcxxoQu.js → book-open-DzdUViDm.js} +1 -1
  16. package/dist/assets/chat-page-CLp0UV0Y.js +58 -0
  17. package/dist/assets/chat-session-display-DsYHx0RZ.js +1 -0
  18. package/dist/assets/{chunk-JZWAC4HX-DK5HPmIK.js → chunk-JZWAC4HX-C5dEc8hV.js} +1 -1
  19. package/dist/assets/{client-_i4MU2bB.js → client-C-8fH7-c.js} +1 -1
  20. package/dist/assets/{config-DtIQwrHF.js → config-CBScxsdV.js} +1 -1
  21. package/dist/assets/config-split-page-BUout_Ak.js +1 -0
  22. package/dist/assets/{createLucideIcon-BSeTgkZW.js → createLucideIcon-dy5ie7Ox.js} +1 -1
  23. package/dist/assets/desktop-update-config-2BS6BMkW.js +1 -0
  24. package/dist/assets/{dist-ccBFUi-o.js → dist-BruyLa92.js} +1 -1
  25. package/dist/assets/{dist-6TrrnPCR.js → dist-Cy7_j6hA.js} +1 -1
  26. package/dist/assets/{download-BhDxnyvU.js → download-BD0ETkB-.js} +1 -1
  27. package/dist/assets/{external-link-BgErLCNT.js → external-link-kZSAO8nT.js} +1 -1
  28. package/dist/assets/{hash-Bl7dr_UG.js → hash-BHJC2Ovu.js} +1 -1
  29. package/dist/assets/{i18n-eDHeDY0n.js → i18n-CpTZLchQ.js} +1 -1
  30. package/dist/assets/index-mW8W2FUu.css +1 -0
  31. package/dist/assets/index-zDZfXoI4.js +6 -0
  32. package/dist/assets/{infiniteQueryBehavior-ZDS92Qpp.js → infiniteQueryBehavior-CyER9hv0.js} +1 -1
  33. package/dist/assets/loader-circle-Bc2gCU33.js +1 -0
  34. package/dist/assets/{logos-x89HbrZ4.js → logos-B7gRObP8.js} +1 -1
  35. package/dist/assets/marketplace-page-3qVMnF3d.js +1 -0
  36. package/dist/assets/marketplace-page-BhFIeQzI.js +49 -0
  37. package/dist/assets/mcp-marketplace-page-DYfteJ1D.js +40 -0
  38. package/dist/assets/{page-layout-vZnghcFy.js → page-layout-0UcO9H9Z.js} +1 -1
  39. package/dist/assets/play-CKDjSQFL.js +1 -0
  40. package/dist/assets/plus-CG0QrVY_.js +1 -0
  41. package/dist/assets/{refresh-ccw-DT98i__E.js → refresh-ccw-COVhNHtN.js} +1 -1
  42. package/dist/assets/{refresh-cw-C47QSEwg.js → refresh-cw-Bcv40SXy.js} +1 -1
  43. package/dist/assets/remote-access-page-CWHG-sug.js +1 -0
  44. package/dist/assets/{rotate-cw-JtFzpNn6.js → rotate-cw-oHMKJMC8.js} +1 -1
  45. package/dist/assets/{save-3S6-H3Xw.js → save-EqJPOF0G.js} +1 -1
  46. package/dist/assets/search-BCAlB8nz.js +1 -0
  47. package/dist/assets/security-config-Slh0Mayz.js +1 -0
  48. package/dist/assets/select-CVz0t7MF.js +41 -0
  49. package/dist/assets/setting-row-CbVHAuQt.js +1 -0
  50. package/dist/assets/skeleton-D5rdKvzy.js +1 -0
  51. package/dist/assets/{status-dot-vbanNPFU.js → status-dot-DpPtVzQT.js} +1 -1
  52. package/dist/assets/{switch-BsLtHOH-.js → switch-CM29eCAR.js} +1 -1
  53. package/dist/assets/{tabs-custom-D3HYMt6k.js → tabs-custom-YcZUWn3o.js} +1 -1
  54. package/dist/assets/tag-chip-DMXdnLcj.js +1 -0
  55. package/dist/assets/{trash-2-G48scll7.js → trash-2-mJT6oWa2.js} +1 -1
  56. package/dist/assets/{use-infinite-scroll-loader-DkNhD-42.js → use-infinite-scroll-loader-DJ1L81Dz.js} +1 -1
  57. package/dist/assets/{useConfirmDialog-BkvTN-vd.js → useConfirmDialog-BsVuqu1x.js} +1 -1
  58. package/dist/assets/{useMutation-CBWjE2uj.js → useMutation-CNcz2fgt.js} +1 -1
  59. package/dist/assets/x-Czwxm82I.js +1 -0
  60. package/dist/index.html +22 -22
  61. package/dist/runtime-icons/claude.ico +0 -0
  62. package/dist/runtime-icons/codex-openai.svg +6 -0
  63. package/dist/runtime-icons/hermes-agent.png +0 -0
  64. package/package.json +6 -6
  65. package/public/runtime-icons/claude.ico +0 -0
  66. package/public/runtime-icons/codex-openai.svg +6 -0
  67. package/public/runtime-icons/hermes-agent.png +0 -0
  68. package/src/account/components/account-panel.tsx +217 -97
  69. package/src/account/managers/account.manager.ts +3 -2
  70. package/src/api/chat-session-type.types.ts +7 -0
  71. package/src/api/runtime-control.types.ts +8 -0
  72. package/src/api/types.ts +8 -0
  73. package/src/app.tsx +221 -57
  74. package/src/components/agents/agent-dialogs.tsx +499 -0
  75. package/src/components/agents/agents-page.test.tsx +238 -0
  76. package/src/components/agents/agents-page.tsx +435 -0
  77. package/src/components/chat/ChatSidebar.tsx +11 -35
  78. package/src/components/chat/chat-conversation-panel.test.tsx +20 -0
  79. package/src/components/chat/chat-conversation-panel.tsx +83 -13
  80. package/src/components/chat/chat-page-shell.tsx +19 -13
  81. package/src/components/chat/chat-session-type-option-item.test.tsx +46 -0
  82. package/src/components/chat/chat-session-type-option-item.tsx +68 -0
  83. package/src/components/chat/chat-session-workspace-file-preview.test.tsx +87 -0
  84. package/src/components/chat/chat-session-workspace-file-preview.tsx +14 -43
  85. package/src/components/chat/chat-session-workspace-panel-nav.tsx +8 -2
  86. package/src/components/chat/chat-sidebar-project-groups.tsx +11 -36
  87. package/src/components/chat/ncp/__tests__/ncp-session-adapter.cancelled-tool.test.ts +77 -0
  88. package/src/components/chat/ncp/ncp-chat-page.tsx +2 -0
  89. package/src/components/chat/ncp/ncp-session-adapter.test.ts +1 -0
  90. package/src/components/chat/ncp/ncp-session-adapter.ts +3 -0
  91. package/src/components/chat/ncp/page/ncp-chat-derived-state.ts +10 -4
  92. package/src/components/chat/stores/chat-input.store.ts +2 -1
  93. package/src/components/chat/stores/chat-thread.store.ts +3 -1
  94. package/src/components/chat/useChatSessionTypeState.ts +10 -1
  95. package/src/components/chat/workspace/chat-session-workspace-file-breadcrumbs.tsx +86 -0
  96. package/src/components/common/BrandHeader.tsx +3 -1
  97. package/src/components/common/session-context-icon.tsx +15 -2
  98. package/src/components/common/{TagInput.tsx → tag-input.tsx} +25 -17
  99. package/src/components/config/ChannelForm.test.tsx +89 -3
  100. package/src/components/config/ChannelForm.tsx +157 -188
  101. package/src/components/config/ChannelsList.test.tsx +163 -119
  102. package/src/components/config/ChannelsList.tsx +90 -101
  103. package/src/components/config/ProviderForm.tsx +108 -146
  104. package/src/components/config/ProvidersList.tsx +100 -123
  105. package/src/components/config/SearchConfig.tsx +423 -393
  106. package/src/components/config/channel-form-fields-section.tsx +70 -37
  107. package/src/components/config/config-split-page.tsx +109 -0
  108. package/src/components/config/provider-enabled-field.tsx +17 -10
  109. package/src/components/config/runtime-control-card.test.tsx +56 -0
  110. package/src/components/config/runtime-control-card.tsx +25 -0
  111. package/src/components/config/runtime-presence-card.tsx +93 -79
  112. package/src/components/layout/AppLayout.tsx +25 -37
  113. package/src/components/layout/app-layout.test.tsx +46 -14
  114. package/src/components/layout/runtime-status-entry.test.tsx +157 -0
  115. package/src/components/layout/runtime-status-entry.tsx +143 -0
  116. package/src/components/marketplace/marketplace-detail-doc.ts +93 -0
  117. package/src/components/marketplace/marketplace-list-card.tsx +288 -0
  118. package/src/components/marketplace/marketplace-page-data.ts +129 -0
  119. package/src/components/marketplace/marketplace-page.test.tsx +339 -0
  120. package/src/components/marketplace/marketplace-page.tsx +596 -0
  121. package/src/components/marketplace/mcp/mcp-marketplace-card.tsx +128 -0
  122. package/src/components/marketplace/mcp/mcp-marketplace-dialogs.tsx +191 -0
  123. package/src/components/marketplace/mcp/mcp-marketplace-doc.ts +152 -0
  124. package/src/components/marketplace/mcp/mcp-marketplace-page.test.tsx +223 -0
  125. package/src/components/marketplace/mcp/mcp-marketplace-page.tsx +414 -0
  126. package/src/components/remote/remote-access-page.test.tsx +105 -0
  127. package/src/components/remote/remote-access-page.tsx +248 -0
  128. package/src/components/ui/notice-card.tsx +129 -0
  129. package/src/components/ui/setting-row.tsx +51 -0
  130. package/src/components/ui/tag-chip.tsx +39 -0
  131. package/src/components/ui/textarea.tsx +19 -0
  132. package/src/hooks/useConfig.ts +2 -1
  133. package/src/index.css +24 -0
  134. package/src/lib/app-resource-uri.test.ts +20 -0
  135. package/src/lib/app-resource-uri.ts +29 -0
  136. package/src/lib/i18n.remote.ts +1 -1
  137. package/src/lib/i18n.runtime-control.ts +31 -0
  138. package/src/lib/i18n.ts +5 -8
  139. package/src/lib/session-context.utils.test.ts +71 -0
  140. package/src/lib/session-context.utils.ts +28 -3
  141. package/src/lib/session-project/workspace-file-breadcrumb.test.ts +83 -0
  142. package/src/lib/session-project/workspace-file-breadcrumb.ts +188 -0
  143. package/dist/assets/ChannelsList-Ita2Zm1_.js +0 -8
  144. package/dist/assets/DocBrowser-BNwbPHf4.js +0 -1
  145. package/dist/assets/MarketplacePage-CjX2MWww.js +0 -1
  146. package/dist/assets/MarketplacePage-D0sDlYX4.js +0 -49
  147. package/dist/assets/McpMarketplacePage-BGKJm1sJ.js +0 -40
  148. package/dist/assets/ModelConfig-BzZenCH-.js +0 -1
  149. package/dist/assets/ProvidersList-BbVzRxjY.js +0 -1
  150. package/dist/assets/RemoteAccessPage-BaDH_X1Q.js +0 -1
  151. package/dist/assets/RuntimeConfig-F_XKGgLm.js +0 -1
  152. package/dist/assets/SearchConfig-BGkzXQP-.js +0 -1
  153. package/dist/assets/SessionsConfig-ChHQ7M5c.js +0 -2
  154. package/dist/assets/chat-page-Doe0yTtB.js +0 -58
  155. package/dist/assets/chat-session-display-cw78aiI_.js +0 -1
  156. package/dist/assets/config-layout-CHs0mAaR.js +0 -1
  157. package/dist/assets/desktop-update-config-Dpcf4BKG.js +0 -1
  158. package/dist/assets/index-CF9xve0E.js +0 -6
  159. package/dist/assets/index-FgA52VBt.css +0 -1
  160. package/dist/assets/loader-circle-ACM1s51e.js +0 -1
  161. package/dist/assets/play-CFUwCA2E.js +0 -1
  162. package/dist/assets/plus-rYsv72JG.js +0 -1
  163. package/dist/assets/popover-Bg1VoTZ6.js +0 -1
  164. package/dist/assets/search-3kFR_zh9.js +0 -1
  165. package/dist/assets/security-config-BWaiARNk.js +0 -1
  166. package/dist/assets/select-DJ2MUjBB.js +0 -41
  167. package/dist/assets/skeleton-ByQepn0M.js +0 -1
  168. package/dist/assets/x-ByDbItbq.js +0 -1
  169. package/src/components/agents/AgentDialogs.tsx +0 -400
  170. package/src/components/agents/AgentsPage.test.tsx +0 -217
  171. package/src/components/agents/AgentsPage.tsx +0 -352
  172. package/src/components/config/config-layout.ts +0 -10
  173. package/src/components/marketplace/MarketplacePage.test.tsx +0 -322
  174. package/src/components/marketplace/MarketplacePage.tsx +0 -827
  175. package/src/components/marketplace/mcp/McpMarketplacePage.test.tsx +0 -208
  176. package/src/components/marketplace/mcp/McpMarketplacePage.tsx +0 -580
  177. package/src/components/remote/RemoteAccessPage.test.tsx +0 -103
  178. package/src/components/remote/RemoteAccessPage.tsx +0 -144
@@ -1,322 +0,0 @@
1
- import { render, screen } from '@testing-library/react';
2
- import userEvent from '@testing-library/user-event';
3
- import { MarketplacePage } from '@/components/marketplace/MarketplacePage';
4
- import type {
5
- MarketplaceInstalledRecord,
6
- MarketplaceInstalledView,
7
- MarketplaceItemSummary,
8
- MarketplaceListView
9
- } from '@/api/types';
10
-
11
- type ItemsQueryState = {
12
- data?: MarketplaceListView;
13
- isLoading: boolean;
14
- isFetching: boolean;
15
- isError: boolean;
16
- error: Error | null;
17
- };
18
-
19
- type InstalledQueryState = {
20
- data?: MarketplaceInstalledView;
21
- isLoading: boolean;
22
- isFetching: boolean;
23
- isError: boolean;
24
- error: Error | null;
25
- };
26
-
27
- const mocks = vi.hoisted(() => ({
28
- navigate: vi.fn(),
29
- docOpen: vi.fn(),
30
- confirm: vi.fn(),
31
- itemsQuery: null as unknown as ItemsQueryState,
32
- installedQuery: null as unknown as InstalledQueryState,
33
- installMutation: {
34
- mutateAsync: vi.fn(),
35
- isPending: false,
36
- variables: undefined
37
- },
38
- manageMutation: {
39
- mutate: vi.fn(),
40
- mutateAsync: vi.fn(),
41
- isPending: false,
42
- variables: undefined
43
- }
44
- }));
45
-
46
- vi.mock('react-router-dom', async () => {
47
- const actual = await vi.importActual('react-router-dom');
48
- return {
49
- ...(actual as object),
50
- useNavigate: () => mocks.navigate,
51
- useParams: () => ({})
52
- };
53
- });
54
-
55
- vi.mock('@/components/doc-browser', () => ({
56
- useDocBrowser: () => ({
57
- open: mocks.docOpen
58
- })
59
- }));
60
-
61
- vi.mock('@/components/providers/I18nProvider', () => ({
62
- useI18n: () => ({
63
- language: 'en'
64
- })
65
- }));
66
-
67
- vi.mock('@/hooks/useConfirmDialog', () => ({
68
- useConfirmDialog: () => ({
69
- confirm: mocks.confirm,
70
- ConfirmDialog: () => null
71
- })
72
- }));
73
-
74
- vi.mock('@/hooks/useMarketplace', () => ({
75
- useMarketplaceItems: () => mocks.itemsQuery,
76
- useMarketplaceInstalled: () => mocks.installedQuery,
77
- useInstallMarketplaceItem: () => mocks.installMutation,
78
- useManageMarketplaceItem: () => mocks.manageMutation
79
- }));
80
-
81
- function createMarketplaceItem(overrides: Partial<MarketplaceItemSummary> = {}): MarketplaceItemSummary {
82
- return {
83
- id: 'skill-web-search',
84
- slug: 'web-search',
85
- type: 'skill',
86
- name: 'Web Search',
87
- summary: 'Search the web from the marketplace',
88
- summaryI18n: { en: 'Search the web from the marketplace' },
89
- tags: ['search'],
90
- author: 'NextClaw',
91
- install: {
92
- kind: 'marketplace',
93
- spec: '@nextclaw/web-search',
94
- command: 'nextclaw skills install @nextclaw/web-search'
95
- },
96
- updatedAt: '2026-03-17T00:00:00.000Z',
97
- ...overrides
98
- };
99
- }
100
-
101
- function createPluginMarketplaceItem(overrides: Partial<MarketplaceItemSummary> = {}): MarketplaceItemSummary {
102
- return createMarketplaceItem({
103
- id: 'plugin-codex-runtime',
104
- slug: 'codex-runtime',
105
- type: 'plugin',
106
- name: 'Codex SDK NCP Runtime',
107
- summary: 'Optional Codex runtime for NextClaw',
108
- summaryI18n: { en: 'Optional Codex runtime for NextClaw' },
109
- install: {
110
- kind: 'npm',
111
- spec: '@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk',
112
- command: 'npm install @nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk'
113
- },
114
- ...overrides
115
- });
116
- }
117
-
118
- function createInstalledRecord(overrides: Partial<MarketplaceInstalledRecord> = {}): MarketplaceInstalledRecord {
119
- return {
120
- type: 'plugin',
121
- id: '@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk',
122
- spec: '@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk',
123
- label: 'Codex SDK NCP Runtime',
124
- enabled: true,
125
- origin: 'marketplace',
126
- source: 'marketplace',
127
- installedAt: '2026-03-19T00:00:00.000Z',
128
- ...overrides
129
- };
130
- }
131
-
132
- function createItemsQuery(overrides: Partial<Record<string, unknown>> = {}) {
133
- return {
134
- data: undefined as MarketplaceListView | undefined,
135
- isLoading: false,
136
- isFetching: false,
137
- isError: false,
138
- error: null,
139
- ...overrides
140
- };
141
- }
142
-
143
- function createInstalledQuery(overrides: Partial<Record<string, unknown>> = {}) {
144
- return {
145
- data: {
146
- type: 'skill',
147
- total: 0,
148
- specs: [],
149
- records: []
150
- } satisfies MarketplaceInstalledView,
151
- isLoading: false,
152
- isFetching: false,
153
- isError: false,
154
- error: null,
155
- ...overrides
156
- };
157
- }
158
-
159
- describe('MarketplacePage', () => {
160
- beforeEach(() => {
161
- mocks.navigate.mockReset();
162
- mocks.docOpen.mockReset();
163
- mocks.confirm.mockReset();
164
- mocks.installMutation.mutateAsync.mockReset();
165
- mocks.manageMutation.mutate.mockReset();
166
- mocks.manageMutation.mutateAsync.mockReset();
167
- mocks.installMutation.isPending = false;
168
- mocks.installMutation.variables = undefined;
169
- mocks.manageMutation.isPending = false;
170
- mocks.manageMutation.variables = undefined;
171
- mocks.itemsQuery = createItemsQuery();
172
- mocks.installedQuery = createInstalledQuery();
173
- });
174
-
175
- it('renders skeleton cards during initial skills loading', () => {
176
- mocks.itemsQuery = createItemsQuery({
177
- isLoading: true,
178
- isFetching: true
179
- });
180
-
181
- const { container } = render(<MarketplacePage forcedType="skills" />);
182
-
183
- expect(screen.getByTestId('marketplace-list-skeleton')).toBeTruthy();
184
- expect(container.querySelectorAll('[data-testid="marketplace-list-skeleton"] > article')).toHaveLength(12);
185
- });
186
-
187
- it('keeps loaded cards visible during background refresh', () => {
188
- mocks.itemsQuery = createItemsQuery({
189
- data: {
190
- total: 1,
191
- page: 1,
192
- pageSize: 12,
193
- totalPages: 1,
194
- sort: 'relevance',
195
- items: [createMarketplaceItem()]
196
- } satisfies MarketplaceListView,
197
- isFetching: true
198
- });
199
-
200
- render(<MarketplacePage forcedType="skills" />);
201
-
202
- expect(screen.queryByTestId('marketplace-list-skeleton')).toBeNull();
203
- expect(screen.getByText('Web Search')).toBeTruthy();
204
- });
205
-
206
- it('does not render the redundant plugin type label in plugin cards', () => {
207
- mocks.itemsQuery = createItemsQuery({
208
- data: {
209
- total: 1,
210
- page: 1,
211
- pageSize: 12,
212
- totalPages: 1,
213
- sort: 'relevance',
214
- items: [
215
- createMarketplaceItem({
216
- id: 'plugin-codex-runtime',
217
- slug: 'codex-runtime',
218
- type: 'plugin',
219
- name: 'Codex SDK NCP Runtime',
220
- summary: 'Optional Codex runtime for NextClaw',
221
- summaryI18n: { en: 'Optional Codex runtime for NextClaw' },
222
- install: {
223
- kind: 'npm',
224
- spec: '@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk',
225
- command: 'npm install @nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk'
226
- }
227
- })
228
- ]
229
- } satisfies MarketplaceListView
230
- });
231
-
232
- const { container } = render(<MarketplacePage forcedType="plugins" />);
233
- const card = container.querySelector('article');
234
-
235
- expect(card?.textContent).toContain('@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk');
236
- expect(card?.textContent).not.toContain('Plugin');
237
- });
238
-
239
- it('does not dim the loaded list during background refresh', () => {
240
- mocks.itemsQuery = createItemsQuery({
241
- data: {
242
- total: 1,
243
- page: 1,
244
- pageSize: 12,
245
- totalPages: 1,
246
- sort: 'relevance',
247
- items: [createPluginMarketplaceItem()]
248
- } satisfies MarketplaceListView,
249
- isFetching: true
250
- });
251
-
252
- const { container } = render(<MarketplacePage forcedType="plugins" />);
253
-
254
- expect(screen.getByText('Codex SDK NCP Runtime')).toBeTruthy();
255
- expect(container.querySelector('.opacity-70')).toBeNull();
256
- });
257
-
258
- it('only disables the targeted plugin action while a manage request is pending', async () => {
259
- const user = userEvent.setup();
260
- let resolveMutation: (() => void) | undefined;
261
- mocks.itemsQuery = createItemsQuery({
262
- data: {
263
- total: 2,
264
- page: 1,
265
- pageSize: 12,
266
- totalPages: 1,
267
- sort: 'relevance',
268
- items: [
269
- createPluginMarketplaceItem(),
270
- createPluginMarketplaceItem({
271
- id: 'plugin-claude-runtime',
272
- slug: 'claude-runtime',
273
- name: 'Claude Agent Runtime',
274
- install: {
275
- kind: 'npm',
276
- spec: '@nextclaw/nextclaw-ncp-runtime-plugin-claude-code-sdk',
277
- command: 'npm install @nextclaw/nextclaw-ncp-runtime-plugin-claude-code-sdk'
278
- }
279
- })
280
- ]
281
- } satisfies MarketplaceListView
282
- });
283
- mocks.installedQuery = createInstalledQuery({
284
- data: {
285
- type: 'plugin',
286
- total: 2,
287
- specs: [
288
- '@nextclaw/nextclaw-ncp-runtime-plugin-codex-sdk',
289
- '@nextclaw/nextclaw-ncp-runtime-plugin-claude-code-sdk'
290
- ],
291
- records: [
292
- createInstalledRecord(),
293
- createInstalledRecord({
294
- id: '@nextclaw/nextclaw-ncp-runtime-plugin-claude-code-sdk',
295
- spec: '@nextclaw/nextclaw-ncp-runtime-plugin-claude-code-sdk',
296
- label: 'Claude Agent Runtime'
297
- })
298
- ]
299
- } satisfies MarketplaceInstalledView
300
- });
301
- mocks.manageMutation.mutateAsync.mockImplementation(
302
- () => new Promise<void>((resolve) => {
303
- resolveMutation = resolve;
304
- })
305
- );
306
-
307
- render(<MarketplacePage forcedType="plugins" />);
308
-
309
- const disableButtons = screen.getAllByRole('button', { name: 'Disable' });
310
- const firstDisableButton = disableButtons[0];
311
- const secondDisableButton = disableButtons[1];
312
-
313
- await user.click(firstDisableButton);
314
-
315
- expect(mocks.manageMutation.mutateAsync).toHaveBeenCalledTimes(1);
316
- expect(firstDisableButton.hasAttribute('disabled')).toBe(true);
317
- expect(firstDisableButton.textContent).toContain('Disabling');
318
- expect(secondDisableButton.hasAttribute('disabled')).toBe(false);
319
-
320
- resolveMutation?.();
321
- });
322
- });