@nextclaw/ui 0.13.3 → 0.13.5

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 (132) hide show
  1. package/CHANGELOG.md +128 -0
  2. package/dist/assets/api-BAyyGaoz.js +15 -0
  3. package/dist/assets/app-presenter-provider-DqHHfC1K.js +3 -0
  4. package/dist/assets/arrow-left-Bn-3Pj3p.js +1 -0
  5. package/dist/assets/{boxes-BvA-I63l.js → boxes-ClVuotcz.js} +1 -1
  6. package/dist/assets/channels-list-page-BnGxv6ma.js +8 -0
  7. package/dist/assets/chat-page-DInyWLb4.js +105 -0
  8. package/dist/assets/config-split-page-CWN2YG9U.js +1 -0
  9. package/dist/assets/confirm-dialog-CVbgKuHp.js +5 -0
  10. package/dist/assets/{createLucideIcon-Br1EFMEr.js → createLucideIcon-CjPUPAAK.js} +1 -1
  11. package/dist/assets/desktop-update-config-abmYzHSy.js +1 -0
  12. package/dist/assets/{dist-rzakR_bu.js → dist-hkrb6Ynt.js} +1 -1
  13. package/dist/assets/doc-browser-BlrpUKVJ.js +1 -0
  14. package/dist/assets/doc-browser-context-Cb1sEUT0.js +1 -0
  15. package/dist/assets/doc-browser-kJ2ClG0H.js +1 -0
  16. package/dist/assets/ellipsis-CdkxFkVZ.js +1 -0
  17. package/dist/assets/{external-link-DHjFdCpy.js → external-link-CT9_qFwN.js} +1 -1
  18. package/dist/assets/index-BGXTR0F_.css +1 -0
  19. package/dist/assets/index-Ccbswzt9.js +101 -0
  20. package/dist/assets/{key-round-D41Oe35x.js → key-round-xJfR4iRe.js} +1 -1
  21. package/dist/assets/{book-open-CRotBOF2.js → loader-circle-BI6HJDg5.js} +1 -1
  22. package/dist/assets/{mcp-marketplace-page-Dvi8mCBd.js → mcp-marketplace-page-CmUKUeno.js} +2 -2
  23. package/dist/assets/mcp-marketplace-page-DH11bSVx.js +1 -0
  24. package/dist/assets/model-config-Dbo3zWDl.js +1 -0
  25. package/dist/assets/notice-card-CFTFtJky.js +1 -0
  26. package/dist/assets/play-DyBWW2j4.js +1 -0
  27. package/dist/assets/plus-BlnBmcKE.js +1 -0
  28. package/dist/assets/popover-DPqa_Mfb.js +1 -0
  29. package/dist/assets/provider-models-CXUgDxGR.js +1 -0
  30. package/dist/assets/provider-scoped-model-input-C6xL7zSJ.js +1 -0
  31. package/dist/assets/providers-list-DOj_7GL8.js +1 -0
  32. package/dist/assets/react-oAHNoVya.js +1 -0
  33. package/dist/assets/refresh-cw-o8NSmIYV.js +1 -0
  34. package/dist/assets/remote-fCE5fLsj.js +1 -0
  35. package/dist/assets/rotate-cw-DA8EqNAH.js +1 -0
  36. package/dist/assets/runtime-config-page-Dg8aEgWx.js +1 -0
  37. package/dist/assets/{save-aYdSdK97.js → save-D4-g0EAq.js} +1 -1
  38. package/dist/assets/search-G2cMn3M5.js +1 -0
  39. package/dist/assets/{search-config-G2O1fcRb.js → search-config-DAibVFGR.js} +1 -1
  40. package/dist/assets/{secrets-config-TenzxYbE.js → secrets-config-CoJzy13_.js} +2 -2
  41. package/dist/assets/{status-dot-BSMG7ZaC.js → status-dot-D8xx5ZR8.js} +1 -1
  42. package/dist/assets/{tabs-D1CjpWNX.js → tabs-DxmND60Q.js} +1 -1
  43. package/dist/assets/{tabs-custom-CRSN1ekg.js → tabs-custom-Cjo0bHw8.js} +1 -1
  44. package/dist/assets/{tag-chip-DEashiw6.js → tag-chip-DnNWFRMu.js} +1 -1
  45. package/dist/assets/{trash-2-DSWTsARW.js → trash-2-C9tc-9sQ.js} +1 -1
  46. package/dist/assets/x-DmLl2yG6.js +1 -0
  47. package/dist/index.html +24 -23
  48. package/dist/logos/mimo.svg +9 -0
  49. package/package.json +8 -8
  50. package/public/logos/mimo.svg +9 -0
  51. package/src/app/components/layout/app-layout.tsx +10 -2
  52. package/src/features/agents/components/agents-page.tsx +6 -4
  53. package/src/features/chat/hooks/use-ncp-chat-page-data.ts +17 -10
  54. package/src/features/marketplace/components/marketplace-list-card.tsx +22 -1
  55. package/src/features/marketplace/components/marketplace-page.tsx +3 -1
  56. package/src/features/marketplace/hooks/use-marketplace.ts +3 -1
  57. package/src/features/marketplace/utils/marketplace-installed-cache.utils.ts +8 -0
  58. package/src/features/right-panel-resources/configs/right-panel-resource-routes.config.ts +70 -6
  59. package/src/features/right-panel-resources/utils/right-panel-resource-route-resolver.utils.test.ts +25 -1
  60. package/src/features/right-panel-resources/utils/right-panel-resource-route-resolver.utils.ts +1 -1
  61. package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.test.ts +42 -0
  62. package/src/features/right-panel-resources/utils/right-panel-resource-uri.utils.ts +22 -0
  63. package/src/features/service-apps/components/service-apps-panel.test.tsx +88 -0
  64. package/src/features/service-apps/components/service-apps-panel.tsx +246 -56
  65. package/src/features/service-apps/hooks/use-service-apps.ts +14 -0
  66. package/src/features/side-dock/components/side-dock.test.tsx +58 -3
  67. package/src/features/side-dock/components/side-dock.tsx +77 -24
  68. package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +37 -34
  69. package/src/features/side-dock/index.ts +2 -0
  70. package/src/features/side-dock/managers/side-dock.manager.test.ts +84 -3
  71. package/src/features/side-dock/managers/side-dock.manager.ts +135 -12
  72. package/src/features/side-dock/types/side-dock.types.ts +11 -4
  73. package/src/features/side-dock/utils/side-dock-item.utils.test.ts +8 -8
  74. package/src/features/side-dock/utils/side-dock-item.utils.ts +13 -5
  75. package/src/platforms/desktop/components/desktop-app-shell.tsx +5 -2
  76. package/src/platforms/mobile/components/mobile-app-shell.tsx +4 -1
  77. package/src/shared/components/config/provider-form-support.test.ts +21 -0
  78. package/src/shared/components/config/provider-form-support.ts +38 -78
  79. package/src/shared/components/config/provider-form.tsx +54 -67
  80. package/src/shared/components/config/providers-list.tsx +211 -73
  81. package/src/shared/components/doc-browser/doc-browser-context.test.tsx +73 -0
  82. package/src/shared/components/doc-browser/doc-browser-context.tsx +3 -0
  83. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +25 -2
  84. package/src/shared/components/doc-browser/doc-browser.test.tsx +42 -0
  85. package/src/shared/components/doc-browser/doc-browser.tsx +38 -7
  86. package/src/shared/components/doc-browser/index.ts +1 -0
  87. package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +30 -6
  88. package/src/shared/components/doc-browser/stores/doc-browser.store.ts +29 -0
  89. package/src/shared/components/doc-browser/types/doc-browser.types.ts +23 -0
  90. package/src/shared/components/doc-browser/utils/doc-browser-state.utils.ts +7 -0
  91. package/src/shared/components/model-config.tsx +5 -4
  92. package/src/shared/components/provider-auth-section.tsx +2 -2
  93. package/src/shared/components/ui/switch.tsx +5 -2
  94. package/src/shared/hooks/use-config.ts +27 -6
  95. package/src/shared/lib/api/marketplace.types.ts +193 -0
  96. package/src/shared/lib/api/types.ts +23 -200
  97. package/src/shared/lib/api/utils/config.utils.ts +17 -7
  98. package/src/shared/lib/i18n/index.ts +10 -12
  99. package/src/shared/lib/i18n/{marketplace.ts → marketplace-labels.utils.ts} +3 -0
  100. package/src/shared/lib/i18n/runtime/doc-browser-labels.utils.ts +46 -2
  101. package/src/shared/lib/provider-models/index.test.ts +93 -15
  102. package/src/shared/lib/provider-models/index.ts +35 -20
  103. package/src/shared/lib/transport/request-raw-api-response.utils.ts +1 -1
  104. package/dist/assets/api-DruAcori.js +0 -15
  105. package/dist/assets/app-presenter-provider-DIcnNB9s.js +0 -3
  106. package/dist/assets/channels-list-page-wK3l7p5R.js +0 -8
  107. package/dist/assets/chat-page-DgehvdJT.js +0 -105
  108. package/dist/assets/config-split-page-BnTO1mAM.js +0 -1
  109. package/dist/assets/confirm-dialog-CGrKHbAW.js +0 -5
  110. package/dist/assets/desktop-update-config-CehWxSSg.js +0 -1
  111. package/dist/assets/dist-CpwDZDtv.js +0 -1
  112. package/dist/assets/doc-browser-BgxmVsTx.js +0 -1
  113. package/dist/assets/doc-browser-CZn5-Qbu.js +0 -1
  114. package/dist/assets/doc-browser-context-B8gk7HMr.js +0 -1
  115. package/dist/assets/index-DORmh0Cy.css +0 -1
  116. package/dist/assets/index-DcFpvnge.js +0 -101
  117. package/dist/assets/mcp-marketplace-page-DBLhJWOJ.js +0 -1
  118. package/dist/assets/model-config-B-EJXGtO.js +0 -1
  119. package/dist/assets/play-C-0vo7hi.js +0 -1
  120. package/dist/assets/plus-Cr_akLp2.js +0 -1
  121. package/dist/assets/popover-YieElO8c.js +0 -1
  122. package/dist/assets/provider-scoped-model-input-BPRNGqYn.js +0 -1
  123. package/dist/assets/providers-list-CtBpL1ZY.js +0 -1
  124. package/dist/assets/react-B2X_ph0a.js +0 -1
  125. package/dist/assets/remote-BGHbHoHf.js +0 -1
  126. package/dist/assets/rotate-cw-jueVCNJQ.js +0 -1
  127. package/dist/assets/runtime-config-page-C1Hv7mad.js +0 -1
  128. package/dist/assets/search-CdhgFNwf.js +0 -1
  129. package/dist/assets/setting-row-BNu0qhOM.js +0 -1
  130. package/dist/assets/x-D1v_vb54.js +0 -1
  131. package/src/shared/components/provider-enabled-field.tsx +0 -23
  132. /package/dist/assets/{config-hints-A7G_jMTU.js → config-hints-TmNgpFHv.js} +0 -0
