basic features
Browse files- app/(index)/SentimentWidget.tsx +24 -0
- app/(index)/TextWidget.tsx +12 -0
- app/(index)/page.tsx +11 -0
- app/favicon.ico +0 -0
- app/layout.tsx +5 -11
- app/page.tsx +0 -101
- components/sentiment-display/SentimentDisplay.tsx +27 -0
- components/title-bar/TitleBar.tsx +15 -0
- package.json +7 -6
- pnpm-lock.yaml +9 -0
app/(index)/SentimentWidget.tsx
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { SentimentDisplay } from "@/components/sentiment-display/SentimentDisplay";
|
| 2 |
+
import { TitleBar } from "@/components/title-bar/TitleBar";
|
| 3 |
+
|
| 4 |
+
export function SentimentWidget() {
|
| 5 |
+
return (
|
| 6 |
+
<section className="w-full max-w-2xl">
|
| 7 |
+
<TitleBar label="Sentiment">
|
| 8 |
+
<p
|
| 9 |
+
className="border-b border-b-zinc-200 border-dotted"
|
| 10 |
+
title="This widget uses an AI"
|
| 11 |
+
>
|
| 12 |
+
How?
|
| 13 |
+
</p>
|
| 14 |
+
</TitleBar>
|
| 15 |
+
<div className="flex flex-col gap-8 p-12">
|
| 16 |
+
<SentimentDisplay label="confusion" value={1} />
|
| 17 |
+
<SentimentDisplay label="fear" value={0.50} />
|
| 18 |
+
<SentimentDisplay label="fear" value={0.532} />
|
| 19 |
+
<SentimentDisplay label="fear" value={0.0001} />
|
| 20 |
+
</div>
|
| 21 |
+
</section>
|
| 22 |
+
|
| 23 |
+
)
|
| 24 |
+
}
|
app/(index)/TextWidget.tsx
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { TitleBar } from "@/components/title-bar/TitleBar";
|
| 2 |
+
|
| 3 |
+
export function TextWidget() {
|
| 4 |
+
return (
|
| 5 |
+
<section className="w-full">
|
| 6 |
+
<TitleBar label="Text">
|
| 7 |
+
30 words
|
| 8 |
+
</TitleBar>
|
| 9 |
+
</section>
|
| 10 |
+
|
| 11 |
+
)
|
| 12 |
+
}
|
app/(index)/page.tsx
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { SentimentWidget } from "./SentimentWidget";
|
| 2 |
+
import { TextWidget } from "./TextWidget";
|
| 3 |
+
|
| 4 |
+
export default function Home() {
|
| 5 |
+
return (
|
| 6 |
+
<div className="flex h-screen divide-x divide-zinc-200">
|
| 7 |
+
<TextWidget />
|
| 8 |
+
<SentimentWidget />
|
| 9 |
+
</div>
|
| 10 |
+
);
|
| 11 |
+
}
|
app/favicon.ico
DELETED
|
Binary file (25.9 kB)
|
|
|
app/layout.tsx
CHANGED
|
@@ -1,20 +1,14 @@
|
|
| 1 |
import type { Metadata } from "next";
|
| 2 |
-
import {
|
| 3 |
import "./globals.css";
|
| 4 |
|
| 5 |
-
const
|
| 6 |
-
variable: "--font-geist-sans",
|
| 7 |
-
subsets: ["latin"],
|
| 8 |
-
});
|
| 9 |
-
|
| 10 |
-
const geistMono = Geist_Mono({
|
| 11 |
-
variable: "--font-geist-mono",
|
| 12 |
subsets: ["latin"],
|
| 13 |
});
|
| 14 |
|
| 15 |
export const metadata: Metadata = {
|
| 16 |
-
title: "
|
| 17 |
-
description: "
|
| 18 |
};
|
| 19 |
|
| 20 |
export default function RootLayout({
|
|
@@ -25,7 +19,7 @@ export default function RootLayout({
|
|
| 25 |
return (
|
| 26 |
<html lang="en">
|
| 27 |
<body
|
| 28 |
-
className={`${
|
| 29 |
>
|
| 30 |
{children}
|
| 31 |
</body>
|
|
|
|
| 1 |
import type { Metadata } from "next";
|
| 2 |
+
import { Space_Grotesk as Font } from "next/font/google";
|
| 3 |
import "./globals.css";
|
| 4 |
|
| 5 |
+
const font = Font({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
subsets: ["latin"],
|
| 7 |
});
|
| 8 |
|
| 9 |
export const metadata: Metadata = {
|
| 10 |
+
title: "Analyzo",
|
| 11 |
+
description: "Analyze the sentiment of your text.",
|
| 12 |
};
|
| 13 |
|
| 14 |
export default function RootLayout({
|
|
|
|
| 19 |
return (
|
| 20 |
<html lang="en">
|
| 21 |
<body
|
| 22 |
+
className={`${font.className} antialiased`}
|
| 23 |
>
|
| 24 |
{children}
|
| 25 |
</body>
|
app/page.tsx
DELETED
|
@@ -1,101 +0,0 @@
|
|
| 1 |
-
import Image from "next/image";
|
| 2 |
-
|
| 3 |
-
export default function Home() {
|
| 4 |
-
return (
|
| 5 |
-
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
| 6 |
-
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
| 7 |
-
<Image
|
| 8 |
-
className="dark:invert"
|
| 9 |
-
src="/next.svg"
|
| 10 |
-
alt="Next.js logo"
|
| 11 |
-
width={180}
|
| 12 |
-
height={38}
|
| 13 |
-
priority
|
| 14 |
-
/>
|
| 15 |
-
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
| 16 |
-
<li className="mb-2">
|
| 17 |
-
Get started by editing{" "}
|
| 18 |
-
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
| 19 |
-
app/page.tsx
|
| 20 |
-
</code>
|
| 21 |
-
.
|
| 22 |
-
</li>
|
| 23 |
-
<li>Save and see your changes instantly.</li>
|
| 24 |
-
</ol>
|
| 25 |
-
|
| 26 |
-
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
| 27 |
-
<a
|
| 28 |
-
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
| 29 |
-
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
| 30 |
-
target="_blank"
|
| 31 |
-
rel="noopener noreferrer"
|
| 32 |
-
>
|
| 33 |
-
<Image
|
| 34 |
-
className="dark:invert"
|
| 35 |
-
src="/vercel.svg"
|
| 36 |
-
alt="Vercel logomark"
|
| 37 |
-
width={20}
|
| 38 |
-
height={20}
|
| 39 |
-
/>
|
| 40 |
-
Deploy now
|
| 41 |
-
</a>
|
| 42 |
-
<a
|
| 43 |
-
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
| 44 |
-
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
| 45 |
-
target="_blank"
|
| 46 |
-
rel="noopener noreferrer"
|
| 47 |
-
>
|
| 48 |
-
Read our docs
|
| 49 |
-
</a>
|
| 50 |
-
</div>
|
| 51 |
-
</main>
|
| 52 |
-
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
| 53 |
-
<a
|
| 54 |
-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
| 55 |
-
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
| 56 |
-
target="_blank"
|
| 57 |
-
rel="noopener noreferrer"
|
| 58 |
-
>
|
| 59 |
-
<Image
|
| 60 |
-
aria-hidden
|
| 61 |
-
src="/file.svg"
|
| 62 |
-
alt="File icon"
|
| 63 |
-
width={16}
|
| 64 |
-
height={16}
|
| 65 |
-
/>
|
| 66 |
-
Learn
|
| 67 |
-
</a>
|
| 68 |
-
<a
|
| 69 |
-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
| 70 |
-
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
| 71 |
-
target="_blank"
|
| 72 |
-
rel="noopener noreferrer"
|
| 73 |
-
>
|
| 74 |
-
<Image
|
| 75 |
-
aria-hidden
|
| 76 |
-
src="/window.svg"
|
| 77 |
-
alt="Window icon"
|
| 78 |
-
width={16}
|
| 79 |
-
height={16}
|
| 80 |
-
/>
|
| 81 |
-
Examples
|
| 82 |
-
</a>
|
| 83 |
-
<a
|
| 84 |
-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
| 85 |
-
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
| 86 |
-
target="_blank"
|
| 87 |
-
rel="noopener noreferrer"
|
| 88 |
-
>
|
| 89 |
-
<Image
|
| 90 |
-
aria-hidden
|
| 91 |
-
src="/globe.svg"
|
| 92 |
-
alt="Globe icon"
|
| 93 |
-
width={16}
|
| 94 |
-
height={16}
|
| 95 |
-
/>
|
| 96 |
-
Go to nextjs.org →
|
| 97 |
-
</a>
|
| 98 |
-
</footer>
|
| 99 |
-
</div>
|
| 100 |
-
);
|
| 101 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
components/sentiment-display/SentimentDisplay.tsx
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export interface ISentimentDisplay {
|
| 2 |
+
label: string;
|
| 3 |
+
value: number;
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
export function SentimentDisplay({ label, value }: ISentimentDisplay) {
|
| 7 |
+
const printed_percentage = (value * 100).toFixed(2).padStart(5, "0");
|
| 8 |
+
const width_percentage = Math.max(value * 100, 2)
|
| 9 |
+
return (
|
| 10 |
+
<div className="flex flex-col gap-1.5">
|
| 11 |
+
<div className="flex gap-3 items-baseline">
|
| 12 |
+
<p className="capitalize font-medium">
|
| 13 |
+
{label}
|
| 14 |
+
</p>
|
| 15 |
+
<p className="">
|
| 16 |
+
{printed_percentage}%
|
| 17 |
+
</p>
|
| 18 |
+
</div>
|
| 19 |
+
<div className="w-full">
|
| 20 |
+
<div
|
| 21 |
+
className="bg-gradient-to-r from-rose-400 to-rose-50 h-5 rounded-full"
|
| 22 |
+
style={{ width: `${width_percentage}%` }}
|
| 23 |
+
/>
|
| 24 |
+
</div>
|
| 25 |
+
</div>
|
| 26 |
+
)
|
| 27 |
+
}
|
components/title-bar/TitleBar.tsx
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import cn from "clsx";
|
| 2 |
+
|
| 3 |
+
export interface ITitleBar extends React.ComponentPropsWithoutRef<'div'> {
|
| 4 |
+
label: string;
|
| 5 |
+
children?: React.ReactNode;
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
export function TitleBar({ label, children, className, ...rest }: ITitleBar) {
|
| 9 |
+
return (
|
| 10 |
+
<div className={cn("p-12 flex items-center justify-between border-b border-b-zinc-200", className)} {...rest}>
|
| 11 |
+
<p className="font-medium text-xl">{label}</p>
|
| 12 |
+
{children}
|
| 13 |
+
</div >
|
| 14 |
+
)
|
| 15 |
+
}
|
package.json
CHANGED
|
@@ -9,20 +9,21 @@
|
|
| 9 |
"lint": "next lint"
|
| 10 |
},
|
| 11 |
"dependencies": {
|
|
|
|
|
|
|
| 12 |
"react": "^19.0.0",
|
| 13 |
-
"react-dom": "^19.0.0"
|
| 14 |
-
"next": "15.1.3"
|
| 15 |
},
|
| 16 |
"devDependencies": {
|
| 17 |
-
"
|
| 18 |
"@types/node": "^20",
|
| 19 |
"@types/react": "^19",
|
| 20 |
"@types/react-dom": "^19",
|
| 21 |
-
"postcss": "^8",
|
| 22 |
-
"tailwindcss": "^3.4.1",
|
| 23 |
"eslint": "^9",
|
| 24 |
"eslint-config-next": "15.1.3",
|
| 25 |
-
"
|
|
|
|
|
|
|
| 26 |
},
|
| 27 |
"packageManager": "pnpm@9.15.2+sha512.93e57b0126f0df74ce6bff29680394c0ba54ec47246b9cf321f0121d8d9bb03f750a705f24edc3c1180853afd7c2c3b94196d0a3d53d3e069d9e2793ef11f321"
|
| 28 |
}
|
|
|
|
| 9 |
"lint": "next lint"
|
| 10 |
},
|
| 11 |
"dependencies": {
|
| 12 |
+
"clsx": "^2.1.1",
|
| 13 |
+
"next": "15.1.3",
|
| 14 |
"react": "^19.0.0",
|
| 15 |
+
"react-dom": "^19.0.0"
|
|
|
|
| 16 |
},
|
| 17 |
"devDependencies": {
|
| 18 |
+
"@eslint/eslintrc": "^3",
|
| 19 |
"@types/node": "^20",
|
| 20 |
"@types/react": "^19",
|
| 21 |
"@types/react-dom": "^19",
|
|
|
|
|
|
|
| 22 |
"eslint": "^9",
|
| 23 |
"eslint-config-next": "15.1.3",
|
| 24 |
+
"postcss": "^8",
|
| 25 |
+
"tailwindcss": "^3.4.1",
|
| 26 |
+
"typescript": "^5"
|
| 27 |
},
|
| 28 |
"packageManager": "pnpm@9.15.2+sha512.93e57b0126f0df74ce6bff29680394c0ba54ec47246b9cf321f0121d8d9bb03f750a705f24edc3c1180853afd7c2c3b94196d0a3d53d3e069d9e2793ef11f321"
|
| 29 |
}
|
pnpm-lock.yaml
CHANGED
|
@@ -8,6 +8,9 @@ importers:
|
|
| 8 |
|
| 9 |
.:
|
| 10 |
dependencies:
|
|
|
|
|
|
|
|
|
|
| 11 |
next:
|
| 12 |
specifier: 15.1.3
|
| 13 |
version: 15.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
|
@@ -537,6 +540,10 @@ packages:
|
|
| 537 |
client-only@0.0.1:
|
| 538 |
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
| 539 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
color-convert@2.0.1:
|
| 541 |
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
| 542 |
engines: {node: '>=7.0.0'}
|
|
@@ -2156,6 +2163,8 @@ snapshots:
|
|
| 2156 |
|
| 2157 |
client-only@0.0.1: {}
|
| 2158 |
|
|
|
|
|
|
|
| 2159 |
color-convert@2.0.1:
|
| 2160 |
dependencies:
|
| 2161 |
color-name: 1.1.4
|
|
|
|
| 8 |
|
| 9 |
.:
|
| 10 |
dependencies:
|
| 11 |
+
clsx:
|
| 12 |
+
specifier: ^2.1.1
|
| 13 |
+
version: 2.1.1
|
| 14 |
next:
|
| 15 |
specifier: 15.1.3
|
| 16 |
version: 15.1.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
|
|
|
| 540 |
client-only@0.0.1:
|
| 541 |
resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
|
| 542 |
|
| 543 |
+
clsx@2.1.1:
|
| 544 |
+
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
| 545 |
+
engines: {node: '>=6'}
|
| 546 |
+
|
| 547 |
color-convert@2.0.1:
|
| 548 |
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
| 549 |
engines: {node: '>=7.0.0'}
|
|
|
|
| 2163 |
|
| 2164 |
client-only@0.0.1: {}
|
| 2165 |
|
| 2166 |
+
clsx@2.1.1: {}
|
| 2167 |
+
|
| 2168 |
color-convert@2.0.1:
|
| 2169 |
dependencies:
|
| 2170 |
color-name: 1.1.4
|