mcp-probe-kit 3.0.19 → 3.0.22
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.
- package/README.md +12 -5
- package/build/index.js +3 -1
- package/build/lib/__tests__/agents-md-template.unit.test.js +2 -0
- package/build/lib/__tests__/memory-config.unit.test.js +9 -0
- package/build/lib/__tests__/memory-injection.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-injection.unit.test.js +51 -0
- package/build/lib/__tests__/memory-orchestration.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-orchestration.unit.test.js +84 -0
- package/build/lib/__tests__/memory-payload.unit.test.d.ts +1 -0
- package/build/lib/__tests__/memory-payload.unit.test.js +35 -0
- package/build/lib/agents-md-template.js +7 -5
- package/build/lib/memory-client.d.ts +8 -1
- package/build/lib/memory-client.js +53 -44
- package/build/lib/memory-config.d.ts +8 -0
- package/build/lib/memory-config.js +19 -0
- package/build/lib/memory-orchestration.d.ts +7 -2
- package/build/lib/memory-orchestration.js +81 -8
- package/build/lib/memory-payload.d.ts +21 -0
- package/build/lib/memory-payload.js +65 -0
- package/build/lib/shadcn-ui.d.ts +11 -0
- package/build/lib/shadcn-ui.js +78 -0
- package/build/resources/ui-ux-data/guidelines/vercel-web-interface.json +1632 -0
- package/build/resources/ui-ux-data/metadata.json +27 -3
- package/build/resources/ui-ux-data/shadcn/blocks.json +2541 -0
- package/build/resources/ui-ux-data/shadcn/components.json +997 -0
- package/build/resources/ui-ux-data/themes/presets.json +483 -0
- package/build/schemas/index.d.ts +38 -9
- package/build/schemas/memory-tools.d.ts +38 -9
- package/build/schemas/memory-tools.js +24 -9
- package/build/schemas/output/ui-ux-tools.d.ts +16 -0
- package/build/schemas/output/ui-ux-tools.js +4 -0
- package/build/schemas/ui-ux-schemas.js +3 -3
- package/build/tools/__tests__/start_ui.property.test.js +4 -3
- package/build/tools/index.d.ts +1 -0
- package/build/tools/index.js +1 -0
- package/build/tools/memorize_asset.js +12 -0
- package/build/tools/scan_and_extract_patterns.js +7 -7
- package/build/tools/search_memory.d.ts +7 -0
- package/build/tools/search_memory.js +57 -0
- package/build/tools/start_bugfix.js +3 -3
- package/build/tools/start_feature.js +3 -3
- package/build/tools/start_ui.js +33 -6
- package/build/tools/ui-ux-tools.js +322 -244
- package/build/utils/__tests__/shadcn-sync.unit.test.d.ts +1 -0
- package/build/utils/__tests__/shadcn-sync.unit.test.js +49 -0
- package/build/utils/__tests__/theme-pick.unit.test.d.ts +1 -0
- package/build/utils/__tests__/theme-pick.unit.test.js +9 -0
- package/build/utils/__tests__/themes-sync.unit.test.d.ts +1 -0
- package/build/utils/__tests__/themes-sync.unit.test.js +21 -0
- package/build/utils/__tests__/ui-metadata.unit.test.d.ts +1 -0
- package/build/utils/__tests__/ui-metadata.unit.test.js +35 -0
- package/build/utils/__tests__/vercel-guidelines-sync.unit.test.d.ts +1 -0
- package/build/utils/__tests__/vercel-guidelines-sync.unit.test.js +34 -0
- package/build/utils/bm25.d.ts +2 -1
- package/build/utils/bm25.js +17 -5
- package/build/utils/shadcn-sync.d.ts +55 -0
- package/build/utils/shadcn-sync.js +146 -0
- package/build/utils/themes-sync.d.ts +32 -0
- package/build/utils/themes-sync.js +201 -0
- package/build/utils/ui-data-loader.js +13 -2
- package/build/utils/ui-metadata.d.ts +27 -0
- package/build/utils/ui-metadata.js +39 -0
- package/build/utils/ui-search-engine.d.ts +1 -0
- package/build/utils/ui-search-engine.js +20 -6
- package/build/utils/ui-sync.d.ts +24 -2
- package/build/utils/ui-sync.js +152 -86
- package/build/utils/vercel-guidelines-sync.d.ts +30 -0
- package/build/utils/vercel-guidelines-sync.js +133 -0
- package/docs/data/tools.js +18 -0
- package/docs/i18n/all-tools/en.json +6 -1
- package/docs/i18n/all-tools/ja.json +2 -1
- package/docs/i18n/all-tools/ko.json +2 -1
- package/docs/i18n/all-tools/zh-CN.json +7 -2
- package/docs/i18n/en.json +5 -5
- package/docs/i18n/ja.json +2 -2
- package/docs/i18n/ko.json +2 -2
- package/docs/i18n/zh-CN.json +7 -7
- package/docs/memory-local-setup.md +1 -1
- package/docs/memory-local-setup.zh-CN.md +5 -2
- package/docs/pages/getting-started.html +3 -2
- package/package.json +2 -2
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { createMemoryClient } from './memory-client.js';
|
|
2
|
-
|
|
2
|
+
import { getMemoryConfig } from './memory-config.js';
|
|
3
|
+
function kindSearchPreferences(kind) {
|
|
4
|
+
switch (kind) {
|
|
5
|
+
case 'bugfix':
|
|
6
|
+
return { preferTypes: ['bugfix'], preferTags: ['bugfix', 'root-cause'] };
|
|
7
|
+
case 'ui':
|
|
8
|
+
return { preferTypes: ['component', 'pattern'], preferTags: ['ui', 'pattern'] };
|
|
9
|
+
case 'feature':
|
|
10
|
+
return { preferTypes: ['pattern', 'code'], preferTags: ['feature', 'pattern'] };
|
|
11
|
+
default:
|
|
12
|
+
return { preferTypes: [], preferTags: [] };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function truncateInjectionText(value, maxChars) {
|
|
16
|
+
if (value.length <= maxChars) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
return `${value.slice(0, Math.max(0, maxChars - 3))}...`;
|
|
20
|
+
}
|
|
21
|
+
async function loadFullAssets(results) {
|
|
22
|
+
const client = createMemoryClient();
|
|
23
|
+
if (!client.isReadEnabled() || results.length === 0) {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
const entries = await Promise.all(results.map(async (item) => {
|
|
27
|
+
const asset = await client.getAsset(item.id);
|
|
28
|
+
return asset ? [item.id, asset] : null;
|
|
29
|
+
}));
|
|
30
|
+
return Object.fromEntries(entries.filter((entry) => entry !== null));
|
|
31
|
+
}
|
|
32
|
+
export async function loadMemoryInjectionContext(query, kind = 'default') {
|
|
3
33
|
const client = createMemoryClient();
|
|
4
34
|
if (!client.isEnabled()) {
|
|
5
35
|
return {
|
|
@@ -8,16 +38,23 @@ export async function loadMemoryInjectionContext(query) {
|
|
|
8
38
|
degraded: false,
|
|
9
39
|
query,
|
|
10
40
|
results: [],
|
|
41
|
+
assetsById: {},
|
|
11
42
|
};
|
|
12
43
|
}
|
|
13
44
|
try {
|
|
14
|
-
const
|
|
45
|
+
const prefs = kindSearchPreferences(kind);
|
|
46
|
+
const results = await client.search(query, {
|
|
47
|
+
preferTypes: prefs.preferTypes,
|
|
48
|
+
preferTags: prefs.preferTags,
|
|
49
|
+
});
|
|
50
|
+
const assetsById = await loadFullAssets(results);
|
|
15
51
|
return {
|
|
16
52
|
enabled: true,
|
|
17
53
|
available: true,
|
|
18
54
|
degraded: false,
|
|
19
55
|
query,
|
|
20
56
|
results,
|
|
57
|
+
assetsById,
|
|
21
58
|
};
|
|
22
59
|
}
|
|
23
60
|
catch (error) {
|
|
@@ -27,6 +64,7 @@ export async function loadMemoryInjectionContext(query) {
|
|
|
27
64
|
degraded: true,
|
|
28
65
|
query,
|
|
29
66
|
results: [],
|
|
67
|
+
assetsById: {},
|
|
30
68
|
error: error instanceof Error ? error.message : String(error),
|
|
31
69
|
};
|
|
32
70
|
}
|
|
@@ -39,7 +77,44 @@ function formatMemoryResultLabel(item) {
|
|
|
39
77
|
: '历史资产';
|
|
40
78
|
return `${item.name} [${item.type}] (${kind})`;
|
|
41
79
|
}
|
|
80
|
+
export function shouldShowSourceInSearch(item, config = getMemoryConfig()) {
|
|
81
|
+
if (config.searchShowSource) {
|
|
82
|
+
return Boolean(item.sourcePath);
|
|
83
|
+
}
|
|
84
|
+
if (!config.repoId || !item.sourceProject || !item.sourcePath) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
return item.sourceProject === config.repoId;
|
|
88
|
+
}
|
|
89
|
+
function formatSourceHint(item, config) {
|
|
90
|
+
if (!shouldShowSourceInSearch(item, config)) {
|
|
91
|
+
return '';
|
|
92
|
+
}
|
|
93
|
+
return `\n - 来源: ${item.sourcePath}`;
|
|
94
|
+
}
|
|
95
|
+
function formatAssetBody(asset, config) {
|
|
96
|
+
const lines = [
|
|
97
|
+
`### ${asset.name}`,
|
|
98
|
+
`- asset_id: ${asset.id}`,
|
|
99
|
+
asset.description ? `- 描述: ${asset.description}` : '',
|
|
100
|
+
asset.usage ? `- 适用: ${asset.usage}` : '',
|
|
101
|
+
asset.tags.length > 0 ? `- 标签: ${asset.tags.join(', ')}` : '',
|
|
102
|
+
'',
|
|
103
|
+
truncateInjectionText(asset.content, config.injectionContentMaxChars),
|
|
104
|
+
].filter(Boolean);
|
|
105
|
+
return lines.join('\n');
|
|
106
|
+
}
|
|
107
|
+
function formatResultBlock(item, index, context, config) {
|
|
108
|
+
const label = formatMemoryResultLabel(item);
|
|
109
|
+
const asset = context.assetsById[item.id];
|
|
110
|
+
const header = `${index + 1}. ${label} score=${item.score.toFixed(3)}\n - 摘要: ${item.summary}${formatSourceHint(item, config)}`;
|
|
111
|
+
if (asset?.content) {
|
|
112
|
+
return `${header}\n\n${formatAssetBody(asset, config)}\n`;
|
|
113
|
+
}
|
|
114
|
+
return `${header}\n - 全文加载失败,可手动: read_memory_asset {"asset_id": "${item.id}"}\n`;
|
|
115
|
+
}
|
|
42
116
|
export function renderMemoryGuideSection(context) {
|
|
117
|
+
const config = getMemoryConfig();
|
|
43
118
|
if (!context.enabled) {
|
|
44
119
|
return '';
|
|
45
120
|
}
|
|
@@ -49,13 +124,11 @@ export function renderMemoryGuideSection(context) {
|
|
|
49
124
|
if (context.results.length === 0) {
|
|
50
125
|
return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 检索结果: 未找到高相关记录(含历史 Bug 修复与可复用模式)\n- 处理: 继续主流程;Bug 修复验证通过后必须 \`memorize_asset\` 沉淀;功能/UI 有可复用产出再沉淀\n`;
|
|
51
126
|
}
|
|
127
|
+
const loadedCount = context.results.filter((item) => context.assetsById[item.id]?.content).length;
|
|
52
128
|
const items = context.results
|
|
53
|
-
.map((item, index) =>
|
|
54
|
-
const label = formatMemoryResultLabel(item);
|
|
55
|
-
return `${index + 1}. ${label} score=${item.score.toFixed(3)}\n - 摘要: ${item.summary}\n - 读取: read_memory_asset {\"asset_id\": \"${item.id}\"}${item.sourcePath ? `\n - 来源: ${item.sourcePath}` : ''}`;
|
|
56
|
-
})
|
|
129
|
+
.map((item, index) => formatResultBlock(item, index, context, config))
|
|
57
130
|
.join('\n');
|
|
58
|
-
return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 指令:
|
|
131
|
+
return `\n\n## 🧠 记忆系统\n- 状态: 已启用\n- 指令: 下列为已自动加载的历史经验全文(${loadedCount}/${context.results.length} 条);开干前直接复用,无需再调 \`read_memory_asset\`\n- 检索结果:\n${items}`;
|
|
59
132
|
}
|
|
60
133
|
export function buildMemoryPlanStep(kind = 'default') {
|
|
61
134
|
if (kind === 'bugfix') {
|
|
@@ -68,7 +141,7 @@ export function buildMemoryPlanStep(kind = 'default') {
|
|
|
68
141
|
type: 'bugfix',
|
|
69
142
|
description: '[现象、报错信息、复现条件]',
|
|
70
143
|
summary: '[检索用:关键词 + 根因 + 修复要点,一句话]',
|
|
71
|
-
content: '【现象】...\n【根因】...\n
|
|
144
|
+
content: '【现象】...\n【根因】...\n【修复】具体改动与关键代码/配置\n【验证】如何确认已修好',
|
|
72
145
|
usage: '[再次遇到何种症状时可参考]',
|
|
73
146
|
tags: ['bugfix', 'root-cause'],
|
|
74
147
|
confidence: 0.85,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unify legacy Qdrant payloads (kind/title/source) with the current asset schema.
|
|
3
|
+
*/
|
|
4
|
+
export declare function normalizeMemoryPayload(payload: Record<string, unknown>): Record<string, unknown>;
|
|
5
|
+
export declare function payloadToMemoryFields(payload: Record<string, unknown>): {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
description: string;
|
|
10
|
+
summary: string;
|
|
11
|
+
content: string;
|
|
12
|
+
tags: string[];
|
|
13
|
+
confidence: number;
|
|
14
|
+
sourceProject?: string;
|
|
15
|
+
sourcePath?: string;
|
|
16
|
+
usage?: string;
|
|
17
|
+
contentHash?: string;
|
|
18
|
+
normalizedContentHash?: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
function truncate(value, maxChars) {
|
|
2
|
+
if (value.length <= maxChars) {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
return `${value.slice(0, Math.max(0, maxChars - 3))}...`;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Unify legacy Qdrant payloads (kind/title/source) with the current asset schema.
|
|
9
|
+
*/
|
|
10
|
+
export function normalizeMemoryPayload(payload) {
|
|
11
|
+
const normalized = { ...payload };
|
|
12
|
+
if (!normalized.name && typeof normalized.title === 'string') {
|
|
13
|
+
normalized.name = normalized.title;
|
|
14
|
+
}
|
|
15
|
+
if (!normalized.type && typeof normalized.kind === 'string') {
|
|
16
|
+
const kind = normalized.kind;
|
|
17
|
+
normalized.type = kind === 'extracted_pattern' ? 'pattern' : kind;
|
|
18
|
+
}
|
|
19
|
+
if (!normalized.description) {
|
|
20
|
+
if (typeof normalized.title === 'string') {
|
|
21
|
+
normalized.description = normalized.title;
|
|
22
|
+
}
|
|
23
|
+
else if (typeof normalized.source === 'string') {
|
|
24
|
+
normalized.description = `Legacy memory from ${normalized.source}`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!normalized.summary && typeof normalized.content === 'string') {
|
|
28
|
+
normalized.summary = truncate(normalized.content, 280);
|
|
29
|
+
}
|
|
30
|
+
if (!normalized.createdAt && typeof normalized.created_at === 'string') {
|
|
31
|
+
normalized.createdAt = normalized.created_at;
|
|
32
|
+
}
|
|
33
|
+
if (!normalized.updatedAt) {
|
|
34
|
+
if (typeof normalized.updated_at === 'string') {
|
|
35
|
+
normalized.updatedAt = normalized.updated_at;
|
|
36
|
+
}
|
|
37
|
+
else if (typeof normalized.createdAt === 'string') {
|
|
38
|
+
normalized.updatedAt = normalized.createdAt;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return normalized;
|
|
42
|
+
}
|
|
43
|
+
export function payloadToMemoryFields(payload) {
|
|
44
|
+
const p = normalizeMemoryPayload(payload);
|
|
45
|
+
const tags = Array.isArray(p.tags)
|
|
46
|
+
? p.tags.filter((item) => typeof item === 'string' && item.trim().length > 0)
|
|
47
|
+
: [];
|
|
48
|
+
return {
|
|
49
|
+
id: String(p.id || ''),
|
|
50
|
+
name: String(p.name || ''),
|
|
51
|
+
type: String(p.type || ''),
|
|
52
|
+
description: String(p.description || ''),
|
|
53
|
+
summary: String(p.summary || ''),
|
|
54
|
+
content: String(p.content || ''),
|
|
55
|
+
tags,
|
|
56
|
+
confidence: typeof p.confidence === 'number' && Number.isFinite(p.confidence) ? p.confidence : 0.5,
|
|
57
|
+
sourceProject: typeof p.sourceProject === 'string' ? p.sourceProject : undefined,
|
|
58
|
+
sourcePath: typeof p.sourcePath === 'string' ? p.sourcePath : undefined,
|
|
59
|
+
usage: typeof p.usage === 'string' ? p.usage : undefined,
|
|
60
|
+
contentHash: typeof p.contentHash === 'string' ? p.contentHash : undefined,
|
|
61
|
+
normalizedContentHash: typeof p.normalizedContentHash === 'string' ? p.normalizedContentHash : undefined,
|
|
62
|
+
createdAt: String(p.createdAt || ''),
|
|
63
|
+
updatedAt: String(p.updatedAt || ''),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 判断是否适合使用 shadcn/ui 实现路径
|
|
3
|
+
*/
|
|
4
|
+
export declare function isShadcnStack(stack?: string): boolean;
|
|
5
|
+
export declare function isShadcnCategory(category?: string): boolean;
|
|
6
|
+
export declare function isThemeCategory(category?: string): boolean;
|
|
7
|
+
export declare function isGuidelineCategory(category?: string): boolean;
|
|
8
|
+
export declare function formatShadcnResult(data: Record<string, any>): string;
|
|
9
|
+
export declare function formatThemeResult(data: Record<string, any>): string;
|
|
10
|
+
export declare function formatGuidelineResult(data: Record<string, any>): string;
|
|
11
|
+
export declare function pickThemeForProductType(themes: Record<string, any>[], productType: string): Record<string, any> | undefined;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 判断是否适合使用 shadcn/ui 实现路径
|
|
3
|
+
*/
|
|
4
|
+
export function isShadcnStack(stack) {
|
|
5
|
+
if (!stack)
|
|
6
|
+
return false;
|
|
7
|
+
const normalized = stack.toLowerCase();
|
|
8
|
+
return (normalized.includes('react') ||
|
|
9
|
+
normalized.includes('next') ||
|
|
10
|
+
normalized.includes('shadcn') ||
|
|
11
|
+
normalized.includes('tailwind'));
|
|
12
|
+
}
|
|
13
|
+
export function isShadcnCategory(category) {
|
|
14
|
+
return Boolean(category?.startsWith('shadcn-'));
|
|
15
|
+
}
|
|
16
|
+
export function isThemeCategory(category) {
|
|
17
|
+
return category === 'ui-themes';
|
|
18
|
+
}
|
|
19
|
+
export function isGuidelineCategory(category) {
|
|
20
|
+
return category === 'ui-guidelines-vercel';
|
|
21
|
+
}
|
|
22
|
+
export function formatShadcnResult(data) {
|
|
23
|
+
const lines = [
|
|
24
|
+
`- **名称**: ${data.name || data.title}`,
|
|
25
|
+
`- **类型**: ${data.type || 'unknown'}`,
|
|
26
|
+
`- **描述**: ${data.description || '—'}`,
|
|
27
|
+
`- **安装**: \`${data.installCommand || `npx shadcn@latest add ${data.name}`}\``,
|
|
28
|
+
];
|
|
29
|
+
if (Array.isArray(data.registryDependencies) && data.registryDependencies.length > 0) {
|
|
30
|
+
lines.push(`- **依赖组件**: ${data.registryDependencies.join(', ')}`);
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(data.files) && data.files.length > 0) {
|
|
33
|
+
lines.push(`- **文件**: ${data.files.slice(0, 5).join(', ')}${data.files.length > 5 ? '…' : ''}`);
|
|
34
|
+
}
|
|
35
|
+
return lines.join('\n');
|
|
36
|
+
}
|
|
37
|
+
export function formatThemeResult(data) {
|
|
38
|
+
const lines = [
|
|
39
|
+
`- **主题**: ${data.title || data.name}`,
|
|
40
|
+
`- **描述**: ${data.description || '—'}`,
|
|
41
|
+
`- **基色**: ${data.baseColor || '—'}`,
|
|
42
|
+
`- **适合**: ${Array.isArray(data.bestFor) ? data.bestFor.join(', ') : '—'}`,
|
|
43
|
+
`- **用法**: 将 \`globalsCssSnippet\` 粘贴到 \`app/globals.css\`(shadcn new-york)`,
|
|
44
|
+
];
|
|
45
|
+
return lines.join('\n');
|
|
46
|
+
}
|
|
47
|
+
export function formatGuidelineResult(data) {
|
|
48
|
+
return [
|
|
49
|
+
`- **级别**: ${data.level || '—'}`,
|
|
50
|
+
`- **章节**: ${data.section || '—'} / ${data.subsection || '—'}`,
|
|
51
|
+
`- **规则**: ${data.rule || data.description || '—'}`,
|
|
52
|
+
`- **来源**: Vercel Web Interface Guidelines`,
|
|
53
|
+
].join('\n');
|
|
54
|
+
}
|
|
55
|
+
export function pickThemeForProductType(themes, productType) {
|
|
56
|
+
if (themes.length === 0)
|
|
57
|
+
return undefined;
|
|
58
|
+
const normalized = productType.toLowerCase();
|
|
59
|
+
const scored = themes.map((theme) => {
|
|
60
|
+
const bestFor = Array.isArray(theme.bestFor) ? theme.bestFor : [];
|
|
61
|
+
const score = bestFor.reduce((acc, item) => {
|
|
62
|
+
const token = item.toLowerCase();
|
|
63
|
+
if (normalized.includes(token) || token.includes(normalized)) {
|
|
64
|
+
return acc + 2;
|
|
65
|
+
}
|
|
66
|
+
if (normalized.split(/\s+/).some((part) => token.includes(part) && part.length > 2)) {
|
|
67
|
+
return acc + 1;
|
|
68
|
+
}
|
|
69
|
+
return acc;
|
|
70
|
+
}, 0);
|
|
71
|
+
return { theme, score };
|
|
72
|
+
});
|
|
73
|
+
scored.sort((a, b) => b.score - a.score);
|
|
74
|
+
if (scored[0]?.score > 0) {
|
|
75
|
+
return scored[0].theme;
|
|
76
|
+
}
|
|
77
|
+
return themes.find((theme) => theme.name === 'zinc-neutral') || themes[0];
|
|
78
|
+
}
|