@minus-ai/create-skill 0.1.0-beta.1
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/index.mjs +658 -0
- package/package.json +12 -0
- package/templates/README.md.tpl +123 -0
- package/templates/asin/en-US.json.tpl +7 -0
- package/templates/asin/main.tsx.tpl +95 -0
- package/templates/asin/pipeline.py.tpl +11 -0
- package/templates/asin/zh-CN.json.tpl +7 -0
- package/templates/custom/en-US.json.tpl +8 -0
- package/templates/custom/main.tsx.tpl +93 -0
- package/templates/custom/pipeline.py.tpl +9 -0
- package/templates/custom/zh-CN.json.tpl +8 -0
- package/templates/default/en-US.json.tpl +4 -0
- package/templates/default/main.tsx.tpl +109 -0
- package/templates/default/pipeline.py.tpl +8 -0
- package/templates/default/zh-CN.json.tpl +4 -0
- package/templates/embed.html.tpl +13 -0
- package/templates/en-US.json.tpl +10 -0
- package/templates/env.example.tpl +3 -0
- package/templates/file/en-US.json.tpl +10 -0
- package/templates/file/main.tsx.tpl +104 -0
- package/templates/file/pipeline.py.tpl +11 -0
- package/templates/file/zh-CN.json.tpl +10 -0
- package/templates/frontend-package.json.tpl +22 -0
- package/templates/gitignore.tpl +10 -0
- package/templates/keyword/en-US.json.tpl +7 -0
- package/templates/keyword/main.tsx.tpl +95 -0
- package/templates/keyword/pipeline.py.tpl +11 -0
- package/templates/keyword/zh-CN.json.tpl +7 -0
- package/templates/main.tsx.tpl +193 -0
- package/templates/pipeline.py.tpl +12 -0
- package/templates/pnpm-workspace.yaml.tpl +2 -0
- package/templates/pyproject.toml.tpl +13 -0
- package/templates/root-package.json.tpl +18 -0
- package/templates/server.py.tpl +4 -0
- package/templates/tsconfig.json.tpl +15 -0
- package/templates/vite.config.ts.tpl +23 -0
- package/templates/zh-CN.json.tpl +10 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@minus-skill/{{skillId}}-frontend",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc -b && vite build",
|
|
8
|
+
"dev": "vite build --watch"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"sonner": "^2.0.7"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@minus-ai/dev-vite-plugin": "^0.1.0-beta.1",
|
|
15
|
+
"@types/node": "^20.14.0",
|
|
16
|
+
"@types/react": "^18.3.3",
|
|
17
|
+
"@types/react-dom": "^18.3.0",
|
|
18
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
19
|
+
"typescript": "^5.5.0",
|
|
20
|
+
"vite": "^5.4.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { StrictMode, useMemo, useState } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { FlowApp, I18nProvider, mergeMessages, useT, type StepConfig } from '@minus/widget-framework';
|
|
4
|
+
import { AmazonSearchBar, CountrySelect, SearchSubmitButton, validateKeywords, platformWidgetMessages } from '@minus/platform-widgets';
|
|
5
|
+
import { Toaster } from 'sonner';
|
|
6
|
+
import zhCN from './locales/zh-CN.json';
|
|
7
|
+
import enUS from './locales/en-US.json';
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* 防御性编码提示(适用于所有 step render 函数):
|
|
11
|
+
* - 后端 data 不可信,所有字段都要 ?? 兜底
|
|
12
|
+
* - 数组:(data.list as T[] | undefined) ?? []
|
|
13
|
+
* - 字符串:(data.name as string | undefined) ?? ''
|
|
14
|
+
* - 数字:Number(data.count) || 0
|
|
15
|
+
* - 嵌套:(data.obj as Record<string, unknown> | undefined)?.field ?? fallback
|
|
16
|
+
* 框架已有 Error Boundary 兜底,但不应依赖——优先在数据层做防御。
|
|
17
|
+
*/
|
|
18
|
+
function buildSteps(t: (k: string, fb?: string) => string): StepConfig[] {
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
render: ({ data }) => (
|
|
22
|
+
<div className="minus-default-step-done">
|
|
23
|
+
{(data.text as string) ?? t('{{namespace}}.step.empty', '(空)')}
|
|
24
|
+
</div>
|
|
25
|
+
),
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function Home({ title, description, useCases, tags, onStart }: { title: string; description?: string; useCases?: string[]; tags?: Array<{ code: string; label: string }>; onStart: (input: Record<string, unknown>) => Promise<void> }) {
|
|
31
|
+
const t = useT();
|
|
32
|
+
const [value, setValue] = useState('');
|
|
33
|
+
const [country, setCountry] = useState('US');
|
|
34
|
+
const [error, setError] = useState<string | null>(null);
|
|
35
|
+
const [loading, setLoading] = useState(false);
|
|
36
|
+
|
|
37
|
+
async function handleSubmit() {
|
|
38
|
+
if (loading) return;
|
|
39
|
+
const { keywords, error: kwError } = validateKeywords(value);
|
|
40
|
+
if (kwError) { setError(t(kwError.key, kwError.fallback, kwError.vars)); return; }
|
|
41
|
+
setError(null);
|
|
42
|
+
setLoading(true);
|
|
43
|
+
try { await onStart({ keywords: keywords.join(','), country }); } finally { setLoading(false); }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<h1>{title}</h1>
|
|
49
|
+
{description && <p className="minus-skill-hero__subtitle">{description}</p>}
|
|
50
|
+
{((useCases && useCases.length > 0) || (tags && tags.length > 0)) && (
|
|
51
|
+
<ul className="minus-skill-hero__use-cases">
|
|
52
|
+
{useCases?.map((uc, i) => (
|
|
53
|
+
<li key={`uc-${i}`} className="minus-skill-hero__use-case-chip">{uc}</li>
|
|
54
|
+
))}
|
|
55
|
+
{tags?.map((tag) => (
|
|
56
|
+
<li key={`tag-${tag.code}`} className="minus-skill-hero__use-case-chip">{tag.label}</li>
|
|
57
|
+
))}
|
|
58
|
+
</ul>
|
|
59
|
+
)}
|
|
60
|
+
<AmazonSearchBar
|
|
61
|
+
onSubmit={handleSubmit}
|
|
62
|
+
country={<CountrySelect onChange={setCountry} />}
|
|
63
|
+
input={<textarea value={value} onChange={(e) => { setValue(e.target.value); if (error) setError(null); }} onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSubmit(); } }} placeholder={t('{{namespace}}.home.placeholder', '输入关键词,多个关键词用逗号或换行分隔')} spellCheck={false} disabled={loading} rows={1} />}
|
|
64
|
+
submit={<SearchSubmitButton />}
|
|
65
|
+
error={error}
|
|
66
|
+
/>
|
|
67
|
+
</>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const rootEl = document.getElementById('root');
|
|
72
|
+
if (!rootEl) throw new Error('#root not found');
|
|
73
|
+
|
|
74
|
+
const skillMessages = mergeMessages(platformWidgetMessages, { 'zh-CN': zhCN as Record<string, string>, 'en-US': enUS as Record<string, string> });
|
|
75
|
+
|
|
76
|
+
function SkillRoot() {
|
|
77
|
+
const t = useT();
|
|
78
|
+
const steps = useMemo(() => buildSteps(t), [t]);
|
|
79
|
+
return (
|
|
80
|
+
<FlowApp
|
|
81
|
+
steps={steps}
|
|
82
|
+
renderHome={({ flow, onStart }) => <Home title={flow.title} description={flow.description} useCases={flow.useCases} tags={flow.tags} onStart={onStart} />}
|
|
83
|
+
renderCompletion={() => null}
|
|
84
|
+
renderHistoryItem={(entry) => {
|
|
85
|
+
const inp = entry.entryParams as Record<string, string> | undefined;
|
|
86
|
+
return {
|
|
87
|
+
label: inp?.keywords ?? '—',
|
|
88
|
+
meta: inp?.country || undefined,
|
|
89
|
+
};
|
|
90
|
+
}}
|
|
91
|
+
/>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
createRoot(rootEl).render(<StrictMode><I18nProvider messages={skillMessages}><SkillRoot /><Toaster /></I18nProvider></StrictMode>);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from minus_ai_sdk import Pipeline, PipelineContext, StepOutcome
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class {{className}}(Pipeline):
|
|
5
|
+
|
|
6
|
+
async def step_1(self, ctx: PipelineContext) -> StepOutcome:
|
|
7
|
+
keywords: str = ctx.entry_params.get("keywords", "")
|
|
8
|
+
country: str = ctx.entry_params.get("country", "US")
|
|
9
|
+
|
|
10
|
+
result = f"Keywords: {keywords} ({country})"
|
|
11
|
+
return StepOutcome.complete(payload={"text": result})
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { StrictMode, useMemo, useState } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import { FlowApp, I18nProvider, mergeMessages, useT, type StepConfig, type StepRecord } from '@minus/widget-framework';
|
|
4
|
+
import { AmazonSearchBar, CompletionPanel, CountrySelect, SearchSubmitButton, validateAsins, validateKeywords, platformWidgetMessages } from '@minus/platform-widgets';
|
|
5
|
+
import { Toaster } from 'sonner';
|
|
6
|
+
import zhCN from './locales/zh-CN.json';
|
|
7
|
+
import enUS from './locales/en-US.json';
|
|
8
|
+
|
|
9
|
+
type InputType = 'keyword' | 'asin';
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* 防御性编码提示(适用于所有 step render 函数):
|
|
13
|
+
* - 后端 data 不可信,所有字段都要 ?? 兜底
|
|
14
|
+
* - 数组:(data.list as T[] | undefined) ?? []
|
|
15
|
+
* - 字符串:(data.name as string | undefined) ?? ''
|
|
16
|
+
* - 数字:Number(data.count) || 0
|
|
17
|
+
* - 嵌套:(data.obj as Record<string, unknown> | undefined)?.field ?? fallback
|
|
18
|
+
* 框架已有 Error Boundary 兜底,但不应依赖——优先在数据层做防御。
|
|
19
|
+
*/
|
|
20
|
+
function buildSteps(t: (k: string, fb?: string) => string): StepConfig[] {
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
render: ({ data }) => (
|
|
24
|
+
<div className="minus-default-step-done">
|
|
25
|
+
{(data.text as string) ?? t('{{namespace}}.step.empty', '(空)')}
|
|
26
|
+
</div>
|
|
27
|
+
),
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function Home({
|
|
33
|
+
title,
|
|
34
|
+
description,
|
|
35
|
+
useCases,
|
|
36
|
+
tags,
|
|
37
|
+
onStart,
|
|
38
|
+
}: {
|
|
39
|
+
title: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
useCases?: string[];
|
|
42
|
+
tags?: Array<{ code: string; label: string }>;
|
|
43
|
+
onStart: (input: Record<string, unknown>) => Promise<void>;
|
|
44
|
+
}) {
|
|
45
|
+
const t = useT();
|
|
46
|
+
const [inputType, setInputType] = useState<InputType>('keyword');
|
|
47
|
+
const [value, setValue] = useState('');
|
|
48
|
+
const [country, setCountry] = useState('US');
|
|
49
|
+
const [error, setError] = useState<string | null>(null);
|
|
50
|
+
const [loading, setLoading] = useState(false);
|
|
51
|
+
|
|
52
|
+
async function handleSubmit() {
|
|
53
|
+
if (loading) return;
|
|
54
|
+
|
|
55
|
+
if (inputType === 'asin') {
|
|
56
|
+
const { asins, error: asinError } = validateAsins(value);
|
|
57
|
+
if (asinError) {
|
|
58
|
+
setError(t(asinError.key, asinError.fallback, asinError.vars));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
setError(null);
|
|
62
|
+
setLoading(true);
|
|
63
|
+
try {
|
|
64
|
+
await onStart({ inputType, value: asins.join(','), country });
|
|
65
|
+
} finally {
|
|
66
|
+
setLoading(false);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
const { keywords, error: kwError } = validateKeywords(value);
|
|
70
|
+
if (kwError) {
|
|
71
|
+
setError(t(kwError.key, kwError.fallback, kwError.vars));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
setError(null);
|
|
75
|
+
setLoading(true);
|
|
76
|
+
try {
|
|
77
|
+
await onStart({ inputType, value: keywords.join(','), country });
|
|
78
|
+
} finally {
|
|
79
|
+
setLoading(false);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const placeholder = inputType === 'keyword'
|
|
85
|
+
? t('{{namespace}}.home.placeholderKeyword', '输入关键词,如 bluetooth earbuds')
|
|
86
|
+
: t('{{namespace}}.home.placeholderAsin', '输入一个或多个ASIN');
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
<h1>{title}</h1>
|
|
91
|
+
{description && <p className="minus-skill-hero__subtitle">{description}</p>}
|
|
92
|
+
{((useCases && useCases.length > 0) || (tags && tags.length > 0)) && (
|
|
93
|
+
<ul className="minus-skill-hero__use-cases">
|
|
94
|
+
{useCases?.map((uc, i) => (
|
|
95
|
+
<li key={`uc-${i}`} className="minus-skill-hero__use-case-chip">{uc}</li>
|
|
96
|
+
))}
|
|
97
|
+
{tags?.map((tag) => (
|
|
98
|
+
<li key={`tag-${tag.code}`} className="minus-skill-hero__use-case-chip">{tag.label}</li>
|
|
99
|
+
))}
|
|
100
|
+
</ul>
|
|
101
|
+
)}
|
|
102
|
+
|
|
103
|
+
<div style={{ display: 'flex', gap: 0, marginBottom: 16 }}>
|
|
104
|
+
{(['keyword', 'asin'] as const).map((type) => (
|
|
105
|
+
<button
|
|
106
|
+
key={type}
|
|
107
|
+
onClick={() => { setInputType(type); setValue(''); setError(null); }}
|
|
108
|
+
style={{
|
|
109
|
+
padding: '6px 16px',
|
|
110
|
+
fontSize: 14,
|
|
111
|
+
border: '1px solid #d1d5db',
|
|
112
|
+
cursor: 'pointer',
|
|
113
|
+
background: inputType === type ? '#111827' : '#fff',
|
|
114
|
+
color: inputType === type ? '#fff' : '#374151',
|
|
115
|
+
borderRadius: type === 'keyword' ? '6px 0 0 6px' : '0 6px 6px 0',
|
|
116
|
+
marginLeft: type === 'asin' ? -1 : 0,
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
{t(`{{namespace}}.home.tab.${type}`, type === 'keyword' ? '关键词' : 'ASIN')}
|
|
120
|
+
</button>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<AmazonSearchBar
|
|
125
|
+
onSubmit={handleSubmit}
|
|
126
|
+
country={<CountrySelect onChange={setCountry} />}
|
|
127
|
+
input={
|
|
128
|
+
<input
|
|
129
|
+
type="text"
|
|
130
|
+
value={value}
|
|
131
|
+
onChange={(e) => { setValue(e.target.value); if (error) setError(null); }}
|
|
132
|
+
placeholder={placeholder}
|
|
133
|
+
spellCheck={false}
|
|
134
|
+
disabled={loading}
|
|
135
|
+
/>
|
|
136
|
+
}
|
|
137
|
+
submit={<SearchSubmitButton />}
|
|
138
|
+
error={error}
|
|
139
|
+
/>
|
|
140
|
+
</>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const rootEl = document.getElementById('root');
|
|
145
|
+
if (!rootEl) throw new Error('#root not found');
|
|
146
|
+
|
|
147
|
+
const skillMessages = mergeMessages(
|
|
148
|
+
platformWidgetMessages,
|
|
149
|
+
{ 'zh-CN': zhCN as Record<string, string>, 'en-US': enUS as Record<string, string> },
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
function SkillRoot() {
|
|
153
|
+
const t = useT();
|
|
154
|
+
const steps = useMemo(() => buildSteps(t), [t]);
|
|
155
|
+
return (
|
|
156
|
+
<FlowApp
|
|
157
|
+
steps={steps}
|
|
158
|
+
renderHome={({ flow, onStart }) => <Home title={flow.title} description={flow.description} useCases={flow.useCases} tags={flow.tags} onStart={onStart} />}
|
|
159
|
+
renderCompletion={({ stepHistory }) => {
|
|
160
|
+
const last = stepHistory.reduce<StepRecord | undefined>(
|
|
161
|
+
(acc, r) => (acc && acc.stepNumber > r.stepNumber ? acc : r),
|
|
162
|
+
undefined,
|
|
163
|
+
);
|
|
164
|
+
const data = last?.data ?? {};
|
|
165
|
+
return (
|
|
166
|
+
<CompletionPanel
|
|
167
|
+
title={data.title as string | undefined}
|
|
168
|
+
summary={data.summary as string | undefined}
|
|
169
|
+
filename={data.filename as string | undefined}
|
|
170
|
+
fileId={data.fileId as string | undefined}
|
|
171
|
+
sizeBytes={data.sizeBytes as number | undefined}
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
}}
|
|
175
|
+
renderHistoryItem={(entry) => {
|
|
176
|
+
const inp = entry.entryParams as Record<string, string> | undefined;
|
|
177
|
+
return {
|
|
178
|
+
label: inp?.value ?? '—',
|
|
179
|
+
meta: inp?.country || undefined,
|
|
180
|
+
};
|
|
181
|
+
}}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
createRoot(rootEl).render(
|
|
187
|
+
<StrictMode>
|
|
188
|
+
<I18nProvider messages={skillMessages}>
|
|
189
|
+
<SkillRoot />
|
|
190
|
+
<Toaster />
|
|
191
|
+
</I18nProvider>
|
|
192
|
+
</StrictMode>,
|
|
193
|
+
);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from minus_ai_sdk import Pipeline, PipelineContext, StepOutcome
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class {{className}}(Pipeline):
|
|
5
|
+
|
|
6
|
+
async def step_1(self, ctx: PipelineContext) -> StepOutcome:
|
|
7
|
+
input_type: str = ctx.entry_params.get("inputType", "keyword")
|
|
8
|
+
value: str = ctx.entry_params.get("value", "")
|
|
9
|
+
country: str = ctx.entry_params.get("country", "US")
|
|
10
|
+
|
|
11
|
+
result = f"[{input_type.upper()}] {value} ({country})"
|
|
12
|
+
return StepOutcome.complete(payload={"text": result})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "minus-skill-{{skillId}}"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "{{description}}"
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"minus-ai-sdk-python @ https://minus-ai-dev.oss-cn-shenzhen.aliyuncs.com/sdks/python/minus_ai_sdk_python-0.1.0-py3-none-any.whl",
|
|
8
|
+
"python-dotenv",
|
|
9
|
+
"uvicorn[standard]",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
[tool.setuptools]
|
|
13
|
+
py-modules = ["pipeline", "server"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "minus-skill-{{skillId}}",
|
|
3
|
+
"description": "{{description}}",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"workspaces": ["frontend"],
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "minus-dev-cleanup && mkdir -p .minus && echo $$ > .minus/dev.pid && concurrently -n skill,fe \".venv/bin/uvicorn server:app --port {{port}} --reload --reload-include '*.py' --reload-include '.env.local' --env-file .env.local\" \"cd frontend && pnpm exec vite\"",
|
|
9
|
+
"build": "cd frontend && pnpm run build"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@minus-ai/dev-vite-plugin": "^0.1.0-beta.1",
|
|
13
|
+
"concurrently": "^9.1.2"
|
|
14
|
+
},
|
|
15
|
+
"pnpm": {
|
|
16
|
+
"onlyBuiltDependencies": ["esbuild"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"noEmit": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { minusDev } from '@minus-ai/dev-vite-plugin';
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react(), ...minusDev({ localBackend: 'http://localhost:{{port}}' })],
|
|
8
|
+
build: {
|
|
9
|
+
target: 'esnext',
|
|
10
|
+
outDir: path.resolve(__dirname, '../static'),
|
|
11
|
+
emptyOutDir: true,
|
|
12
|
+
rollupOptions: {
|
|
13
|
+
input: {
|
|
14
|
+
embed: path.resolve(__dirname, 'embed.html'),
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
base: './',
|
|
19
|
+
server: {
|
|
20
|
+
host: true,
|
|
21
|
+
open: false,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"{{namespace}}.step.title": "结果",
|
|
3
|
+
"{{namespace}}.step.empty": "(空)",
|
|
4
|
+
"{{namespace}}.home.tab.keyword": "关键词",
|
|
5
|
+
"{{namespace}}.home.tab.asin": "ASIN",
|
|
6
|
+
"{{namespace}}.home.placeholderKeyword": "输入关键词,如 bluetooth earbuds",
|
|
7
|
+
"{{namespace}}.home.placeholderAsin": "输入一个或多个ASIN",
|
|
8
|
+
"{{namespace}}.home.processing": "处理中…",
|
|
9
|
+
"{{namespace}}.home.send": "开始"
|
|
10
|
+
}
|