Commit
Β·
297c500
1
Parent(s):
5053808
dockerize, add category pages, and footer
Browse files- Dockerfile +49 -0
- app/category/[slug]/page.tsx +9 -0
- app/page.tsx +10 -2
- components/Footer.tsx +37 -0
- components/InstallLine.tsx +41 -0
- components/Navbar.tsx +18 -13
- components/PillLink.tsx +15 -27
- components/{CategoryCard.tsx β category/CategoryCard.tsx} +2 -2
- components/category/CategoryContent.tsx +99 -0
- components/{CategoryPage.tsx β category/CategoryPage.tsx} +0 -0
- components/{CategoryRow.tsx β category/CategoryRow.tsx} +1 -1
- lib/icons/ClassificationIcon.tsx +6 -3
- next.config.mjs +2 -2
- types/categories.ts +1 -0
Dockerfile
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# syntax=docker/dockerfile:1.4
|
2 |
+
|
3 |
+
FROM node:20-alpine AS base
|
4 |
+
|
5 |
+
# Install dependencies only when needed
|
6 |
+
FROM base AS deps
|
7 |
+
WORKDIR /app
|
8 |
+
|
9 |
+
# Install dependencies based on the preferred package manager
|
10 |
+
COPY package.json package-lock.json* ./
|
11 |
+
RUN npm ci
|
12 |
+
|
13 |
+
# Rebuild the source code only when needed
|
14 |
+
FROM base AS builder
|
15 |
+
WORKDIR /app
|
16 |
+
COPY --from=deps /app/node_modules ./node_modules
|
17 |
+
COPY . .
|
18 |
+
|
19 |
+
# Next.js collects completely anonymous telemetry data about general usage.
|
20 |
+
# Uncomment the following line in case you want to disable telemetry during the build.
|
21 |
+
# ENV NEXT_TELEMETRY_DISABLED 1
|
22 |
+
|
23 |
+
RUN npm run build
|
24 |
+
|
25 |
+
# Production image, copy all the files and run next
|
26 |
+
FROM base AS runner
|
27 |
+
WORKDIR /app
|
28 |
+
|
29 |
+
ENV NODE_ENV production
|
30 |
+
# Uncomment the following line in case you want to disable telemetry during runtime.
|
31 |
+
# ENV NEXT_TELEMETRY_DISABLED 1
|
32 |
+
|
33 |
+
RUN \
|
34 |
+
addgroup --system --gid 1001 nodejs; \
|
35 |
+
adduser --system --uid 1001 nextjs
|
36 |
+
|
37 |
+
COPY --from=builder /app/public ./public
|
38 |
+
|
39 |
+
# Automatically leverage output traces to reduce image size
|
40 |
+
COPY --from=builder --chown=1001:1001 /app/.next/standalone ./
|
41 |
+
COPY --from=builder --chown=1001:1001 /app/.next/static ./.next/static
|
42 |
+
|
43 |
+
USER nextjs
|
44 |
+
|
45 |
+
EXPOSE 3000
|
46 |
+
|
47 |
+
ENV PORT 3000
|
48 |
+
ENV HOSTNAME 0.0.0.0
|
49 |
+
CMD ["node", "server.js"]
|
app/category/[slug]/page.tsx
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import CategoryContent from '@/components/category/CategoryContent';
|
2 |
+
|
3 |
+
export default function CategoryPage({ params }: { params: { slug: string } }) {
|
4 |
+
return (
|
5 |
+
<div className="container mx-auto px-4 py-8">
|
6 |
+
<CategoryContent slug={params.slug} />
|
7 |
+
</div>
|
8 |
+
);
|
9 |
+
}
|
app/page.tsx
CHANGED
@@ -1,6 +1,8 @@
|
|
1 |
"use client"
|
2 |
import PillLink from '@/components/PillLink';
|
3 |
-
import CategoryRow from '@/components/CategoryRow';
|
|
|
|
|
4 |
import { categories } from "@/lib/categories";
|
5 |
|
6 |
export default function Home() {
|
@@ -10,12 +12,18 @@ export default function Home() {
|
|
10 |
<h1 className="text-4xl sm:text-5xl md:text-6xl font-extrabold tracking-tighter mb-4">
|
11 |
Playground
|
12 |
</h1>
|
13 |
-
<p className="font-bold text-slate-700 font-mono italic text-lg mb-
|
14 |
The fastest way to get started with Transformers.js.
|
15 |
</p>
|
|
|
|
|
|
|
16 |
<div className="mt-24">
|
17 |
<CategoryRow categories={categories} />
|
18 |
</div>
|
|
|
|
|
|
|
19 |
</div>
|
20 |
);
|
21 |
}
|
|
|
1 |
"use client"
|
2 |
import PillLink from '@/components/PillLink';
|
3 |
+
import CategoryRow from '@/components/category/CategoryRow';
|
4 |
+
import InstallLine from '@/components/InstallLine';
|
5 |
+
import Footer from '@/components/Footer';
|
6 |
import { categories } from "@/lib/categories";
|
7 |
|
8 |
export default function Home() {
|
|
|
12 |
<h1 className="text-4xl sm:text-5xl md:text-6xl font-extrabold tracking-tighter mb-4">
|
13 |
Playground
|
14 |
</h1>
|
15 |
+
<p className="font-bold text-slate-700 font-mono italic text-lg mb-6">
|
16 |
The fastest way to get started with Transformers.js.
|
17 |
</p>
|
18 |
+
<div className="mx-auto mb-12">
|
19 |
+
<InstallLine packageName="@huggingface/transformers" />
|
20 |
+
</div>
|
21 |
<div className="mt-24">
|
22 |
<CategoryRow categories={categories} />
|
23 |
</div>
|
24 |
+
<div className="mt-64">
|
25 |
+
<Footer />
|
26 |
+
</div>
|
27 |
</div>
|
28 |
);
|
29 |
}
|
components/Footer.tsx
ADDED
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React from 'react';
|
2 |
+
import Link from 'next/link';
|
3 |
+
import { categories } from '../lib/categories';
|
4 |
+
|
5 |
+
const Footer: React.FC = () => {
|
6 |
+
return (
|
7 |
+
<footer className="bg-emerald-400/80 p-20 relative overflow-hidden rounded-xl">
|
8 |
+
<div className="max-w-7xl mx-auto relative z-10">
|
9 |
+
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-8 mb-12">
|
10 |
+
{categories.map((category) => (
|
11 |
+
<div key={category.slug} className="text-center sm:text-left">
|
12 |
+
<Link
|
13 |
+
href={`/category/${category.slug}`}
|
14 |
+
className="text-lg font-bold text-emerald-800 hover:text-emerald-900 transition-colors duration-300 ease-in-out relative group"
|
15 |
+
>
|
16 |
+
{category.title}
|
17 |
+
<span className="absolute left-0 right-0 bottom-0 h-0.5 bg-emerald-800 transform scale-x-0 group-hover:scale-x-100 transition-transform duration-300 ease-in-out"></span>
|
18 |
+
</Link>
|
19 |
+
</div>
|
20 |
+
))}
|
21 |
+
</div>
|
22 |
+
</div>
|
23 |
+
|
24 |
+
<div className="absolute inset-x-0 bottom-0 text-center py-4">
|
25 |
+
<h1 className="text-6xl md:text-8xl font-black text-red-500 tracking-tighter transform -skew-x-6">
|
26 |
+
TRANSFORMERS.JS
|
27 |
+
</h1>
|
28 |
+
</div>
|
29 |
+
|
30 |
+
<div className="absolute inset-0 flex items-center justify-center opacity-5">
|
31 |
+
<div className="w-96 h-96 bg-black rounded-full"></div>
|
32 |
+
</div>
|
33 |
+
</footer>
|
34 |
+
);
|
35 |
+
};
|
36 |
+
|
37 |
+
export default Footer;
|
components/InstallLine.tsx
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState } from 'react';
|
2 |
+
import { Clipboard, Check } from 'lucide-react';
|
3 |
+
|
4 |
+
const InstallLine: React.FC<{ packageName: string }> = ({ packageName }) => {
|
5 |
+
const [hasCopied, setHasCopied] = useState(false);
|
6 |
+
const command = `npm i ${packageName}`;
|
7 |
+
|
8 |
+
const copyToClipboard = () => {
|
9 |
+
navigator.clipboard.writeText(command);
|
10 |
+
setHasCopied(true);
|
11 |
+
setTimeout(() => setHasCopied(false), 2000);
|
12 |
+
};
|
13 |
+
|
14 |
+
return (
|
15 |
+
<div className="flex items-center justify-center p-2">
|
16 |
+
<div
|
17 |
+
className="flex max-w-sm w-full items-center justify-between bg-white border border-gray-200 rounded-md py-1.5 px-2 font-mono text-xs shadow-sm hover:shadow transition-all duration-200 ease-in-out cursor-pointer"
|
18 |
+
onClick={copyToClipboard}
|
19 |
+
role="button"
|
20 |
+
tabIndex={0}
|
21 |
+
aria-label="Copy npm install command"
|
22 |
+
>
|
23 |
+
<div className="flex items-center font-bold space-x-2 overflow-x-auto">
|
24 |
+
<span className="text-gray-400 select-none">$</span>
|
25 |
+
<span className="text-blue-500 font-semibold">npm</span>
|
26 |
+
<span className="text-green-600">i</span>
|
27 |
+
<span className="text-pink-500">{packageName}</span>
|
28 |
+
</div>
|
29 |
+
<div className="p-1 flex-shrink-0" aria-hidden="true">
|
30 |
+
{hasCopied ? (
|
31 |
+
<Check className="size-4 text-emerald-500" />
|
32 |
+
) : (
|
33 |
+
<Clipboard className="size-4 text-gray-400 hover:text-gray-600" />
|
34 |
+
)}
|
35 |
+
</div>
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
);
|
39 |
+
};
|
40 |
+
|
41 |
+
export default InstallLine;
|
components/Navbar.tsx
CHANGED
@@ -5,19 +5,24 @@ import { FileText, Github } from "lucide-react"
|
|
5 |
export default function Navbar() {
|
6 |
return (
|
7 |
<nav className="border-b">
|
8 |
-
<div className="container flex
|
9 |
-
<
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
21 |
</div>
|
22 |
</nav>
|
23 |
)
|
|
|
5 |
export default function Navbar() {
|
6 |
return (
|
7 |
<nav className="border-b">
|
8 |
+
<div className="container mx-auto flex items-center justify-between py-4 px-4 sm:px-8">
|
9 |
+
<Link href="/" className="text-lg font-bold">
|
10 |
+
Playground
|
11 |
+
</Link>
|
12 |
+
<div className="flex items-center">
|
13 |
+
<Button variant="ghost" asChild className="p-2 sm:p-4 mr-2">
|
14 |
+
<Link href="https://huggingface.co/docs/transformers.js/en/index" target="_blank" rel="noopener noreferrer" className="flex items-center">
|
15 |
+
<FileText className="h-4 w-4 mr-2" />
|
16 |
+
<span className="font-medium">Docs</span>
|
17 |
+
</Link>
|
18 |
+
</Button>
|
19 |
+
<Button variant="ghost" asChild className="p-2 sm:p-4">
|
20 |
+
<Link href="https://github.com/cfahlgren1/transformersjs-playground" target="_blank" rel="noopener noreferrer" className="flex items-center">
|
21 |
+
<Github className="h-4 w-4 mr-2" />
|
22 |
+
<span className="font-medium hidden sm:inline">Star on GitHub</span>
|
23 |
+
</Link>
|
24 |
+
</Button>
|
25 |
+
</div>
|
26 |
</div>
|
27 |
</nav>
|
28 |
)
|
components/PillLink.tsx
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
import Link from 'next/link';
|
2 |
-
import { Badge } from '@/components/ui/badge';
|
3 |
|
4 |
interface PillLinkProps {
|
5 |
text: string;
|
@@ -8,32 +7,21 @@ interface PillLinkProps {
|
|
8 |
newText?: string;
|
9 |
}
|
10 |
|
11 |
-
const PillLink: React.FC<PillLinkProps> = ({ text, link, isNew = false, newText = 'NEW' }) =>
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
overflow-hidden
|
22 |
-
group
|
23 |
-
"
|
24 |
-
>
|
25 |
-
<span className="relative z-10">
|
26 |
-
{(isNew || newText) && (
|
27 |
-
<span className="mr-2 rounded-full bg-white px-1.5 py-0.5 text-[10px] font-bold text-gray-900">
|
28 |
-
{newText}
|
29 |
-
</span>
|
30 |
-
)}
|
31 |
-
{text}
|
32 |
</span>
|
33 |
-
|
34 |
-
</
|
35 |
-
</
|
36 |
-
|
37 |
-
|
38 |
|
39 |
export default PillLink;
|
|
|
1 |
import Link from 'next/link';
|
|
|
2 |
|
3 |
interface PillLinkProps {
|
4 |
text: string;
|
|
|
7 |
newText?: string;
|
8 |
}
|
9 |
|
10 |
+
const PillLink: React.FC<PillLinkProps> = ({ text, link, isNew = false, newText = 'NEW' }) => (
|
11 |
+
<Link
|
12 |
+
href={link}
|
13 |
+
target="_blank"
|
14 |
+
className="inline-flex items-center bg-white border border-gray-200 rounded-full py-1 px-2.5 mb-3 shadow-sm hover:shadow transition-all duration-200 ease-in-out cursor-pointer"
|
15 |
+
>
|
16 |
+
<span className="font-mono text-xs flex items-center">
|
17 |
+
{(isNew || newText) && (
|
18 |
+
<span className="mr-1.5 rounded-full bg-red-500 px-2 py-0.5 text-[8px] font-bold text-white">
|
19 |
+
{newText}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
</span>
|
21 |
+
)}
|
22 |
+
<span className="text-gray-800 text-[11px]">{text}</span>
|
23 |
+
</span>
|
24 |
+
</Link>
|
25 |
+
);
|
26 |
|
27 |
export default PillLink;
|
components/{CategoryCard.tsx β category/CategoryCard.tsx}
RENAMED
@@ -1,7 +1,7 @@
|
|
1 |
import React from 'react';
|
2 |
import Link from 'next/link';
|
3 |
-
import type { Category } from '
|
4 |
-
import { getColorConfig } from '
|
5 |
|
6 |
type CategoryCardProps = Category;
|
7 |
|
|
|
1 |
import React from 'react';
|
2 |
import Link from 'next/link';
|
3 |
+
import type { Category } from '../../types/categories';
|
4 |
+
import { getColorConfig } from '../../lib/utils';
|
5 |
|
6 |
type CategoryCardProps = Category;
|
7 |
|
components/category/CategoryContent.tsx
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
'use client';
|
2 |
+
|
3 |
+
import { useEffect, useState } from 'react';
|
4 |
+
import Link from 'next/link';
|
5 |
+
import { notFound } from 'next/navigation';
|
6 |
+
import { categories } from '@/lib/categories';
|
7 |
+
import type { Category } from '@/types/categories';
|
8 |
+
import { getColorConfig, cn } from '@/lib/utils';
|
9 |
+
|
10 |
+
export default function CategoryContent({ slug }: { slug: string }) {
|
11 |
+
const [category, setCategory] = useState<Category | null>(null);
|
12 |
+
const [isLoading, setIsLoading] = useState(true);
|
13 |
+
|
14 |
+
useEffect(() => {
|
15 |
+
const foundCategory = categories.find((cat) => cat.slug === slug);
|
16 |
+
setCategory(foundCategory || null);
|
17 |
+
setIsLoading(false);
|
18 |
+
}, [slug]);
|
19 |
+
|
20 |
+
if (isLoading) {
|
21 |
+
return <div className="flex justify-center items-center h-screen">Loading...</div>;
|
22 |
+
}
|
23 |
+
|
24 |
+
if (!category) {
|
25 |
+
notFound();
|
26 |
+
}
|
27 |
+
|
28 |
+
const colorConfig = getColorConfig(category.colorName);
|
29 |
+
|
30 |
+
return (
|
31 |
+
<div className="container mx-auto px-4 py-8">
|
32 |
+
<Breadcrumb category={category} colorConfig={colorConfig} />
|
33 |
+
<Header category={category} colorConfig={colorConfig} />
|
34 |
+
<div className="mt-12">
|
35 |
+
<DemoCards category={category} colorConfig={colorConfig} />
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
);
|
39 |
+
}
|
40 |
+
|
41 |
+
|
42 |
+
function Breadcrumb({ category, colorConfig }: { category: Category; colorConfig: ReturnType<typeof getColorConfig> }) {
|
43 |
+
return (
|
44 |
+
<nav className="text-sm mb-4">
|
45 |
+
<ol className="list-none p-0 inline-flex">
|
46 |
+
<li className="flex items-center">
|
47 |
+
<Link href="/" className="text-slate-600 hover:underline">
|
48 |
+
Home
|
49 |
+
</Link>
|
50 |
+
<span className="mx-2 text-slate-400">/</span>
|
51 |
+
</li>
|
52 |
+
<li className={cn("text-opacity-80", colorConfig.text)}>{category.title}</li>
|
53 |
+
</ol>
|
54 |
+
</nav>
|
55 |
+
);
|
56 |
+
}
|
57 |
+
|
58 |
+
function Header({ category, colorConfig }: { category: Category; colorConfig: ReturnType<typeof getColorConfig> }) {
|
59 |
+
return (
|
60 |
+
<header className="text-center">
|
61 |
+
<h1 className={cn("text-4xl sm:text-5xl md:text-6xl font-extrabold tracking-tighter mb-4", colorConfig.text)}>
|
62 |
+
{category.title}
|
63 |
+
</h1>
|
64 |
+
<p className="font-bold text-slate-700 font-mono italic text-lg mb-10">
|
65 |
+
{category.description}
|
66 |
+
</p>
|
67 |
+
</header>
|
68 |
+
);
|
69 |
+
}
|
70 |
+
|
71 |
+
function DemoCards({ category, colorConfig }: { category: Category; colorConfig: ReturnType<typeof getColorConfig> }) {
|
72 |
+
if (!category.demos || category.demos.length === 0) {
|
73 |
+
return (
|
74 |
+
<div className="flex flex-col items-center justify-center h-64 md:h-96 px-4 py-8 bg-gray-50 rounded-lg shadow-inner">
|
75 |
+
<div className={cn("text-6xl mb-4", colorConfig.text)}>
|
76 |
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="black" className="w-16 h-16">
|
77 |
+
<path fillRule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25zm-2.625 6c-.54 0-.828.419-.936.634a1.96 1.96 0 00-.189.866c0 .298.059.605.189.866.108.215.395.634.936.634.54 0 .828-.419.936-.634.13-.26.189-.568.189-.866 0-.298-.059-.605-.189-.866-.108-.215-.395-.634-.936-.634zm4.314.634c.108-.215.395-.634.936-.634.54 0 .828.419.936.634.13.26.189.568.189.866 0 .298-.059.605-.189.866-.108.215-.395.634-.936.634-.54 0-.828-.419-.936-.634a1.96 1.96 0 01-.189-.866c0-.298.059-.605.189-.866zm-4.34 7.964a.75.75 0 01-1.061-1.06 5.236 5.236 0 013.73-1.538 5.236 5.236 0 013.695 1.538.75.75 0 11-1.061 1.06 3.736 3.736 0 00-2.639-1.098 3.736 3.736 0 00-2.664 1.098z" clipRule="evenodd" />
|
78 |
+
</svg>
|
79 |
+
</div>
|
80 |
+
<h2 className={cn("text-2xl md:text-3xl font-bold text-center mb-2")}>
|
81 |
+
No demos available yet
|
82 |
+
</h2>
|
83 |
+
<p className="text-base md:text-lg text-gray-600 text-center max-w-md">
|
84 |
+
We're working on exciting demos for this category. Check back soon to see what's new!
|
85 |
+
</p>
|
86 |
+
</div>
|
87 |
+
);
|
88 |
+
}
|
89 |
+
|
90 |
+
return (
|
91 |
+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
92 |
+
{category.demos.map((Demo, index) => (
|
93 |
+
<div key={index} className={cn("rounded-lg shadow-md p-6", colorConfig.bg)}>
|
94 |
+
<Demo />
|
95 |
+
</div>
|
96 |
+
))}
|
97 |
+
</div>
|
98 |
+
);
|
99 |
+
}
|
components/{CategoryPage.tsx β category/CategoryPage.tsx}
RENAMED
File without changes
|
components/{CategoryRow.tsx β category/CategoryRow.tsx}
RENAMED
@@ -1,5 +1,5 @@
|
|
1 |
import { motion } from 'framer-motion';
|
2 |
-
import CategoryCard from '@/components/CategoryCard';
|
3 |
import { Category } from '@/types/categories';
|
4 |
|
5 |
interface CategoryRowProps {
|
|
|
1 |
import { motion } from 'framer-motion';
|
2 |
+
import CategoryCard from '@/components/category/CategoryCard';
|
3 |
import { Category } from '@/types/categories';
|
4 |
|
5 |
interface CategoryRowProps {
|
lib/icons/ClassificationIcon.tsx
CHANGED
@@ -23,17 +23,20 @@ const ClassificationIcon: React.FC<ClassificationIconProps> = ({ className = ''
|
|
23 |
<text x="130" y="25" fill="#7e22ce" fontSize="12" fontWeight="bold">Output</text>
|
24 |
|
25 |
{/* Classification bars */}
|
|
|
26 |
<rect x="130" y="35" width="65" height="14" fill="#d8b4fe" rx="3" ry="3" />
|
27 |
<text x="135" y="45" fill="#6b21a8" fontSize="10" fontWeight="bold">Sport</text>
|
28 |
-
<text x="
|
29 |
|
|
|
30 |
<rect x="130" y="53" width="26" height="14" fill="#d8b4fe" rx="3" ry="3" />
|
31 |
<text x="135" y="63" fill="#6b21a8" fontSize="10" fontWeight="bold">Food</text>
|
32 |
-
<text x="
|
33 |
|
|
|
34 |
<rect x="130" y="71" width="9" height="14" fill="#d8b4fe" rx="3" ry="3" />
|
35 |
<text x="135" y="81" fill="#6b21a8" fontSize="10" fontWeight="bold">Music</text>
|
36 |
-
<text x="
|
37 |
</svg>
|
38 |
);
|
39 |
};
|
|
|
23 |
<text x="130" y="25" fill="#7e22ce" fontSize="12" fontWeight="bold">Output</text>
|
24 |
|
25 |
{/* Classification bars */}
|
26 |
+
{/* Sport */}
|
27 |
<rect x="130" y="35" width="65" height="14" fill="#d8b4fe" rx="3" ry="3" />
|
28 |
<text x="135" y="45" fill="#6b21a8" fontSize="10" fontWeight="bold">Sport</text>
|
29 |
+
<text x="200" y="45" fill="#6b21a8" fontSize="10" textAnchor="end">0.800</text>
|
30 |
|
31 |
+
{/* Food */}
|
32 |
<rect x="130" y="53" width="26" height="14" fill="#d8b4fe" rx="3" ry="3" />
|
33 |
<text x="135" y="63" fill="#6b21a8" fontSize="10" fontWeight="bold">Food</text>
|
34 |
+
<text x="200" y="63" fill="#6b21a8" fontSize="10" textAnchor="end">0.150</text>
|
35 |
|
36 |
+
{/* Music */}
|
37 |
<rect x="130" y="71" width="9" height="14" fill="#d8b4fe" rx="3" ry="3" />
|
38 |
<text x="135" y="81" fill="#6b21a8" fontSize="10" fontWeight="bold">Music</text>
|
39 |
+
<text x="200" y="81" fill="#6b21a8" fontSize="10" textAnchor="end">0.050</text>
|
40 |
</svg>
|
41 |
);
|
42 |
};
|
next.config.mjs
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
/** @type {import('next').NextConfig} */
|
2 |
const nextConfig = {
|
3 |
-
output:
|
4 |
-
|
5 |
};
|
6 |
|
7 |
export default nextConfig;
|
|
|
1 |
/** @type {import('next').NextConfig} */
|
2 |
const nextConfig = {
|
3 |
+
output: "standalone",
|
4 |
+
reactStrictMode: true,
|
5 |
};
|
6 |
|
7 |
export default nextConfig;
|
types/categories.ts
CHANGED
@@ -10,4 +10,5 @@ export interface Category {
|
|
10 |
status: CategoryStatus;
|
11 |
colorName: string;
|
12 |
graphic: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
|
|
13 |
}
|
|
|
10 |
status: CategoryStatus;
|
11 |
colorName: string;
|
12 |
graphic: React.ComponentType<React.SVGProps<SVGSVGElement>>;
|
13 |
+
demos?: React.ComponentType[];
|
14 |
}
|