@@ -2,13 +2,102 @@ import { describe, expect, it } from 'vitest';
2
2
  import { buildProviderModelCatalog } from './index';
3
3
 
4
4
  describe('buildProviderModelCatalog', () => {
5
+ it('keeps empty provider models empty', () => {
6
+ const catalog = buildProviderModelCatalog({
7
+ onlyConfigured: false,
8
+ providersView: {
9
+ providers: {
10
+ openai: {
11
+ providerId: 'openai',
12
+ providerType: 'openai',
13
+ isBuiltInType: true,
14
+ isCustom: false,
15
+ enabled: true,
16
+ apiKeySet: true,
17
+ models: []
18
+ }
19
+ }
20
+ },
21
+ templatesView: {
22
+ providerTemplates: [
23
+ {
24
+ id: 'openai',
25
+ providerType: 'openai',
26
+ displayName: 'OpenAI',
27
+ modelPrefix: 'openai',
28
+ defaultModels: ['openai/gpt-5.5', 'openai/gpt-5.4'],
29
+ keywords: [],
30
+ envKey: 'OPENAI_API_KEY'
31
+ }
32
+ ],
33
+ }
34
+ });
35
+
36
+ expect(catalog).toHaveLength(1);
37
+ expect(catalog[0]?.models).toEqual([]);
38
+ });
39
+
40
+ it('keeps builtin modelConfig when a configured provider has an empty persisted modelConfig', () => {
41
+ const catalog = buildProviderModelCatalog({
42
+ onlyConfigured: true,
43
+ providersView: {
44
+ providers: {
45
+ gemini: {
46
+ providerId: 'gemini',
47
+ providerType: 'gemini',
48
+ isBuiltInType: true,
49
+ isCustom: false,
50
+ enabled: true,
51
+ apiKeySet: true,
52
+ models: ['gemini/gemini-3.1-pro-preview'],
53
+ modelConfig: {}
54
+ }
55
+ }
56
+ },
57
+ templatesView: {
58
+ providerTemplates: [
59
+ {
60
+ id: 'gemini',
61
+ providerType: 'gemini',
62
+ displayName: 'Gemini',
63
+ modelPrefix: 'gemini',
64
+ defaultModels: ['gemini/gemini-3.1-pro-preview'],
65
+ modelConfig: {
66
+ 'gemini/gemini-3.1-pro-preview': { vision: true }
67
+ },
68
+ keywords: [],
69
+ envKey: 'GEMINI_API_KEY'
70
+ }
71
+ ],
72
+ }
73
+ });
74
+
75
+ expect(catalog[0]?.modelConfig).toEqual({
76
+ 'gemini-3.1-pro-preview': { vision: true }
77
+ });
78
+ });
79
+
5
80
  it('does not resurrect an OpenRouter model removed from config models', () => {
6
81
  const catalog = buildProviderModelCatalog({
7
82
  onlyConfigured: true,
8
- meta: {
9
- providers: [
83
+ providersView: {
84
+ providers: {
85
+ openrouter: {
86
+ providerId: 'openrouter',
87
+ providerType: 'openrouter',
88
+ isBuiltInType: true,
89
+ isCustom: false,
90
+ enabled: true,
91
+ apiKeySet: true,
92
+ models: ['openrouter/openai/gpt-5.3-codex']
93
+ }
94
+ }
95
+ },
96
+ templatesView: {
97
+ providerTemplates: [
10
98
  {
11
- name: 'openrouter',
99
+ id: 'openrouter',
100
+ providerType: 'openrouter',
12
101
  displayName: 'OpenRouter',
13
102
  modelPrefix: 'openrouter',
14
103
  defaultModels: [
@@ -19,18 +108,7 @@ describe('buildProviderModelCatalog', () => {
19
108
  envKey: 'OPENROUTER_API_KEY'
20
109
  }
21
110
  ],
22
- search: [],
23
- channels: []
24
- },
25
- config: {
26
- providers: {
27
- openrouter: {
28
- enabled: true,
29
- apiKeySet: true,
30
- models: ['openai/gpt-5.3-codex']
31
- }
32
- }
33
- } as never
111
+ }
34
112
  });
35
113
 
36
114
  expect(catalog).toHaveLength(1);
@@ -1,6 +1,6 @@
1
- import type { ConfigMetaView, ConfigView, ProviderConfigView, ThinkingLevel } from '@/shared/lib/api';
1
+ import type { ConfigView, ProviderConfigView, ProviderTemplateView, ProvidersView, ProviderTemplatesView, ThinkingLevel } from '@/shared/lib/api';
2
2
 
3
- const THINKING_LEVELS: ThinkingLevel[] = ['off', 'minimal', 'low', 'medium', 'high', 'adaptive', 'xhigh'];
3
+ export const THINKING_LEVELS: ThinkingLevel[] = ['off', 'minimal', 'low', 'medium', 'high', 'adaptive', 'xhigh'];
4
4
  const THINKING_LEVEL_SET = new Set<string>(THINKING_LEVELS);
5
5
 
6
6
  export type ModelThinkingCapability = {
@@ -38,6 +38,14 @@ export function normalizeStringList(input: string[] | null | undefined): string[
38
38
  return [...deduped];
39
39
  }
40
40
 
41
+ export function resolveModelsWithDefaults(defaultModels: string[], savedModels: string[]): string[] {
42
+ const normalizedSavedModels = normalizeStringList(savedModels);
43
+ if (normalizedSavedModels.length > 0) {
44
+ return normalizedSavedModels;
45
+ }
46
+ return normalizeStringList(defaultModels);
47
+ }
48
+
41
49
  export function stripProviderPrefix(model: string, prefix: string): string {
42
50
  const trimmed = model.trim();
43
51
  const cleanPrefix = prefix.trim();
@@ -74,7 +82,7 @@ export function composeProviderModel(prefix: string, localModel: string): string
74
82
  return `${normalizedPrefix}/${normalizedModel}`;
75
83
  }
76
84
 
77
- function parseThinkingLevel(value: unknown): ThinkingLevel | null {
85
+ export function parseThinkingLevel(value: unknown): ThinkingLevel | null {
78
86
  if (typeof value !== 'string') {
79
87
  return null;
80
88
  }
@@ -85,7 +93,7 @@ function parseThinkingLevel(value: unknown): ThinkingLevel | null {
85
93
  return THINKING_LEVEL_SET.has(normalized) ? (normalized as ThinkingLevel) : null;
86
94
  }
87
95
 
88
- function normalizeThinkingLevels(values: unknown): ThinkingLevel[] {
96
+ export function normalizeThinkingLevels(values: unknown): ThinkingLevel[] {
89
97
  if (!Array.isArray(values)) {
90
98
  return [];
91
99
  }
@@ -132,11 +140,9 @@ export function normalizeModelConfigMap(
132
140
  return normalized;
133
141
  }
134
142
 
135
- export function normalizeModelThinkingMap(
136
- input: ProviderConfigView['modelConfig'],
137
- aliases: string[]
143
+ function extractModelThinkingMap(
144
+ modelConfig: Record<string, ModelConfig>
138
145
  ): Record<string, ModelThinkingCapability> {
139
- const modelConfig = normalizeModelConfigMap(input, aliases);
140
146
  return Object.fromEntries(
141
147
  Object.entries(modelConfig)
142
148
  .filter((entry): entry is [string, ModelConfig & { thinking: ModelThinkingCapability }] =>
@@ -204,28 +210,37 @@ function isProviderConfigured(provider: ProviderConfigView | undefined): boolean
204
210
  }
205
211
 
206
212
  export function buildProviderModelCatalog(params: {
207
- meta?: ConfigMetaView;
213
+ providersView?: ProvidersView;
214
+ templatesView?: ProviderTemplatesView;
208
215
  config?: ConfigView;
209
216
  onlyConfigured?: boolean;
210
217
  }): ProviderModelCatalogItem[] {
211
- const { meta, config, onlyConfigured = false } = params;
218
+ const { providersView, templatesView, config, onlyConfigured = false } = params;
219
+ const templateByType = new Map((templatesView?.providerTemplates ?? []).map((template) => [template.providerType, template]));
220
+ const providerEntries = Object.values(providersView?.providers ?? config?.providers ?? {});
212
221
 
213
- const catalog = (meta?.providers ?? []).map((spec) => {
214
- const providerConfig = config?.providers?.[spec.name];
215
- const prefix = (spec.modelPrefix || spec.name || '').trim();
216
- const aliases = normalizeStringList([spec.modelPrefix || '', spec.name || '']);
217
- const models = normalizeStringList(
222
+ const catalog = providerEntries.map((providerConfig) => {
223
+ const { providerId } = providerConfig;
224
+ const template: ProviderTemplateView | undefined = providerConfig.providerType
225
+ ? templateByType.get(providerConfig.providerType)
226
+ : undefined;
227
+ const prefix = providerId.trim();
228
+ const aliases = normalizeStringList([providerId, template?.modelPrefix || '', template?.providerType || '']);
229
+ const savedModels = normalizeStringList(
218
230
  (providerConfig?.models ?? []).map((model) => toProviderLocalModel(model, aliases))
219
231
  );
220
- const rawModelConfig = providerConfig?.modelConfig ?? spec.modelConfig;
221
- const modelConfig = normalizeModelConfigMap(rawModelConfig, aliases);
222
- const modelThinking = normalizeModelThinkingMap(rawModelConfig, aliases);
232
+ const models = savedModels;
233
+ const modelConfig = {
234
+ ...normalizeModelConfigMap(template?.modelConfig, aliases),
235
+ ...normalizeModelConfigMap(providerConfig?.modelConfig, aliases)
236
+ };
237
+ const modelThinking = extractModelThinkingMap(modelConfig);
223
238
  const configDisplayName = providerConfig?.displayName?.trim();
224
239
  const configured = isProviderConfigured(providerConfig);
225
240
 
226
241
  return {
227
- name: spec.name,
228
- displayName: configDisplayName || spec.displayName || spec.name,
242
+ name: providerId,
243
+ displayName: configDisplayName || template?.displayName || providerId,
229
244
  prefix,
230
245
  aliases,
231
246
  models,
@@ -8,7 +8,7 @@ function compactSnippet(text: string) {
8
8
  function inferNonJsonHint(endpoint: string, status: number): string | undefined {
9
9
  if (
10
10
  status === 404 &&
11
- endpoint.startsWith('/api/config/providers/') &&
11
+ endpoint.startsWith('/api/providers/') &&
12
12
  endpoint.endsWith('/test')
13
13
  ) {
14
14
  return 'Provider test endpoint is missing. This usually means nextclaw runtime version is outdated.';