Comprehend.jsx
6564 Chars | 237 Lines | 6.57 kb | Text File
Parent Directory
import React, { useState, useReducer, useContext, useRef } from 'react'
import { useShallow } from 'zustand/react/shallow'
import { usePack } from '@/stores/pack'
import { Experience } from '../Experience'
import { useReactToPrint } from 'react-to-print'
import Table from 'react-bootstrap/Table'
import Button from 'react-bootstrap/Button'
import Spinner from 'react-bootstrap/Spinner'
import Form from 'react-bootstrap/Form'
import { Markup } from '@/lib/Markup'
import nlp from 'compromise'
import { v4 as uuidv4 } from 'uuid'
import LLMPipeline from '../LLMPipeline'
import { ThemeContext } from '@/lib/context'
const reducer = (state, action) => {
return { ...state, [action.i]: action.q }
}
function ComprehendComponent() {
const [pack] = usePack(useShallow((state) => [state.pack]))
const [questions, setQuestions] = useReducer(reducer, {})
const [generating, setGenerating] = useState(false)
const [showNumber, setShowNumber] = useState(true)
const [showTerm, setShowTerm] = useState(true)
const [showDefinition, setShowDefinition] = useState(true)
const [showSummary, setShowSummary] = useState(true)
const [showQuestions, setShowQuestions] = useState(true)
const [showAnswerBox, setShowAnswerBox] = useState(false)
const printRef = useRef()
const print = useReactToPrint({
content: () => printRef.current,
})
const theme = useContext(ThemeContext)
// Must be a single sentence
// TODO: implement https://github.com/spencermountain/compromise/issues/388#issuecomment-1602804777
function minifyDefinition(sentence, includeSubject = false) {
// Strip HTML
sentence = sentence.replace(/<[^>]*>?/gm, '')
// Convert to present tense
let doc = nlp(nlp(sentence).sentences().toPresentTense().text())
var output = ''
const and = uuidv4()
// Get the parts of the sentence
// let parts = doc.sentences().json()[0].sentence
doc.sentences()
.json()
.forEach((e, i) => {
if (e.sentence.predicate.length > 2 && e.sentence.verb.length > 2) {
if (includeSubject) {
output +=
(i > 0 ? and : '') + e.sentence.subject + ' is ' + e.sentence.predicate
} else {
output += (i > 0 ? and : '') + e.sentence.verb + ' ' + e.sentence.predicate
}
}
})
// Remove the and placeholder if it is at the start or end of the string
if (output.trim().startsWith(`${and} `)) output = output.substring(5)
if (output.trim().endsWith(` ${and}`)) output = output.substring(0, output.length - 5)
// Capitalize the first letter
output = output.charAt(0).toUpperCase() + output.slice(1)
output = output.replaceAll(and, ' and ')
return output
}
// function toPresentTense(text) {
// text = text.replace(/<[^>]*>?/gm, '')
// let doc = nlp(text)
// // ignore spaces when calculating equality
// if (
// doc.sentences().toPresentTense().text().trim().replaceAll(' ', '') !=
// text.trim().replaceAll(' ', '')
// ) {
// return text
// }
// }
async function generateQuestions() {
const questionizer = await LLMPipeline.getInstance()
for (const [index, card] of pack.content.entries()) {
setQuestions({ i: index, q: await questionizer(card.definition, card.term) })
}
}
return (
<>
<div className="text-center">
<h1>Comprehend</h1>
<p className="mb-3">
Generate cheat sheets, summaries, or worksheets using Artificial Intelligence
and Natural Language Processing
</p>
<div className="d-flex justify-content-center">
<Button
variant={theme.dark ? 'light' : 'dark'}
size="lg"
className="mb-3 me-2"
onClick={async () => {
setGenerating(true)
setTimeout(async () => {
await generateQuestions()
setGenerating(false)
}, 500)
}}
>
{generating ? (
<>
<Spinner size="sm" /> Generating (can take up to a minute)
</>
) : (
'🏭 Generate Questions'
)}
</Button>
<Button
size="lg"
className="mb-3"
variant={theme.dark ? 'light' : 'dark'}
onClick={print}
>
🖨️ Print
</Button>
</div>
</div>
<div className="mb-2">
<Form.Check
type="checkbox"
label="Number"
inline
checked={showNumber}
onChange={(e) => setShowNumber(e.target.checked)}
/>
<Form.Check
type="checkbox"
label="Term"
inline
checked={showTerm}
onChange={(e) => setShowTerm(e.target.checked)}
/>
<Form.Check
type="checkbox"
label="Definition"
inline
checked={showDefinition}
onChange={(e) => setShowDefinition(e.target.checked)}
/>
<Form.Check
type="checkbox"
label="Summary"
inline
checked={showSummary}
onChange={(e) => setShowSummary(e.target.checked)}
/>
<Form.Check
type="checkbox"
label="Questions"
inline
checked={showQuestions}
onChange={(e) => setShowQuestions(e.target.checked)}
/>
<Form.Check
type="checkbox"
label="Answer Box"
inline
checked={showAnswerBox}
onChange={(e) => setShowAnswerBox(e.target.checked)}
/>
</div>
<Table striped bordered className="mx-auto" ref={printRef}>
<thead>
<tr>
{showNumber && <th>#</th>}
{showTerm && <th>Term</th>}
{showDefinition && <th>Definition</th>}
{showSummary && <th>Summary</th>}
{showQuestions && <th>Questions</th>}
{showAnswerBox && <th>Answer Box</th>}
</tr>
</thead>
<tbody>
{pack.content.map((card, index) => {
return (
<tr key={index}>
{showNumber && <td>{index + 1}</td>}
{showTerm && (
<td>
<Markup content={card.term} />
</td>
)}
{showDefinition && (
<td>
<Markup content={card.definition} />
</td>
)}
{showSummary && <td>{minifyDefinition(card.definition)}</td>}
{showQuestions && <td>{questions[index]}</td>}
{showAnswerBox && (
<td className="w-25">
{[...Array(10)].map((_, i) => (
<br key={i} />
))}
</td>
)}
</tr>
)
})}
</tbody>
</Table>
</>
)
}
const Comprehend = function () {
return (
<Experience name="comprehend">
<ComprehendComponent />
</Experience>
)
}
export default Comprehend