move to vite
Browse files- .gitignore +3 -1
- README.md +1 -1
- index.html +19 -0
- package.json +13 -7
- pnpm-lock.yaml +0 -0
- postcss.config.mjs +5 -0
- public/index.html +0 -40
- src/App.css +0 -38
- src/App.tsx +1 -1
- src/Header.tsx +1 -1
- src/components/CodeExporter.tsx +1 -1
- src/components/MarkdownRenderer.tsx +1 -1
- src/components/Modal.tsx +1 -1
- src/components/ModelInfo.tsx +10 -10
- src/components/ModelLoader.tsx +2 -2
- src/components/ModelSelector.tsx +11 -11
- src/components/PipelineSelector.tsx +2 -2
- src/components/Sidebar.tsx +1 -1
- src/components/Tooltip.tsx +1 -1
- src/components/pipelines/FeatureExtraction.tsx +1 -1
- src/components/pipelines/FeatureExtractionConfig.tsx +2 -2
- src/components/pipelines/ImageClassification.tsx +1 -1
- src/components/pipelines/TextClassification.tsx +9 -9
- src/components/pipelines/TextGeneration.tsx +3 -3
- src/components/pipelines/TextGenerationConfig.tsx +1 -1
- src/components/pipelines/ZeroShotClassification.tsx +2 -2
- src/components/pipelines/ZeroShotClassificationConfig.tsx +5 -5
- src/index.css +19 -3
- src/lib/workerManager.ts +5 -5
- tailwind.config.js +0 -8
- tsconfig.json +7 -6
- vite-env.d.ts +1 -0
- vite.config.ts +9 -0
.gitignore
CHANGED
@@ -22,4 +22,6 @@ npm-debug.log*
|
|
22 |
yarn-debug.log*
|
23 |
yarn-error.log*
|
24 |
|
25 |
-
.env
|
|
|
|
|
|
22 |
yarn-debug.log*
|
23 |
yarn-error.log*
|
24 |
|
25 |
+
.env
|
26 |
+
|
27 |
+
dist/
|
README.md
CHANGED
@@ -6,7 +6,7 @@ colorTo: red
|
|
6 |
sdk: static
|
7 |
pinned: false
|
8 |
app_build_command: npm run build
|
9 |
-
app_file:
|
10 |
---
|
11 |
|
12 |
|
|
|
6 |
sdk: static
|
7 |
pinned: false
|
8 |
app_build_command: npm run build
|
9 |
+
app_file: dist/index.html
|
10 |
---
|
11 |
|
12 |
|
index.html
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!doctype html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="utf-8" />
|
5 |
+
<link rel="icon" href="/favicon.ico" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7 |
+
<meta name="theme-color" content="#000000" />
|
8 |
+
<meta name="description" content="" />
|
9 |
+
<link rel="apple-touch-icon" href="/logo192.png" />
|
10 |
+
<link rel="manifest" href="/manifest.json" />
|
11 |
+
|
12 |
+
<title>Transformers.js Playground</title>
|
13 |
+
</head>
|
14 |
+
<body>
|
15 |
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
16 |
+
<div id="root"></div>
|
17 |
+
<script type="module" src="/src/index.tsx"></script>
|
18 |
+
</body>
|
19 |
+
</html>
|
package.json
CHANGED
@@ -1,11 +1,13 @@
|
|
1 |
{
|
|
|
2 |
"name": "transformers-js-playground",
|
3 |
"version": "0.1.0",
|
4 |
"private": true,
|
5 |
"dependencies": {
|
6 |
"@headlessui/react": "^2.2.4",
|
7 |
-
"@
|
8 |
"@tailwindcss/typography": "^0.5.16",
|
|
|
9 |
"@testing-library/dom": "^10.4.0",
|
10 |
"@testing-library/jest-dom": "^6.6.3",
|
11 |
"@testing-library/react": "^16.3.0",
|
@@ -14,25 +16,27 @@
|
|
14 |
"@types/node": "^16.18.126",
|
15 |
"@types/react": "^19.1.8",
|
16 |
"@types/react-dom": "^19.1.6",
|
|
|
17 |
"build": "^0.1.4",
|
18 |
"dotenv": "^17.0.1",
|
19 |
"lodash": "^4.17.21",
|
20 |
"lucide-react": "^0.525.0",
|
21 |
"path-browserify": "^1.0.1",
|
|
|
22 |
"react": "^19.1.0",
|
23 |
"react-dom": "^19.1.0",
|
24 |
"react-markdown": "^10.1.0",
|
25 |
-
"react-scripts": "5.0.1",
|
26 |
"react-syntax-highlighter": "^15.6.1",
|
27 |
"remark-gfm": "^4.0.1",
|
28 |
"typescript": "^4.9.5",
|
|
|
|
|
29 |
"web-vitals": "^2.1.4"
|
30 |
},
|
31 |
"scripts": {
|
32 |
-
"start": "
|
33 |
-
"build": "
|
34 |
-
"
|
35 |
-
"eject": "react-scripts eject"
|
36 |
},
|
37 |
"eslintConfig": {
|
38 |
"extends": [
|
@@ -55,6 +59,8 @@
|
|
55 |
"devDependencies": {
|
56 |
"@types/lodash": "^4.17.20",
|
57 |
"@types/react-syntax-highlighter": "^15.5.13",
|
58 |
-
"
|
|
|
|
|
59 |
}
|
60 |
}
|
|
|
1 |
{
|
2 |
+
"type": "module",
|
3 |
"name": "transformers-js-playground",
|
4 |
"version": "0.1.0",
|
5 |
"private": true,
|
6 |
"dependencies": {
|
7 |
"@headlessui/react": "^2.2.4",
|
8 |
+
"@tailwindcss/postcss": "^4.1.11",
|
9 |
"@tailwindcss/typography": "^0.5.16",
|
10 |
+
"@tailwindcss/vite": "^4.1.11",
|
11 |
"@testing-library/dom": "^10.4.0",
|
12 |
"@testing-library/jest-dom": "^6.6.3",
|
13 |
"@testing-library/react": "^16.3.0",
|
|
|
16 |
"@types/node": "^16.18.126",
|
17 |
"@types/react": "^19.1.8",
|
18 |
"@types/react-dom": "^19.1.6",
|
19 |
+
"@vitejs/plugin-react-swc": "^3.11.0",
|
20 |
"build": "^0.1.4",
|
21 |
"dotenv": "^17.0.1",
|
22 |
"lodash": "^4.17.21",
|
23 |
"lucide-react": "^0.525.0",
|
24 |
"path-browserify": "^1.0.1",
|
25 |
+
"postcss": "^8.5.6",
|
26 |
"react": "^19.1.0",
|
27 |
"react-dom": "^19.1.0",
|
28 |
"react-markdown": "^10.1.0",
|
|
|
29 |
"react-syntax-highlighter": "^15.6.1",
|
30 |
"remark-gfm": "^4.0.1",
|
31 |
"typescript": "^4.9.5",
|
32 |
+
"vite-plugin-svgr": "^4.3.0",
|
33 |
+
"vite-tsconfig-paths": "^5.1.4",
|
34 |
"web-vitals": "^2.1.4"
|
35 |
},
|
36 |
"scripts": {
|
37 |
+
"start": "vite",
|
38 |
+
"build": "tsc && vite build",
|
39 |
+
"serve": "vite preview"
|
|
|
40 |
},
|
41 |
"eslintConfig": {
|
42 |
"extends": [
|
|
|
59 |
"devDependencies": {
|
60 |
"@types/lodash": "^4.17.20",
|
61 |
"@types/react-syntax-highlighter": "^15.5.13",
|
62 |
+
"@vitejs/plugin-react": "^4.7.0",
|
63 |
+
"tailwindcss": "^4.1.11",
|
64 |
+
"vite": "^7.0.6"
|
65 |
}
|
66 |
}
|
pnpm-lock.yaml
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
postcss.config.mjs
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
plugins: {
|
3 |
+
'@tailwindcss/postcss': {}
|
4 |
+
}
|
5 |
+
}
|
public/index.html
DELETED
@@ -1,40 +0,0 @@
|
|
1 |
-
<!doctype html>
|
2 |
-
<html lang="en">
|
3 |
-
<head>
|
4 |
-
<meta charset="utf-8" />
|
5 |
-
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
6 |
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7 |
-
<meta name="theme-color" content="#000000" />
|
8 |
-
<meta name="description" content="" />
|
9 |
-
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
10 |
-
<!--
|
11 |
-
manifest.json provides metadata used when your web app is installed on a
|
12 |
-
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
13 |
-
-->
|
14 |
-
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
15 |
-
<!--
|
16 |
-
Notice the use of %PUBLIC_URL% in the tags above.
|
17 |
-
It will be replaced with the URL of the `public` folder during the build.
|
18 |
-
Only files inside the `public` folder can be referenced from the HTML.
|
19 |
-
|
20 |
-
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
21 |
-
work correctly both with client-side routing and a non-root public URL.
|
22 |
-
Learn how to configure a non-root public URL by running `npm run build`.
|
23 |
-
-->
|
24 |
-
<title>Transformers.js Playground</title>
|
25 |
-
</head>
|
26 |
-
<body>
|
27 |
-
<noscript>You need to enable JavaScript to run this app.</noscript>
|
28 |
-
<div id="root"></div>
|
29 |
-
<!--
|
30 |
-
This HTML file is a template.
|
31 |
-
If you open it directly in the browser, you will see an empty page.
|
32 |
-
|
33 |
-
You can add webfonts, meta tags, or analytics to this file.
|
34 |
-
The build step will place the bundled scripts into the <body> tag.
|
35 |
-
|
36 |
-
To begin the development, run `npm start` or `yarn start`.
|
37 |
-
To create a production bundle, use `npm run build` or `yarn build`.
|
38 |
-
-->
|
39 |
-
</body>
|
40 |
-
</html>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/App.css
DELETED
@@ -1,38 +0,0 @@
|
|
1 |
-
.App {
|
2 |
-
text-align: center;
|
3 |
-
}
|
4 |
-
|
5 |
-
.App-logo {
|
6 |
-
height: 40vmin;
|
7 |
-
pointer-events: none;
|
8 |
-
}
|
9 |
-
|
10 |
-
@media (prefers-reduced-motion: no-preference) {
|
11 |
-
.App-logo {
|
12 |
-
animation: App-logo-spin infinite 20s linear;
|
13 |
-
}
|
14 |
-
}
|
15 |
-
|
16 |
-
.App-header {
|
17 |
-
background-color: #282c34;
|
18 |
-
min-height: 100vh;
|
19 |
-
display: flex;
|
20 |
-
flex-direction: column;
|
21 |
-
align-items: center;
|
22 |
-
justify-content: center;
|
23 |
-
font-size: calc(10px + 2vmin);
|
24 |
-
color: white;
|
25 |
-
}
|
26 |
-
|
27 |
-
.App-link {
|
28 |
-
color: #61dafb;
|
29 |
-
}
|
30 |
-
|
31 |
-
@keyframes App-logo-spin {
|
32 |
-
from {
|
33 |
-
transform: rotate(0deg);
|
34 |
-
}
|
35 |
-
to {
|
36 |
-
transform: rotate(360deg);
|
37 |
-
}
|
38 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/App.tsx
CHANGED
@@ -48,7 +48,7 @@ function App() {
|
|
48 |
<div className="absolute right-0 top-16 lg:hidden mb-4">
|
49 |
<button
|
50 |
onClick={() => setIsSidebarOpen(true)}
|
51 |
-
className="items-center px-2 py-2 bg-white border border-gray-300 rounded-l-lg shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50"
|
52 |
>
|
53 |
<Settings className="w-5 h-5" />
|
54 |
</button>
|
|
|
48 |
<div className="absolute right-0 top-16 lg:hidden mb-4">
|
49 |
<button
|
50 |
onClick={() => setIsSidebarOpen(true)}
|
51 |
+
className="flex items-center px-2 py-2 bg-white border border-gray-300 rounded-l-lg shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50"
|
52 |
>
|
53 |
<Settings className="w-5 h-5" />
|
54 |
</button>
|
src/Header.tsx
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
function Header() {
|
2 |
return (
|
3 |
-
<header className="bg-white shadow-
|
4 |
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
5 |
<div className="flex items-center justify-center h-16">
|
6 |
<div className="flex items-center space-x-3">
|
|
|
1 |
function Header() {
|
2 |
return (
|
3 |
+
<header className="bg-white shadow-xs border-b">
|
4 |
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
5 |
<div className="flex items-center justify-center h-16">
|
6 |
<div className="flex items-center space-x-3">
|
src/components/CodeExporter.tsx
CHANGED
@@ -8,7 +8,7 @@ const CodeExporter: React.FC<CodeExporterProps> = ({ code }) => {
|
|
8 |
return (
|
9 |
<div className="flex flex-col">
|
10 |
<h2 className="text-lg font-medium">Code</h2>
|
11 |
-
<pre className="bg-gray-100 p-2 rounded">{code}</pre>
|
12 |
</div>
|
13 |
);
|
14 |
};
|
|
|
8 |
return (
|
9 |
<div className="flex flex-col">
|
10 |
<h2 className="text-lg font-medium">Code</h2>
|
11 |
+
<pre className="bg-gray-100 p-2 rounded-sm">{code}</pre>
|
12 |
</div>
|
13 |
);
|
14 |
};
|
src/components/MarkdownRenderer.tsx
CHANGED
@@ -33,7 +33,7 @@ const MarkdownRenderer = ({ content }: MarkdownRendererProps) => {
|
|
33 |
</SyntaxHighlighter>
|
34 |
) : (
|
35 |
<code
|
36 |
-
className="bg-gray-100 px-1 py-0.5 rounded text-sm font-mono"
|
37 |
{...props}
|
38 |
>
|
39 |
{children}
|
|
|
33 |
</SyntaxHighlighter>
|
34 |
) : (
|
35 |
<code
|
36 |
+
className="bg-gray-100 px-1 py-0.5 rounded-sm text-sm font-mono"
|
37 |
{...props}
|
38 |
>
|
39 |
{children}
|
src/components/Modal.tsx
CHANGED
@@ -78,7 +78,7 @@ const Modal: React.FC<ModalProps> = ({
|
|
78 |
<h3 className="text-lg font-semibold text-gray-900">{title}</h3>
|
79 |
<button
|
80 |
onClick={onClose}
|
81 |
-
className="rounded-md p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 focus:outline-
|
82 |
>
|
83 |
<span className="sr-only">Close</span>
|
84 |
<X className="h-5 w-5" />
|
|
|
78 |
<h3 className="text-lg font-semibold text-gray-900">{title}</h3>
|
79 |
<button
|
80 |
onClick={onClose}
|
81 |
+
className="rounded-md p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 focus:outline-hidden focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
82 |
>
|
83 |
<span className="sr-only">Close</span>
|
84 |
<X className="h-5 w-5" />
|
src/components/ModelInfo.tsx
CHANGED
@@ -28,37 +28,37 @@ const ModelInfo = () => {
|
|
28 |
const { models, modelInfo, selectedQuantization, isFetching } = useModel()
|
29 |
|
30 |
const ModelInfoSkeleton = () => (
|
31 |
-
<div className="bg-
|
32 |
<div className="flex items-center space-x-2">
|
33 |
<Bot className="w-4 h-4 text-blue-300" />
|
34 |
-
<div className="h-4 bg-gray-300 rounded flex-1"></div>
|
35 |
<div className="w-4 h-4 bg-gray-300 rounded-full"></div>
|
36 |
</div>
|
37 |
|
38 |
<div className="flex items-center space-x-2 ml-6">
|
39 |
-
<div className="h-3 bg-gray-200 rounded w-32"></div>
|
40 |
</div>
|
41 |
|
42 |
<div className="grid grid-cols-2 gap-2 text-xs">
|
43 |
<div className="flex items-center space-x-1">
|
44 |
<Heart className="w-3 h-3 text-red-300" />
|
45 |
-
<div className="h-3 bg-gray-200 rounded w-8"></div>
|
46 |
</div>
|
47 |
<div className="flex items-center space-x-1">
|
48 |
<Download className="w-3 h-3 text-green-300" />
|
49 |
-
<div className="h-3 bg-gray-200 rounded w-8"></div>
|
50 |
</div>
|
51 |
<div className="flex items-center space-x-1">
|
52 |
<Cpu className="w-3 h-3 text-purple-300" />
|
53 |
-
<div className="h-3 bg-gray-200 rounded w-8"></div>
|
54 |
</div>
|
55 |
<div className="flex items-center space-x-1">
|
56 |
<DatabaseIcon className="w-3 h-3 text-purple-300" />
|
57 |
-
<div className="h-3 bg-gray-200 rounded w-12"></div>
|
58 |
</div>
|
59 |
</div>
|
60 |
<hr className="border-gray-200" />
|
61 |
-
<div className="h-8 bg-gray-200 rounded w-full"></div>
|
62 |
</div>
|
63 |
)
|
64 |
|
@@ -67,12 +67,12 @@ const ModelInfo = () => {
|
|
67 |
}
|
68 |
|
69 |
return (
|
70 |
-
<div className="relative bg-
|
71 |
{/* Model Name Row */}
|
72 |
<div className="flex justify-center items-center space-x-2">
|
73 |
{/* Compatibility Status */}
|
74 |
{typeof modelInfo.isCompatible === 'boolean' && (
|
75 |
-
<div className="
|
76 |
{modelInfo.isCompatible ? (
|
77 |
<CheckCircle className="w-4 h-4 text-green-500" />
|
78 |
) : (
|
|
|
28 |
const { models, modelInfo, selectedQuantization, isFetching } = useModel()
|
29 |
|
30 |
const ModelInfoSkeleton = () => (
|
31 |
+
<div className="bg-linear-to-r from-blue-50 to-indigo-50 px-3 py-3 rounded-lg border border-blue-200 space-y-3 animate-pulse w-4/5">
|
32 |
<div className="flex items-center space-x-2">
|
33 |
<Bot className="w-4 h-4 text-blue-300" />
|
34 |
+
<div className="h-4 bg-gray-300 rounded-sm flex-1"></div>
|
35 |
<div className="w-4 h-4 bg-gray-300 rounded-full"></div>
|
36 |
</div>
|
37 |
|
38 |
<div className="flex items-center space-x-2 ml-6">
|
39 |
+
<div className="h-3 bg-gray-200 rounded-sm w-32"></div>
|
40 |
</div>
|
41 |
|
42 |
<div className="grid grid-cols-2 gap-2 text-xs">
|
43 |
<div className="flex items-center space-x-1">
|
44 |
<Heart className="w-3 h-3 text-red-300" />
|
45 |
+
<div className="h-3 bg-gray-200 rounded-sm w-8"></div>
|
46 |
</div>
|
47 |
<div className="flex items-center space-x-1">
|
48 |
<Download className="w-3 h-3 text-green-300" />
|
49 |
+
<div className="h-3 bg-gray-200 rounded-sm w-8"></div>
|
50 |
</div>
|
51 |
<div className="flex items-center space-x-1">
|
52 |
<Cpu className="w-3 h-3 text-purple-300" />
|
53 |
+
<div className="h-3 bg-gray-200 rounded-sm w-8"></div>
|
54 |
</div>
|
55 |
<div className="flex items-center space-x-1">
|
56 |
<DatabaseIcon className="w-3 h-3 text-purple-300" />
|
57 |
+
<div className="h-3 bg-gray-200 rounded-sm w-12"></div>
|
58 |
</div>
|
59 |
</div>
|
60 |
<hr className="border-gray-200" />
|
61 |
+
<div className="h-8 bg-gray-200 rounded-sm w-full"></div>
|
62 |
</div>
|
63 |
)
|
64 |
|
|
|
67 |
}
|
68 |
|
69 |
return (
|
70 |
+
<div className="relative bg-linear-to-r from-blue-50 to-indigo-50 px-3 py-3 rounded-lg border border-blue-200 space-y-3 h-full w-4/5">
|
71 |
{/* Model Name Row */}
|
72 |
<div className="flex justify-center items-center space-x-2">
|
73 |
{/* Compatibility Status */}
|
74 |
{typeof modelInfo.isCompatible === 'boolean' && (
|
75 |
+
<div className="shrink-0 ">
|
76 |
{modelInfo.isCompatible ? (
|
77 |
<CheckCircle className="w-4 h-4 text-green-500" />
|
78 |
) : (
|
src/components/ModelLoader.tsx
CHANGED
@@ -127,7 +127,7 @@ const ModelLoader = () => {
|
|
127 |
onChange={(e) =>
|
128 |
setSelectedQuantization(e.target.value as QuantizationType)
|
129 |
}
|
130 |
-
className="appearance-none bg-white border border-gray-300 rounded-md px-3 py-1 pr-8 text-xs text-gray-700 focus:outline-
|
131 |
>
|
132 |
{modelInfo.supportedQuantizations.map((quant) => (
|
133 |
<option key={quant} value={quant}>
|
@@ -148,7 +148,7 @@ const ModelLoader = () => {
|
|
148 |
{selectedQuantization && (
|
149 |
<div className="flex justify-center">
|
150 |
<button
|
151 |
-
className="w-32 py-2 px-4 bg-green-500 hover:bg-green-600 rounded text-white font-medium disabled:opacity-50 disabled:cursor-not-allowed transition-colors text-sm inline-flex items-center text-center justify-center space-x-2"
|
152 |
disabled={hasBeenLoaded || status === 'loading'}
|
153 |
onClick={loadModel}
|
154 |
>
|
|
|
127 |
onChange={(e) =>
|
128 |
setSelectedQuantization(e.target.value as QuantizationType)
|
129 |
}
|
130 |
+
className="appearance-none bg-white border border-gray-300 rounded-md px-3 py-1 pr-8 text-xs text-gray-700 focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
131 |
>
|
132 |
{modelInfo.supportedQuantizations.map((quant) => (
|
133 |
<option key={quant} value={quant}>
|
|
|
148 |
{selectedQuantization && (
|
149 |
<div className="flex justify-center">
|
150 |
<button
|
151 |
+
className="w-32 py-2 px-4 bg-green-500 hover:bg-green-600 rounded-sm text-white font-medium disabled:opacity-50 disabled:cursor-not-allowed transition-colors text-sm inline-flex items-center text-center justify-center space-x-2"
|
152 |
disabled={hasBeenLoaded || status === 'loading'}
|
153 |
onClick={loadModel}
|
154 |
>
|
src/components/ModelSelector.tsx
CHANGED
@@ -252,21 +252,21 @@ function ModelSelector() {
|
|
252 |
<div className="relative">
|
253 |
<div className="w-full px-3 py-2 border border-gray-300 rounded-md bg-white flex items-center justify-between animate-pulse h-10">
|
254 |
<div className="flex flex-col flex-1 min-w-0 space-y-2">
|
255 |
-
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
|
256 |
</div>
|
257 |
|
258 |
<div className="flex items-center space-x-3">
|
259 |
<div className="flex items-center space-x-3">
|
260 |
<div className="flex items-center space-x-1">
|
261 |
<Heart className="w-3 h-3 text-red-500" />
|
262 |
-
<div className="h-3 bg-gray-200 rounded w-8"></div>
|
263 |
</div>
|
264 |
<div className="flex items-center space-x-1">
|
265 |
<Download className="w-3 h-3 text-green-500" />
|
266 |
-
<div className="h-3 bg-gray-200 rounded w-8"></div>
|
267 |
</div>
|
268 |
</div>
|
269 |
-
<div className="w-4 h-4 bg-gray-200 rounded"></div>
|
270 |
</div>
|
271 |
</div>
|
272 |
</div>
|
@@ -280,7 +280,7 @@ function ModelSelector() {
|
|
280 |
onChange={(model) => handleModelSelect(model.id)}
|
281 |
>
|
282 |
<div className="relative">
|
283 |
-
<ListboxButton className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-
|
284 |
<div className="flex items-center justify-between w-full">
|
285 |
<div className="flex flex-col flex-1 min-w-0">
|
286 |
<Tooltip content={modelInfo?.id || 'Select a model'}>
|
@@ -308,7 +308,7 @@ function ModelSelector() {
|
|
308 |
)}
|
309 |
</div>
|
310 |
)}
|
311 |
-
<ChevronDown className="w-4 h-4 ui-open:rotate-180 transition-transform
|
312 |
</div>
|
313 |
</div>
|
314 |
</ListboxButton>
|
@@ -321,7 +321,7 @@ function ModelSelector() {
|
|
321 |
leaveFrom="transform scale-100 opacity-100"
|
322 |
leaveTo="transform scale-95 opacity-0"
|
323 |
>
|
324 |
-
<ListboxOptions className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-96 overflow-hidden focus:outline-
|
325 |
{/* Custom Model Input */}
|
326 |
{showCustomInput ? (
|
327 |
<div className="px-3 py-3 border-b border-gray-200 bg-gray-50 sticky top-0 z-10">
|
@@ -333,13 +333,13 @@ function ModelSelector() {
|
|
333 |
onChange={(e) => setCustomModelName(e.target.value)}
|
334 |
onKeyDown={handleCustomInputKeyPress}
|
335 |
placeholder="Enter model name (e.g., Qwen/Qwen3-0.6B)"
|
336 |
-
className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded focus:outline-
|
337 |
autoFocus
|
338 |
/>
|
339 |
<button
|
340 |
onClick={handleCustomModelLoad}
|
341 |
disabled={isLoadingCustomModel}
|
342 |
-
className="px-3 py-1 text-sm bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-1"
|
343 |
>
|
344 |
{isLoadingCustomModel ? (
|
345 |
<div className="w-3 h-3 border border-white border-t-transparent rounded-full animate-spin" />
|
@@ -373,7 +373,7 @@ function ModelSelector() {
|
|
373 |
{/* Load Custom Model Button */}
|
374 |
<button
|
375 |
onClick={() => setShowCustomInput(true)}
|
376 |
-
className="w-full flex items-center justify-center space-x-2 px-3 py-2 text-sm text-blue-600 hover:bg-blue-50 rounded transition-colors"
|
377 |
>
|
378 |
<Plus className="w-4 h-4" />
|
379 |
<span>Load Custom Model</span>
|
@@ -469,7 +469,7 @@ function ModelSelector() {
|
|
469 |
</span>
|
470 |
</Tooltip>
|
471 |
{selected && (
|
472 |
-
<Check className="w-4 h-4 text-blue-600 ml-2
|
473 |
)}
|
474 |
</div>
|
475 |
{/* Stats Display */}
|
|
|
252 |
<div className="relative">
|
253 |
<div className="w-full px-3 py-2 border border-gray-300 rounded-md bg-white flex items-center justify-between animate-pulse h-10">
|
254 |
<div className="flex flex-col flex-1 min-w-0 space-y-2">
|
255 |
+
<div className="h-4 bg-gray-200 rounded-sm w-3/4"></div>
|
256 |
</div>
|
257 |
|
258 |
<div className="flex items-center space-x-3">
|
259 |
<div className="flex items-center space-x-3">
|
260 |
<div className="flex items-center space-x-1">
|
261 |
<Heart className="w-3 h-3 text-red-500" />
|
262 |
+
<div className="h-3 bg-gray-200 rounded-sm w-8"></div>
|
263 |
</div>
|
264 |
<div className="flex items-center space-x-1">
|
265 |
<Download className="w-3 h-3 text-green-500" />
|
266 |
+
<div className="h-3 bg-gray-200 rounded-sm w-8"></div>
|
267 |
</div>
|
268 |
</div>
|
269 |
+
<div className="w-4 h-4 bg-gray-200 rounded-sm"></div>
|
270 |
</div>
|
271 |
</div>
|
272 |
</div>
|
|
|
280 |
onChange={(model) => handleModelSelect(model.id)}
|
281 |
>
|
282 |
<div className="relative">
|
283 |
+
<ListboxButton className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white text-left flex items-center justify-between">
|
284 |
<div className="flex items-center justify-between w-full">
|
285 |
<div className="flex flex-col flex-1 min-w-0">
|
286 |
<Tooltip content={modelInfo?.id || 'Select a model'}>
|
|
|
308 |
)}
|
309 |
</div>
|
310 |
)}
|
311 |
+
<ChevronDown className="w-4 h-4 ui-open:rotate-180 transition-transform shrink-0" />
|
312 |
</div>
|
313 |
</div>
|
314 |
</ListboxButton>
|
|
|
321 |
leaveFrom="transform scale-100 opacity-100"
|
322 |
leaveTo="transform scale-95 opacity-0"
|
323 |
>
|
324 |
+
<ListboxOptions className="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-96 overflow-hidden focus:outline-hidden">
|
325 |
{/* Custom Model Input */}
|
326 |
{showCustomInput ? (
|
327 |
<div className="px-3 py-3 border-b border-gray-200 bg-gray-50 sticky top-0 z-10">
|
|
|
333 |
onChange={(e) => setCustomModelName(e.target.value)}
|
334 |
onKeyDown={handleCustomInputKeyPress}
|
335 |
placeholder="Enter model name (e.g., Qwen/Qwen3-0.6B)"
|
336 |
+
className="flex-1 px-2 py-1 text-sm border border-gray-300 rounded-sm focus:outline-hidden focus:ring-1 focus:ring-blue-500"
|
337 |
autoFocus
|
338 |
/>
|
339 |
<button
|
340 |
onClick={handleCustomModelLoad}
|
341 |
disabled={isLoadingCustomModel}
|
342 |
+
className="px-3 py-1 text-sm bg-blue-600 text-white rounded-sm hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center space-x-1"
|
343 |
>
|
344 |
{isLoadingCustomModel ? (
|
345 |
<div className="w-3 h-3 border border-white border-t-transparent rounded-full animate-spin" />
|
|
|
373 |
{/* Load Custom Model Button */}
|
374 |
<button
|
375 |
onClick={() => setShowCustomInput(true)}
|
376 |
+
className="w-full flex items-center justify-center space-x-2 px-3 py-2 text-sm text-blue-600 hover:bg-blue-50 rounded-sm transition-colors"
|
377 |
>
|
378 |
<Plus className="w-4 h-4" />
|
379 |
<span>Load Custom Model</span>
|
|
|
469 |
</span>
|
470 |
</Tooltip>
|
471 |
{selected && (
|
472 |
+
<Check className="w-4 h-4 text-blue-600 ml-2 shrink-0" />
|
473 |
)}
|
474 |
</div>
|
475 |
{/* Stats Display */}
|
src/components/PipelineSelector.tsx
CHANGED
@@ -40,7 +40,7 @@ const PipelineSelector: React.FC<PipelineSelectorProps> = ({
|
|
40 |
<div className="relative">
|
41 |
<Listbox value={selectedPipeline} onChange={setPipeline}>
|
42 |
<div className="relative">
|
43 |
-
<ListboxButton className="relative w-full cursor-default rounded-lg bg-white py-2 pl-3 pr-10 text-left focus:outline-
|
44 |
<span className="block truncate font-medium">
|
45 |
{formatPipelineName(selectedPipeline)}
|
46 |
</span>
|
@@ -60,7 +60,7 @@ const PipelineSelector: React.FC<PipelineSelectorProps> = ({
|
|
60 |
leaveFrom="transform scale-100 opacity-100"
|
61 |
leaveTo="transform scale-95 opacity-0"
|
62 |
>
|
63 |
-
<ListboxOptions className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-sm xl:text-base ring-1 ring-black ring-opacity-5 focus:outline-
|
64 |
{supportedPipelines.map((p) => (
|
65 |
<ListboxOption
|
66 |
key={p}
|
|
|
40 |
<div className="relative">
|
41 |
<Listbox value={selectedPipeline} onChange={setPipeline}>
|
42 |
<div className="relative">
|
43 |
+
<ListboxButton className="relative w-full cursor-default rounded-lg bg-white py-2 pl-3 pr-10 text-left focus:outline-hidden focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 text-sm xl:text-base border border-gray-300">
|
44 |
<span className="block truncate font-medium">
|
45 |
{formatPipelineName(selectedPipeline)}
|
46 |
</span>
|
|
|
60 |
leaveFrom="transform scale-100 opacity-100"
|
61 |
leaveTo="transform scale-95 opacity-0"
|
62 |
>
|
63 |
+
<ListboxOptions className="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-sm xl:text-base ring-1 ring-black ring-opacity-5 focus:outline-hidden">
|
64 |
{supportedPipelines.map((p) => (
|
65 |
<ListboxOption
|
66 |
key={p}
|
src/components/Sidebar.tsx
CHANGED
@@ -81,7 +81,7 @@ const Sidebar = ({ isOpen, onClose, setIsModalOpen }: SidebarProps) => {
|
|
81 |
onClick={() => setIsModalOpen(true)}
|
82 |
className="flex items-center w-full px-3 py-2 text-sm text-gray-600 bg-gray-50 rounded-lg border border-gray-200 hover:bg-gray-100 transition-colors"
|
83 |
>
|
84 |
-
<FileText className="w-4 h-4 mr-2
|
85 |
<span className="truncate">View README.md</span>
|
86 |
</button>
|
87 |
</div>
|
|
|
81 |
onClick={() => setIsModalOpen(true)}
|
82 |
className="flex items-center w-full px-3 py-2 text-sm text-gray-600 bg-gray-50 rounded-lg border border-gray-200 hover:bg-gray-100 transition-colors"
|
83 |
>
|
84 |
+
<FileText className="w-4 h-4 mr-2 shrink-0" />
|
85 |
<span className="truncate">View README.md</span>
|
86 |
</button>
|
87 |
</div>
|
src/components/Tooltip.tsx
CHANGED
@@ -11,7 +11,7 @@ const Tooltip: React.FC<TooltipProps> = ({ children, content, className }) => {
|
|
11 |
<div className="relative group flex items-center">
|
12 |
{children}
|
13 |
<div
|
14 |
-
className={`absolute left-0 top-full mt-2 w-max max-w-sm px-3 py-1.5 text-xs font-medium text-white bg-gray-900 rounded-lg shadow-
|
15 |
>
|
16 |
{content}
|
17 |
</div>
|
|
|
11 |
<div className="relative group flex items-center">
|
12 |
{children}
|
13 |
<div
|
14 |
+
className={`absolute left-0 top-full mt-2 w-max max-w-sm px-3 py-1.5 text-xs font-medium text-white bg-gray-900 rounded-lg shadow-xs opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-opacity duration-300 z-20 pointer-events-none ${className}`}
|
15 |
>
|
16 |
{content}
|
17 |
</div>
|
src/components/pipelines/FeatureExtraction.tsx
CHANGED
@@ -273,7 +273,7 @@ function FeatureExtraction() {
|
|
273 |
onChange={(e) => setNewExampleText(e.target.value)}
|
274 |
onKeyPress={handleKeyPress}
|
275 |
placeholder="Enter text to get embeddings... (Press Enter to add)"
|
276 |
-
className="flex-1 p-3 border border-gray-300 rounded-lg resize-none focus:outline-
|
277 |
rows={2}
|
278 |
disabled={!hasBeenLoaded || isExtracting}
|
279 |
/>
|
|
|
273 |
onChange={(e) => setNewExampleText(e.target.value)}
|
274 |
onKeyPress={handleKeyPress}
|
275 |
placeholder="Enter text to get embeddings... (Press Enter to add)"
|
276 |
+
className="flex-1 p-3 border border-gray-300 rounded-lg resize-none focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed"
|
277 |
rows={2}
|
278 |
disabled={!hasBeenLoaded || isExtracting}
|
279 |
/>
|
src/components/pipelines/FeatureExtractionConfig.tsx
CHANGED
@@ -23,7 +23,7 @@ const FeatureExtractionConfig = () => {
|
|
23 |
pooling: e.target.value as 'mean' | 'cls' | 'max'
|
24 |
}))
|
25 |
}
|
26 |
-
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-
|
27 |
>
|
28 |
<option value="mean">Mean Pooling</option>
|
29 |
<option value="cls">CLS Token</option>
|
@@ -45,7 +45,7 @@ const FeatureExtractionConfig = () => {
|
|
45 |
normalize: e.target.checked
|
46 |
}))
|
47 |
}
|
48 |
-
className="rounded border-gray-300 text-blue-600 shadow-
|
49 |
/>
|
50 |
<span className="text-sm font-medium text-gray-700">
|
51 |
Normalize Embeddings
|
|
|
23 |
pooling: e.target.value as 'mean' | 'cls' | 'max'
|
24 |
}))
|
25 |
}
|
26 |
+
className="w-full px-3 py-2 border border-gray-300 rounded-md shadow-xs focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm"
|
27 |
>
|
28 |
<option value="mean">Mean Pooling</option>
|
29 |
<option value="cls">CLS Token</option>
|
|
|
45 |
normalize: e.target.checked
|
46 |
}))
|
47 |
}
|
48 |
+
className="rounded border-gray-300 text-blue-600 shadow-xs focus:border-blue-300 focus:ring-3 focus:ring-blue-200 focus:ring-opacity-50"
|
49 |
/>
|
50 |
<span className="text-sm font-medium text-gray-700">
|
51 |
Normalize Embeddings
|
src/components/pipelines/ImageClassification.tsx
CHANGED
@@ -305,7 +305,7 @@ function ImageClassification() {
|
|
305 |
>
|
306 |
<div className="flex gap-3">
|
307 |
{showPreviews && (
|
308 |
-
<div className="
|
309 |
<img
|
310 |
src={example.url}
|
311 |
alt={example.name}
|
|
|
305 |
>
|
306 |
<div className="flex gap-3">
|
307 |
{showPreviews && (
|
308 |
+
<div className="shrink-0">
|
309 |
<img
|
310 |
src={example.url}
|
311 |
alt={example.name}
|
src/components/pipelines/TextClassification.tsx
CHANGED
@@ -81,28 +81,28 @@ function TextClassification() {
|
|
81 |
|
82 |
return (
|
83 |
<div className="flex flex-col h-full max-h-[92vh] w-full p-4">
|
84 |
-
<h1 className="text-2xl font-bold mb-4
|
85 |
Text Classification
|
86 |
</h1>
|
87 |
|
88 |
<div className="flex flex-col lg:flex-row gap-4 flex-1 min-h-0">
|
89 |
{/* Input Section */}
|
90 |
<div className="flex flex-col w-full lg:w-1/2 min-h-0">
|
91 |
-
<label className="text-lg font-medium mb-2
|
92 |
Input Text ({numberExamples} examples):
|
93 |
</label>
|
94 |
|
95 |
<div className="flex flex-col flex-1 min-h-0">
|
96 |
<textarea
|
97 |
-
className="border border-gray-300 rounded p-3 flex-1 resize-none min-h-[200px]"
|
98 |
value={text}
|
99 |
onChange={(e) => setText(e.target.value)}
|
100 |
placeholder="Enter text to classify (one per line)..."
|
101 |
/>
|
102 |
|
103 |
-
<div className="flex gap-2 mt-4
|
104 |
<button
|
105 |
-
className="flex-1 py-2 px-4 bg-blue-500 hover:bg-blue-600 rounded text-white font-medium disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
106 |
disabled={busy}
|
107 |
onClick={classify}
|
108 |
>
|
@@ -113,7 +113,7 @@ function TextClassification() {
|
|
113 |
: 'Load model first'}
|
114 |
</button>
|
115 |
<button
|
116 |
-
className="py-2 px-4 bg-gray-500 hover:bg-gray-600 rounded text-white font-medium transition-colors"
|
117 |
onClick={handleClear}
|
118 |
>
|
119 |
Clear Results
|
@@ -124,11 +124,11 @@ function TextClassification() {
|
|
124 |
|
125 |
{/* Results Section */}
|
126 |
<div className="flex flex-col w-full lg:w-1/2 min-h-0">
|
127 |
-
<label className="text-lg font-medium mb-2
|
128 |
Classification Results ({results.length}):
|
129 |
</label>
|
130 |
|
131 |
-
<div className="border border-gray-300 rounded p-3 flex-1 overflow-y-auto min-h-[200px]">
|
132 |
{results.length === 0 ? (
|
133 |
<div className="text-gray-500 text-center py-8">
|
134 |
No results yet. Click "Classify Text" to analyze your input.
|
@@ -136,7 +136,7 @@ function TextClassification() {
|
|
136 |
) : (
|
137 |
<div className="space-y-3">
|
138 |
{results.map((result, index) => (
|
139 |
-
<div key={index} className="p-3 rounded border-2">
|
140 |
<div className="flex justify-between items-start mb-2">
|
141 |
<span className="font-semibold text-sm">
|
142 |
{result.labels[0]}
|
|
|
81 |
|
82 |
return (
|
83 |
<div className="flex flex-col h-full max-h-[92vh] w-full p-4">
|
84 |
+
<h1 className="text-2xl font-bold mb-4 shrink-0">
|
85 |
Text Classification
|
86 |
</h1>
|
87 |
|
88 |
<div className="flex flex-col lg:flex-row gap-4 flex-1 min-h-0">
|
89 |
{/* Input Section */}
|
90 |
<div className="flex flex-col w-full lg:w-1/2 min-h-0">
|
91 |
+
<label className="text-lg font-medium mb-2 shrink-0">
|
92 |
Input Text ({numberExamples} examples):
|
93 |
</label>
|
94 |
|
95 |
<div className="flex flex-col flex-1 min-h-0">
|
96 |
<textarea
|
97 |
+
className="border border-gray-300 rounded-sm p-3 flex-1 resize-none min-h-[200px]"
|
98 |
value={text}
|
99 |
onChange={(e) => setText(e.target.value)}
|
100 |
placeholder="Enter text to classify (one per line)..."
|
101 |
/>
|
102 |
|
103 |
+
<div className="flex gap-2 mt-4 shrink-0">
|
104 |
<button
|
105 |
+
className="flex-1 py-2 px-4 bg-blue-500 hover:bg-blue-600 rounded-sm text-white font-medium disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
106 |
disabled={busy}
|
107 |
onClick={classify}
|
108 |
>
|
|
|
113 |
: 'Load model first'}
|
114 |
</button>
|
115 |
<button
|
116 |
+
className="py-2 px-4 bg-gray-500 hover:bg-gray-600 rounded-sm text-white font-medium transition-colors"
|
117 |
onClick={handleClear}
|
118 |
>
|
119 |
Clear Results
|
|
|
124 |
|
125 |
{/* Results Section */}
|
126 |
<div className="flex flex-col w-full lg:w-1/2 min-h-0">
|
127 |
+
<label className="text-lg font-medium mb-2 shrink-0">
|
128 |
Classification Results ({results.length}):
|
129 |
</label>
|
130 |
|
131 |
+
<div className="border border-gray-300 rounded-sm p-3 flex-1 overflow-y-auto min-h-[200px]">
|
132 |
{results.length === 0 ? (
|
133 |
<div className="text-gray-500 text-center py-8">
|
134 |
No results yet. Click "Classify Text" to analyze your input.
|
|
|
136 |
) : (
|
137 |
<div className="space-y-3">
|
138 |
{results.map((result, index) => (
|
139 |
+
<div key={index} className="p-3 rounded-sm border-2">
|
140 |
<div className="flex justify-between items-start mb-2">
|
141 |
<span className="font-semibold text-sm">
|
142 |
{result.labels[0]}
|
src/components/pipelines/TextGeneration.tsx
CHANGED
@@ -227,7 +227,7 @@ function TextGeneration() {
|
|
227 |
onChange={(e) => setCurrentMessage(e.target.value)}
|
228 |
onKeyPress={handleKeyPress}
|
229 |
placeholder="Type your message... (Press Enter to send, Shift+Enter for new line)"
|
230 |
-
className="flex-1 p-3 border border-gray-300 rounded-lg resize-none focus:outline-
|
231 |
rows={2}
|
232 |
disabled={!hasBeenLoaded || isGenerating}
|
233 |
/>
|
@@ -255,7 +255,7 @@ function TextGeneration() {
|
|
255 |
onChange={(e) => setPrompt(e.target.value)}
|
256 |
onKeyPress={handleKeyPress}
|
257 |
placeholder="Enter your text prompt here... (Press Enter to generate, Shift+Enter for new line)"
|
258 |
-
className="w-full p-3 border border-gray-300 rounded-lg resize-none focus:outline-
|
259 |
rows={4}
|
260 |
disabled={!hasBeenLoaded || isGenerating}
|
261 |
/>
|
@@ -286,7 +286,7 @@ function TextGeneration() {
|
|
286 |
</label>
|
287 |
</div>
|
288 |
{generatedText ? (
|
289 |
-
<div className="whitespace-pre-wrap text-gray-800 bg-gray-50 p-3 rounded border">
|
290 |
{generatedText}
|
291 |
</div>
|
292 |
) : (
|
|
|
227 |
onChange={(e) => setCurrentMessage(e.target.value)}
|
228 |
onKeyPress={handleKeyPress}
|
229 |
placeholder="Type your message... (Press Enter to send, Shift+Enter for new line)"
|
230 |
+
className="flex-1 p-3 border border-gray-300 rounded-lg resize-none focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed"
|
231 |
rows={2}
|
232 |
disabled={!hasBeenLoaded || isGenerating}
|
233 |
/>
|
|
|
255 |
onChange={(e) => setPrompt(e.target.value)}
|
256 |
onKeyPress={handleKeyPress}
|
257 |
placeholder="Enter your text prompt here... (Press Enter to generate, Shift+Enter for new line)"
|
258 |
+
className="w-full p-3 border border-gray-300 rounded-lg resize-none focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed"
|
259 |
rows={4}
|
260 |
disabled={!hasBeenLoaded || isGenerating}
|
261 |
/>
|
|
|
286 |
</label>
|
287 |
</div>
|
288 |
{generatedText ? (
|
289 |
+
<div className="whitespace-pre-wrap text-gray-800 bg-gray-50 p-3 rounded-sm border">
|
290 |
{generatedText}
|
291 |
</div>
|
292 |
) : (
|
src/components/pipelines/TextGenerationConfig.tsx
CHANGED
@@ -135,7 +135,7 @@ function TextGenerationConfig() {
|
|
135 |
<textarea
|
136 |
value={messages.find((m) => m.role === 'system')?.content || ''}
|
137 |
onChange={(e) => updateSystemMessage(e.target.value)}
|
138 |
-
className="w-full p-2 border border-gray-300 rounded-md text-sm focus:outline-
|
139 |
rows={4}
|
140 |
placeholder="e.g., You are a helpful assistant."
|
141 |
/>
|
|
|
135 |
<textarea
|
136 |
value={messages.find((m) => m.role === 'system')?.content || ''}
|
137 |
onChange={(e) => updateSystemMessage(e.target.value)}
|
138 |
+
className="w-full p-2 border border-gray-300 rounded-md text-sm focus:outline-hidden focus:ring-1 focus:ring-blue-500 focus:border-blue-500"
|
139 |
rows={4}
|
140 |
placeholder="e.g., You are a helpful assistant."
|
141 |
/>
|
src/components/pipelines/ZeroShotClassification.tsx
CHANGED
@@ -95,7 +95,7 @@ function ZeroShotClassification() {
|
|
95 |
value={text}
|
96 |
onChange={(e) => setText(e.target.value)}
|
97 |
placeholder="Enter text items to classify, one per line..."
|
98 |
-
className="w-full p-3 border border-gray-300 rounded-lg resize-none focus:outline-
|
99 |
rows={12}
|
100 |
disabled={!hasBeenLoaded || busy}
|
101 |
/>
|
@@ -147,7 +147,7 @@ function ZeroShotClassification() {
|
|
147 |
{section.items.map((item, itemIndex) => (
|
148 |
<div
|
149 |
key={itemIndex}
|
150 |
-
className="p-2 bg-blue-50 border border-blue-200 rounded text-sm"
|
151 |
>
|
152 |
{item}
|
153 |
</div>
|
|
|
95 |
value={text}
|
96 |
onChange={(e) => setText(e.target.value)}
|
97 |
placeholder="Enter text items to classify, one per line..."
|
98 |
+
className="w-full p-3 border border-gray-300 rounded-lg resize-none focus:outline-hidden focus:ring-2 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed"
|
99 |
rows={12}
|
100 |
disabled={!hasBeenLoaded || busy}
|
101 |
/>
|
|
|
147 |
{section.items.map((item, itemIndex) => (
|
148 |
<div
|
149 |
key={itemIndex}
|
150 |
+
className="p-2 bg-blue-50 border border-blue-200 rounded-sm text-sm"
|
151 |
>
|
152 |
{item}
|
153 |
</div>
|
src/components/pipelines/ZeroShotClassificationConfig.tsx
CHANGED
@@ -57,9 +57,9 @@ const ZeroShotClassificationConfig = () => {
|
|
57 |
value={section.title}
|
58 |
onChange={(e) => updateSectionTitle(index, e.target.value)}
|
59 |
disabled={section.title === 'Other'}
|
60 |
-
className="flex-1 px-2 py-1 text-xs border border-gray-300 rounded focus:outline-
|
61 |
/>
|
62 |
-
<span className="text-xs text-gray-500 min-w-
|
63 |
({section.items.length})
|
64 |
</span>
|
65 |
</div>
|
@@ -69,7 +69,7 @@ const ZeroShotClassificationConfig = () => {
|
|
69 |
<div className="flex gap-2 mt-3">
|
70 |
<button
|
71 |
onClick={addCategory}
|
72 |
-
className="flex items-center gap-1 px-3 py-1 text-xs bg-green-500 hover:bg-green-600 text-white rounded transition-colors"
|
73 |
title="Add Category"
|
74 |
>
|
75 |
<Plus className="w-3 h-3" />
|
@@ -78,7 +78,7 @@ const ZeroShotClassificationConfig = () => {
|
|
78 |
<button
|
79 |
onClick={removeCategory}
|
80 |
disabled={sections.length <= 1}
|
81 |
-
className="flex items-center gap-1 px-3 py-1 text-xs bg-red-500 hover:bg-red-600 disabled:bg-gray-300 disabled:cursor-not-allowed text-white rounded transition-colors"
|
82 |
title="Remove Category"
|
83 |
>
|
84 |
<Minus className="w-3 h-3" />
|
@@ -86,7 +86,7 @@ const ZeroShotClassificationConfig = () => {
|
|
86 |
</button>
|
87 |
<button
|
88 |
onClick={clearResults}
|
89 |
-
className="flex items-center gap-1 px-3 py-1 text-xs bg-orange-500 hover:bg-orange-600 text-white rounded transition-colors"
|
90 |
title="Clear Results"
|
91 |
>
|
92 |
<Trash2 className="w-3 h-3" />
|
|
|
57 |
value={section.title}
|
58 |
onChange={(e) => updateSectionTitle(index, e.target.value)}
|
59 |
disabled={section.title === 'Other'}
|
60 |
+
className="flex-1 px-2 py-1 text-xs border border-gray-300 rounded-sm focus:outline-hidden focus:ring-1 focus:ring-blue-500 focus:border-blue-500 disabled:bg-gray-100 disabled:cursor-not-allowed"
|
61 |
/>
|
62 |
+
<span className="text-xs text-gray-500 min-w-8">
|
63 |
({section.items.length})
|
64 |
</span>
|
65 |
</div>
|
|
|
69 |
<div className="flex gap-2 mt-3">
|
70 |
<button
|
71 |
onClick={addCategory}
|
72 |
+
className="flex items-center gap-1 px-3 py-1 text-xs bg-green-500 hover:bg-green-600 text-white rounded-sm transition-colors"
|
73 |
title="Add Category"
|
74 |
>
|
75 |
<Plus className="w-3 h-3" />
|
|
|
78 |
<button
|
79 |
onClick={removeCategory}
|
80 |
disabled={sections.length <= 1}
|
81 |
+
className="flex items-center gap-1 px-3 py-1 text-xs bg-red-500 hover:bg-red-600 disabled:bg-gray-300 disabled:cursor-not-allowed text-white rounded-sm transition-colors"
|
82 |
title="Remove Category"
|
83 |
>
|
84 |
<Minus className="w-3 h-3" />
|
|
|
86 |
</button>
|
87 |
<button
|
88 |
onClick={clearResults}
|
89 |
+
className="flex items-center gap-1 px-3 py-1 text-xs bg-orange-500 hover:bg-orange-600 text-white rounded-sm transition-colors"
|
90 |
title="Clear Results"
|
91 |
>
|
92 |
<Trash2 className="w-3 h-3" />
|
src/index.css
CHANGED
@@ -1,6 +1,22 @@
|
|
1 |
-
@
|
2 |
-
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
body {
|
6 |
margin: 0;
|
|
|
1 |
+
@import 'tailwindcss';
|
2 |
+
|
3 |
+
/*
|
4 |
+
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
5 |
+
so we've added these compatibility styles to make sure everything still
|
6 |
+
looks the same as it did with Tailwind CSS v3.
|
7 |
+
|
8 |
+
If we ever want to remove these styles, we need to add an explicit border
|
9 |
+
color utility to any element that depends on these defaults.
|
10 |
+
*/
|
11 |
+
@layer base {
|
12 |
+
*,
|
13 |
+
::after,
|
14 |
+
::before,
|
15 |
+
::backdrop,
|
16 |
+
::file-selector-button {
|
17 |
+
border-color: var(--color-gray-200, currentcolor);
|
18 |
+
}
|
19 |
+
}
|
20 |
|
21 |
body {
|
22 |
margin: 0;
|
src/lib/workerManager.ts
CHANGED
@@ -6,19 +6,19 @@ export const getWorker = (pipeline: string) => {
|
|
6 |
|
7 |
switch (pipeline) {
|
8 |
case 'text-classification':
|
9 |
-
workerUrl =
|
10 |
break
|
11 |
case 'zero-shot-classification':
|
12 |
-
workerUrl =
|
13 |
break
|
14 |
case 'text-generation':
|
15 |
-
workerUrl =
|
16 |
break
|
17 |
case 'feature-extraction':
|
18 |
-
workerUrl =
|
19 |
break
|
20 |
case 'image-classification':
|
21 |
-
workerUrl =
|
22 |
break
|
23 |
default:
|
24 |
return null
|
|
|
6 |
|
7 |
switch (pipeline) {
|
8 |
case 'text-classification':
|
9 |
+
workerUrl = `/workers/text-classification.js`
|
10 |
break
|
11 |
case 'zero-shot-classification':
|
12 |
+
workerUrl = `/workers/zero-shot-classification.js`
|
13 |
break
|
14 |
case 'text-generation':
|
15 |
+
workerUrl = `/workers/text-generation.js`
|
16 |
break
|
17 |
case 'feature-extraction':
|
18 |
+
workerUrl = `/workers/feature-extraction.js`
|
19 |
break
|
20 |
case 'image-classification':
|
21 |
+
workerUrl = `/workers/image-classification.js`
|
22 |
break
|
23 |
default:
|
24 |
return null
|
tailwind.config.js
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
/** @type {import('tailwindcss').Config} */
|
2 |
-
module.exports = {
|
3 |
-
content: ['./src/**/*.{js,jsx,ts,tsx}'],
|
4 |
-
theme: {
|
5 |
-
extend: {}
|
6 |
-
}
|
7 |
-
// plugins: [require('@tailwindcss/typography')]
|
8 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tsconfig.json
CHANGED
@@ -1,20 +1,21 @@
|
|
1 |
{
|
2 |
"compilerOptions": {
|
3 |
-
"target": "
|
4 |
-
"lib": ["dom", "dom.iterable", "esnext"
|
5 |
-
"allowJs":
|
6 |
"skipLibCheck": true,
|
7 |
"esModuleInterop": true,
|
8 |
"allowSyntheticDefaultImports": true,
|
9 |
"strict": true,
|
10 |
"forceConsistentCasingInFileNames": true,
|
11 |
-
"noFallthroughCasesInSwitch": true,
|
12 |
"module": "esnext",
|
13 |
"moduleResolution": "node",
|
14 |
"resolveJsonModule": true,
|
15 |
"isolatedModules": true,
|
16 |
"noEmit": true,
|
17 |
-
"
|
|
|
|
|
18 |
},
|
19 |
-
"include": ["src"
|
20 |
}
|
|
|
1 |
{
|
2 |
"compilerOptions": {
|
3 |
+
"target": "ESNext",
|
4 |
+
"lib": ["dom", "dom.iterable", "esnext"],
|
5 |
+
"allowJs": false,
|
6 |
"skipLibCheck": true,
|
7 |
"esModuleInterop": true,
|
8 |
"allowSyntheticDefaultImports": true,
|
9 |
"strict": true,
|
10 |
"forceConsistentCasingInFileNames": true,
|
|
|
11 |
"module": "esnext",
|
12 |
"moduleResolution": "node",
|
13 |
"resolveJsonModule": true,
|
14 |
"isolatedModules": true,
|
15 |
"noEmit": true,
|
16 |
+
"noFallthroughCasesInSwitch": true,
|
17 |
+
"jsx": "react-jsx",
|
18 |
+
"types": ["vite/client", "vite-plugin-svgr/client"]
|
19 |
},
|
20 |
+
"include": ["src"]
|
21 |
}
|
vite-env.d.ts
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
/// <reference types="vite/client" />
|
vite.config.ts
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { defineConfig } from 'vite'
|
2 |
+
import react from '@vitejs/plugin-react-swc'
|
3 |
+
import tailwindcss from '@tailwindcss/vite'
|
4 |
+
|
5 |
+
// https://vitejs.dev/config/
|
6 |
+
export default defineConfig({
|
7 |
+
base: '/',
|
8 |
+
plugins: [react(), tailwindcss()]
|
9 |
+
})
|