samyosm commited on
Commit
f43196f
·
1 Parent(s): f5d25f5
app/(index)/SentimentList.tsx CHANGED
@@ -1,18 +1,16 @@
1
  'use client'
2
  import { SentimentDisplay } from "@/components/sentiment-display/SentimentDisplay";
3
- import { use } from "react";
4
 
5
  export interface ISentimentList {
6
- dataPromise: Promise<unknown>;
7
  }
8
 
9
- export function SentimentList({ dataPromise }: ISentimentList) {
10
- const out = use(dataPromise);
11
-
12
  return (
13
  <div className="flex flex-col gap-8 p-12 overflow-y-auto">
14
  {Array.isArray(out) ?
15
- out.map(({ label, score }: { label: string, score: number }) =>
16
  <SentimentDisplay key={label} label={label} value={score} />
17
  ) :
18
  <p>Begin to write to see results!</p>
 
1
  'use client'
2
  import { SentimentDisplay } from "@/components/sentiment-display/SentimentDisplay";
3
+ import { ClassifyOutputElement } from "@/util/classify";
4
 
5
  export interface ISentimentList {
6
+ out?: ClassifyOutputElement[];
7
  }
8
 
9
+ export function SentimentList({ out }: ISentimentList) {
 
 
10
  return (
11
  <div className="flex flex-col gap-8 p-12 overflow-y-auto">
12
  {Array.isArray(out) ?
13
+ out.map(({ label, score }) =>
14
  <SentimentDisplay key={label} label={label} value={score} />
15
  ) :
16
  <p>Begin to write to see results!</p>
app/(index)/SentimentWidget.tsx CHANGED
@@ -1,30 +1,22 @@
1
  'use client'
2
  import { TitleBar } from "@/components/title-bar/TitleBar";
3
  import { useTextStore } from "./TextStore";
4
- import { Suspense } from "react";
5
  import { SentimentList } from "./SentimentList";
6
  import { Fallback } from "@/components/fallback/Fallback";
7
  import Link from "next/link";
8
-
9
- async function getData(text: string) {
10
- const raw = await fetch('/api/classify', {
11
- method: 'POST',
12
- headers: {
13
- 'Accept': 'application/json',
14
- 'Content-Type': 'application/json'
15
- },
16
- body: JSON.stringify({
17
- text,
18
- })
19
- });
20
-
21
- return raw.json();
22
-
23
- }
24
 
25
  export function SentimentWidget() {
26
  const { text } = useTextStore();
27
 
 
 
 
 
 
 
 
28
  return (
29
  <section className="w-full max-w-2xl h-full flex flex-col">
30
  <TitleBar label="Sentiment">
@@ -36,9 +28,8 @@ export function SentimentWidget() {
36
  How?
37
  </Link>
38
  </TitleBar>
39
- <Suspense fallback={<Fallback />}>
40
- <SentimentList dataPromise={getData(text)} />
41
- </Suspense>
42
  </section>
43
 
44
  )
 
1
  'use client'
2
  import { TitleBar } from "@/components/title-bar/TitleBar";
3
  import { useTextStore } from "./TextStore";
 
4
  import { SentimentList } from "./SentimentList";
5
  import { Fallback } from "@/components/fallback/Fallback";
6
  import Link from "next/link";
7
+ import { classify } from "@/util/classify";
8
+ import { useQuery } from "@tanstack/react-query";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  export function SentimentWidget() {
11
  const { text } = useTextStore();
12
 
13
+ const { isPending, data: out } = useQuery({
14
+ queryKey: ['text', text],
15
+ queryFn: () =>
16
+ classify(text),
17
+ })
18
+
19
+
20
  return (
21
  <section className="w-full max-w-2xl h-full flex flex-col">
22
  <TitleBar label="Sentiment">
 
28
  How?
29
  </Link>
30
  </TitleBar>
31
+ {isPending ? <Fallback /> : <SentimentList out={out} />}
32
+
 
33
  </section>
34
 
35
  )
app/(index)/TextWidget.tsx CHANGED
@@ -22,7 +22,7 @@ export function TextWidget() {
22
  <textarea
23
  onInput={(e) => debounced(e.currentTarget.value)}
24
  defaultValue={text}
25
- className="w-full h-full outline-none p-12 text-lg"
26
  placeholder="Begin writing..."
27
  />
28
  </section>
 
22
  <textarea
23
  onInput={(e) => debounced(e.currentTarget.value)}
24
  defaultValue={text}
25
+ className="w-full min-h-52 md:min-h-0 h-full outline-none p-12 text-lg"
26
  placeholder="Begin writing..."
27
  />
28
  </section>
app/(index)/page.tsx CHANGED
@@ -1,11 +1,20 @@
 
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
  }
 
1
+ 'use client'
2
  import { SentimentWidget } from "./SentimentWidget";
3
  import { TextWidget } from "./TextWidget";
4
+ import {
5
+ QueryClient,
6
+ QueryClientProvider,
7
+ } from '@tanstack/react-query'
8
+
9
+ const queryClient = new QueryClient();
10
 
11
  export default function Home() {
12
  return (
13
+ <QueryClientProvider client={queryClient}>
14
+ <div className="flex flex-col md:flex-row h-screen divide-x divide-zinc-200">
15
+ <TextWidget />
16
+ <SentimentWidget />
17
+ </div>
18
+ </QueryClientProvider>
19
  );
20
  }
app/api/classify/route.ts CHANGED
@@ -10,7 +10,9 @@ export async function POST(request: NextRequest) {
10
  }
11
  const classifier = await PipelineSingleton.getInstance();
12
 
 
13
  const result = await classifier(text, { top_k: null });
14
 
 
15
  return NextResponse.json(result);
16
  }
 
10
  }
11
  const classifier = await PipelineSingleton.getInstance();
12
 
13
+ //@ts-expect-error That's the library
14
  const result = await classifier(text, { top_k: null });
15
 
16
+
17
  return NextResponse.json(result);
18
  }
components/title-bar/TitleBar.tsx CHANGED
@@ -7,7 +7,10 @@ export interface ITitleBar extends React.ComponentPropsWithoutRef<'div'> {
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 >
 
7
 
8
  export function TitleBar({ label, children, className, ...rest }: ITitleBar) {
9
  return (
10
+ <div
11
+ className={cn("p-12 flex items-center justify-between border-y md:border-t-0 bg-zinc-50 md:bg-white border-zinc-200", className)}
12
+ {...rest}
13
+ >
14
  <p className="font-medium text-xl">{label}</p>
15
  {children}
16
  </div >
next.config.ts CHANGED
@@ -1,8 +1,24 @@
1
  import type { NextConfig } from "next";
2
 
 
 
 
 
 
 
3
  const nextConfig: NextConfig = {
4
  /* config options here */
5
  serverExternalPackages: ['sharp', 'onnxruntime-node'],
 
 
 
 
 
 
 
 
 
 
6
  };
7
 
8
  export default nextConfig;
 
1
  import type { NextConfig } from "next";
2
 
3
+ import path from "path";
4
+ import { fileURLToPath } from 'url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
  const nextConfig: NextConfig = {
10
  /* config options here */
11
  serverExternalPackages: ['sharp', 'onnxruntime-node'],
12
+ webpack: (config) => {
13
+ config.resolve.alias['@huggingface/transformers'] = path.resolve(__dirname, 'node_modules/@huggingface/transformers');
14
+ config.resolve.alias = {
15
+ ...config.resolve.alias,
16
+ "sharp$": false,
17
+ "onnxruntime-node$": false,
18
+ }
19
+ return config;
20
+ },
21
+
22
  };
23
 
24
  export default nextConfig;
package.json CHANGED
@@ -10,13 +10,13 @@
10
  },
11
  "dependencies": {
12
  "@huggingface/transformers": "^3.2.4",
 
13
  "clsx": "^2.1.1",
14
  "copy-webpack-plugin": "^12.0.2",
15
  "next": "15.1.3",
16
  "node-polyfill-webpack-plugin": "^4.1.0",
17
  "react": "^19.0.0",
18
  "react-dom": "^19.0.0",
19
- "swr": "^2.3.0",
20
  "use-debounce": "^10.0.4",
21
  "words-count": "^2.0.2",
22
  "zustand": "^5.0.2"
 
10
  },
11
  "dependencies": {
12
  "@huggingface/transformers": "^3.2.4",
13
+ "@tanstack/react-query": "^5.62.15",
14
  "clsx": "^2.1.1",
15
  "copy-webpack-plugin": "^12.0.2",
16
  "next": "15.1.3",
17
  "node-polyfill-webpack-plugin": "^4.1.0",
18
  "react": "^19.0.0",
19
  "react-dom": "^19.0.0",
 
20
  "use-debounce": "^10.0.4",
21
  "words-count": "^2.0.2",
22
  "zustand": "^5.0.2"
pipeline.ts CHANGED
@@ -21,9 +21,12 @@ if (process.env.NODE_ENV !== 'production') {
21
  // When running in development mode, attach the pipeline to the
22
  // global object so that it's preserved between hot reloads.
23
  // For more information, see https://vercel.com/guides/nextjs-prisma-postgres
 
24
  if (!global.PipelineSingleton) {
 
25
  global.PipelineSingleton = P();
26
  }
 
27
  PipelineSingleton = global.PipelineSingleton;
28
  } else {
29
  PipelineSingleton = P();
 
21
  // When running in development mode, attach the pipeline to the
22
  // global object so that it's preserved between hot reloads.
23
  // For more information, see https://vercel.com/guides/nextjs-prisma-postgres
24
+ //@ts-expect-error I don't know how to define globalThis
25
  if (!global.PipelineSingleton) {
26
+ //@ts-expect-error I don't know how to define globalThis
27
  global.PipelineSingleton = P();
28
  }
29
+ //@ts-expect-error I don't know how to define globalThis
30
  PipelineSingleton = global.PipelineSingleton;
31
  } else {
32
  PipelineSingleton = P();
util/classify.ts ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use server'
2
+ import PipelineSingleton from '@/pipeline';
3
+
4
+ export interface ClassifyOutputElement {
5
+ label: string;
6
+ score: number;
7
+ }
8
+
9
+ export async function classify(text: string) {
10
+ const classifier = await PipelineSingleton.getInstance();
11
+
12
+ //@ts-expect-error That's the library
13
+ const out = await classifier(text, { top_k: null });
14
+ return out as ClassifyOutputElement[]
15
+ }
yarn.lock CHANGED
@@ -444,6 +444,18 @@
444
  dependencies:
445
  tslib "^2.8.0"
446
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  "@types/estree@^1.0.6":
448
  version "1.0.6"
449
  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
@@ -1231,11 +1243,6 @@ define-properties@^1.1.3, define-properties@^1.2.1:
1231
  has-property-descriptors "^1.0.0"
1232
  object-keys "^1.1.1"
1233
 
1234
- dequal@^2.0.3:
1235
- version "2.0.3"
1236
- resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
1237
- integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
1238
-
1239
  des.js@^1.0.0:
1240
  version "1.1.0"
1241
  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da"
@@ -3562,14 +3569,6 @@ supports-preserve-symlinks-flag@^1.0.0:
3562
  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
3563
  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
3564
 
3565
- swr@^2.3.0:
3566
- version "2.3.0"
3567
- resolved "https://registry.yarnpkg.com/swr/-/swr-2.3.0.tgz#66fa45023efd4199f4e7ce608c255709a135943d"
3568
- integrity sha512-NyZ76wA4yElZWBHzSgEJc28a0u6QZvhb6w0azeL2k7+Q1gAzVK+IqQYXhVOC/mzi+HZIozrZvBVeSeOZNR2bqA==
3569
- dependencies:
3570
- dequal "^2.0.3"
3571
- use-sync-external-store "^1.4.0"
3572
-
3573
  tailwindcss@^3.4.1:
3574
  version "3.4.17"
3575
  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63"
@@ -3780,11 +3779,6 @@ use-debounce@^10.0.4:
3780
  resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-10.0.4.tgz#2135be498ad855416c4495cfd8e0e130bd33bb24"
3781
  integrity sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==
3782
 
3783
- use-sync-external-store@^1.4.0:
3784
- version "1.4.0"
3785
- resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc"
3786
- integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==
3787
-
3788
  util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
3789
  version "1.0.2"
3790
  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
 
444
  dependencies:
445
  tslib "^2.8.0"
446
 
447
+ "@tanstack/query-core@5.62.15":
448
+ version "5.62.15"
449
+ resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.62.15.tgz#ded0267ac31bd23f3c45ffc008dba85a25621e91"
450
+ integrity sha512-wT20X14CxcWY8YLJ/1pnsXn/y1Q2uRJZYWW93PWRtZt+3/JlGZyiyTcO4pGnqycnP7CokCROAyatsraosqZsDA==
451
+
452
+ "@tanstack/react-query@^5.62.15":
453
+ version "5.62.15"
454
+ resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.62.15.tgz#ecfe7187913e36c36630afedf4f8eff37247c400"
455
+ integrity sha512-Ny3xxsOWmEQCFyHiV3CF7t6+QAV+LpBEREiXyllKR4+tStyd8smOAa98ZHmEx0ZNy36M31K8enifB5wTSYAKJw==
456
+ dependencies:
457
+ "@tanstack/query-core" "5.62.15"
458
+
459
  "@types/estree@^1.0.6":
460
  version "1.0.6"
461
  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
 
1243
  has-property-descriptors "^1.0.0"
1244
  object-keys "^1.1.1"
1245
 
 
 
 
 
 
1246
  des.js@^1.0.0:
1247
  version "1.1.0"
1248
  resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da"
 
3569
  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
3570
  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
3571
 
 
 
 
 
 
 
 
 
3572
  tailwindcss@^3.4.1:
3573
  version "3.4.17"
3574
  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63"
 
3779
  resolved "https://registry.yarnpkg.com/use-debounce/-/use-debounce-10.0.4.tgz#2135be498ad855416c4495cfd8e0e130bd33bb24"
3780
  integrity sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==
3781
 
 
 
 
 
 
3782
  util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
3783
  version "1.0.2"
3784
  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"