File size: 8,115 Bytes
85a4687 96812c9 85a4687 9283c8b 85a4687 9283c8b 85a4687 9283c8b 85a4687 9283c8b 96812c9 85a4687 9283c8b 96812c9 85a4687 96812c9 85a4687 9283c8b 96812c9 85a4687 ad5cef3 85a4687 96812c9 85a4687 e7ba29d 85a4687 e7ba29d 85a4687 96812c9 ad5cef3 96812c9 9283c8b 96812c9 daa5539 85a4687 96812c9 85a4687 96812c9 85a4687 96812c9 85a4687 9283c8b 96812c9 85a4687 96812c9 85a4687 96812c9 85a4687 96812c9 85a4687 673d22a ad5cef3 daa5539 85a4687 96812c9 673d22a 85a4687 ad5cef3 85a4687 96812c9 85a4687 9283c8b 96812c9 85a4687 96812c9 85a4687 9283c8b 96812c9 85a4687 96812c9 85a4687 08476ef 85a4687 9283c8b 96812c9 85a4687 9283c8b 85a4687 96812c9 85a4687 96812c9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
// src/App.tsx
import { useState, useRef, useEffect, useCallback } from 'react'
import {
Section,
WorkerMessage,
ZeroShotWorkerInput,
} from '../types'
import { useModel } from '../contexts/ModelContext'
const PLACEHOLDER_REVIEWS: string[] = [
// battery/charging problems
'Disappointed with the battery life! The phone barely lasts half a day with regular use. Considering how much I paid for it, I expected better performance in this department.',
"I bought this phone a week ago, and I'm already frustrated with the battery life. It barely lasts half a day with normal usage. I expected more from a supposedly high-end device",
"The charging port is so finicky. Sometimes it takes forever to charge, and other times it doesn't even recognize the charger. Frustrating experience!",
// overheating
"This phone heats up way too quickly, especially when using demanding apps. It's uncomfortable to hold, and I'm concerned it might damage the internal components over time. Not what I expected",
"This phone is like holding a hot potato. Video calls turn it into a scalding nightmare. Seriously, can't it keep its cool?",
"Forget about a heatwave outside; my phone's got its own. It's like a little portable heater. Not what I signed up for.",
// poor build quality
'I dropped the phone from a short distance, and the screen cracked easily. Not as durable as I expected from a flagship device.',
'Took a slight bump in my bag, and the frame got dinged. Are we back in the flip phone era?',
"So, my phone's been in my pocket with just keys – no ninja moves or anything. Still, it managed to get some scratches. Disappointed with the build quality.",
// software
'The software updates are a nightmare. Each update seems to introduce new bugs, and it takes forever for them to be fixed.',
'Constant crashes and freezes make me want to throw it into a black hole.',
"Every time I open Instagram, my phone freezes and crashes. It's so frustrating!",
// other
"I'm not sure what to make of this phone. It's not bad, but it's not great either. I'm on the fence about it.",
"I hate the color of this phone. It's so ugly!",
"This phone sucks! I'm returning it."
].sort(() => Math.random() - 0.5)
const PLACEHOLDER_SECTIONS: string[] = [
'Battery and charging problems',
'Overheating',
'Poor build quality',
'Software issues',
'Other'
]
function ZeroShotClassification() {
const [text, setText] = useState<string>(PLACEHOLDER_REVIEWS.join('\n'))
const [sections, setSections] = useState<Section[]>(
PLACEHOLDER_SECTIONS.map((title) => ({ title, items: [] }))
)
const { status, setStatus, modelInfo } = useModel()
// Create a reference to the worker object.
const worker = useRef<Worker | null>(null)
// We use the `useEffect` hook to setup the worker as soon as the `App` component is mounted.
useEffect(() => {
if (!worker.current) {
return
// Create the worker if it does not yet exist.
// worker.current = new Worker(
// new URL('../workers/zero-shot-classification.js', import.meta.url),
// {
// type: 'module'
// }
// )
}
// Create a callback function for messages from the worker thread.
const onMessageReceived = (e: MessageEvent<WorkerMessage>) => {
const status = e.data.status
if (status === 'ready') {
setStatus('ready')
} else if (status === 'output') {
setStatus('output')
const { sequence, labels, scores } = e.data.output!
// Threshold for classification
const label = scores[0] > 0.5 ? labels[0] : 'Other'
const sectionID =
sections.map((x) => x.title).indexOf(label) ?? sections.length - 1
setSections((sections) => {
const newSections = [...sections]
newSections[sectionID] = {
...newSections[sectionID],
items: [...newSections[sectionID].items, sequence]
}
return newSections
})
} else if (status === 'error') {
setStatus('error')
console.error(e.data.output)
}
}
// Attach the callback function as an event listener.
worker.current.addEventListener('message', onMessageReceived)
// Define a cleanup function for when the component is unmounted.
return () =>
worker.current?.removeEventListener('message', onMessageReceived)
}, [sections])
const classify = useCallback(() => {
if (!modelInfo) return
setStatus('loading')
const message: ZeroShotWorkerInput = {
text,
labels: sections
.slice(0, sections.length - 1)
.map((section) => section.title),
model: modelInfo.name
}
worker.current?.postMessage(message)
}, [text, sections, modelInfo])
const busy: boolean = status !== 'ready'
const handleAddCategory = (): void => {
setSections((sections) => {
const newSections = [...sections]
// add at position 2 from the end
newSections.splice(newSections.length - 1, 0, {
title: 'New Category',
items: []
})
return newSections
})
}
const handleRemoveCategory = (): void => {
setSections((sections) => {
const newSections = [...sections]
newSections.splice(newSections.length - 2, 1) // Remove second last element
return newSections
})
}
const handleClear = (): void => {
setSections((sections) =>
sections.map((section) => ({
...section,
items: []
}))
)
}
const handleSectionTitleChange = (index: number, newTitle: string): void => {
setSections((sections) => {
const newSections = [...sections]
newSections[index].title = newTitle
return newSections
})
}
return (
<div className="flex flex-col h-screen w-full p-1">
<textarea
className="border w-full p-1 h-1/2"
value={text}
onChange={(e) => setText(e.target.value)}
></textarea>
<div className="flex flex-col justify-center items-center m-2 gap-1">
<button
className="border py-1 px-2 bg-blue-400 rounded text-white text-lg font-medium disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer"
disabled={busy}
onClick={classify}
>
{!busy
? 'Categorize'
: status === 'loading'
? 'Model loading...'
: 'Processing'}
</button>
<div className="flex gap-1">
<button
className="border py-1 px-2 bg-green-400 rounded text-white text-sm font-medium cursor-pointer"
onClick={handleAddCategory}
>
Add category
</button>
<button
className="border py-1 px-2 bg-red-400 rounded text-white text-sm font-medium cursor-pointer"
disabled={sections.length <= 1}
onClick={handleRemoveCategory}
>
Remove category
</button>
<button
className="border py-1 px-2 bg-orange-400 rounded text-white text-sm font-medium cursor-pointer"
onClick={handleClear}
>
Clear
</button>
</div>
</div>
<div className="flex justify-between flex-grow overflow-x-auto max-h-[40%]">
{sections.map((section, index) => (
<div key={index} className="flex flex-col w-full">
<input
disabled={section.title === 'Other'}
className="w-full border px-1 text-center"
value={section.title}
onChange={(e) => handleSectionTitleChange(index, e.target.value)}
></input>
<div className="overflow-y-auto h-full border">
{section.items.map((item, itemIndex) => (
<div
className="m-2 border bg-red-50 rounded p-1 text-sm"
key={itemIndex}
>
{item}
</div>
))}
</div>
</div>
))}
</div>
</div>
)
}
export default ZeroShotClassification
|