Snapshot
Browse files- frontend/esbuild.js +22 -16
- frontend/package.json +2 -1
- frontend/public/index.css +84 -0
- frontend/public/index.html +0 -1
- frontend/src/components/app.tsx +46 -17
- frontend/src/index.tsx +2 -1
frontend/esbuild.js
CHANGED
@@ -1,18 +1,24 @@
|
|
1 |
import * as esbuild from 'esbuild'
|
2 |
|
3 |
-
esbuild
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import * as esbuild from 'esbuild'
|
2 |
|
3 |
+
const ctx = await esbuild.context({
|
4 |
+
entryPoints: [
|
5 |
+
"./src/index.tsx",
|
6 |
+
],
|
7 |
+
bundle: true,
|
8 |
+
minify: false,
|
9 |
+
sourcemap: process.env.NODE_ENV !== "production",
|
10 |
+
target: ["chrome120", "firefox110"],
|
11 |
+
outdir: "public",
|
12 |
+
outbase: "src",
|
13 |
+
define: {
|
14 |
+
"process.env.NODE_ENV": `"${process.env.NODE_ENV}"`
|
15 |
+
}
|
16 |
+
})
|
17 |
+
|
18 |
+
if (process.argv.includes('--watch')) {
|
19 |
+
await ctx.watch()
|
20 |
+
console.log('Watching...')
|
21 |
+
} else {
|
22 |
+
await ctx.rebuild()
|
23 |
+
await ctx.dispose()
|
24 |
+
}
|
frontend/package.json
CHANGED
@@ -4,7 +4,8 @@
|
|
4 |
"main": "index.js",
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
-
"build": "node esbuild.js"
|
|
|
8 |
},
|
9 |
"author": "",
|
10 |
"license": "ISC",
|
|
|
4 |
"main": "index.js",
|
5 |
"type": "module",
|
6 |
"scripts": {
|
7 |
+
"build": "node esbuild.js",
|
8 |
+
"watch": "node esbuild.js --watch"
|
9 |
},
|
10 |
"author": "",
|
11 |
"license": "ISC",
|
frontend/public/index.css
CHANGED
@@ -1,3 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
main {
|
2 |
display: flex;
|
3 |
flex-direction: column;
|
@@ -51,3 +125,13 @@ details > summary {
|
|
51 |
font-family: monospace;
|
52 |
color: red;
|
53 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
* {
|
2 |
+
box-sizing: border-box;
|
3 |
+
}
|
4 |
+
|
5 |
+
:root {
|
6 |
+
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
7 |
+
font-size: 16px;
|
8 |
+
line-height: 24px;
|
9 |
+
font-weight: 400;
|
10 |
+
|
11 |
+
color: #213547;
|
12 |
+
background-color: #ffffff;
|
13 |
+
|
14 |
+
font-synthesis: none;
|
15 |
+
text-rendering: optimizeLegibility;
|
16 |
+
-webkit-font-smoothing: antialiased;
|
17 |
+
-moz-osx-font-smoothing: grayscale;
|
18 |
+
-webkit-text-size-adjust: 100%;
|
19 |
+
}
|
20 |
+
|
21 |
+
a {
|
22 |
+
font-weight: 500;
|
23 |
+
color: #646cff;
|
24 |
+
text-decoration: inherit;
|
25 |
+
}
|
26 |
+
a:hover {
|
27 |
+
color: #747bff;
|
28 |
+
}
|
29 |
+
|
30 |
+
body {
|
31 |
+
margin: 0;
|
32 |
+
display: flex;
|
33 |
+
place-items: center;
|
34 |
+
min-width: 320px;
|
35 |
+
min-height: 100vh;
|
36 |
+
}
|
37 |
+
|
38 |
+
h1 {
|
39 |
+
font-size: 3.2em;
|
40 |
+
line-height: 1.1;
|
41 |
+
}
|
42 |
+
|
43 |
+
.card {
|
44 |
+
padding: 2em;
|
45 |
+
}
|
46 |
+
|
47 |
+
#app {
|
48 |
+
width: min(900px, 100vw);
|
49 |
+
min-height: 100vh;
|
50 |
+
margin: 0 auto;
|
51 |
+
padding: 2rem;
|
52 |
+
text-align: center;
|
53 |
+
}
|
54 |
+
|
55 |
+
button {
|
56 |
+
border-radius: 4px;
|
57 |
+
border: 1px solid transparent;
|
58 |
+
padding: 0.6em 1.2em;
|
59 |
+
font-size: 1em;
|
60 |
+
font-weight: 500;
|
61 |
+
font-family: inherit;
|
62 |
+
background-color: #e5e5e5;
|
63 |
+
cursor: pointer;
|
64 |
+
transition: border-color 0.25s;
|
65 |
+
}
|
66 |
+
button:hover {
|
67 |
+
border-color: #646cff;
|
68 |
+
}
|
69 |
+
button:focus,
|
70 |
+
button:focus-visible {
|
71 |
+
outline: 4px auto -webkit-focus-ring-color;
|
72 |
+
}
|
73 |
+
|
74 |
+
|
75 |
main {
|
76 |
display: flex;
|
77 |
flex-direction: column;
|
|
|
125 |
font-family: monospace;
|
126 |
color: red;
|
127 |
}
|
128 |
+
|
129 |
+
span.token-chip {
|
130 |
+
background-color: #222222;
|
131 |
+
color: white;
|
132 |
+
padding: 2px;
|
133 |
+
}
|
134 |
+
|
135 |
+
span.token-chip.flagged {
|
136 |
+
background-color: #dd1111;
|
137 |
+
}
|
frontend/public/index.html
CHANGED
@@ -5,7 +5,6 @@
|
|
5 |
<link rel="stylesheet" href="index.css">
|
6 |
</head>
|
7 |
<body>
|
8 |
-
<h1>GPTed with suggestions</h1>
|
9 |
<div id="app"></div>
|
10 |
<script src="index.js"></script>
|
11 |
</body>
|
|
|
5 |
<link rel="stylesheet" href="index.css">
|
6 |
</head>
|
7 |
<body>
|
|
|
8 |
<div id="app"></div>
|
9 |
<script src="index.js"></script>
|
10 |
</body>
|
frontend/src/components/app.tsx
CHANGED
@@ -1,35 +1,64 @@
|
|
1 |
import React, { useState } from "react"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
export default function App() {
|
4 |
-
const [threshold, setThreshold] = useState(
|
5 |
const [context, setContext] = useState("")
|
6 |
const [wordlist, setWordlist] = useState("")
|
7 |
const [showWholePrompt, setShowWholePrompt] = useState(false)
|
8 |
-
const [checked, setChecked] = useState(false)
|
9 |
const [text, setText] = useState("")
|
|
|
|
|
10 |
|
11 |
-
const
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
}
|
14 |
|
15 |
let result
|
16 |
|
17 |
-
if (
|
18 |
-
result = <textarea value={text} />
|
19 |
} else {
|
20 |
-
result =
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
}
|
24 |
|
25 |
return (
|
26 |
-
|
27 |
<h1>GPTed</h1>
|
28 |
|
29 |
<details>
|
30 |
<summary>Advanced settings</summary>
|
31 |
<label>
|
32 |
-
<strong>Threshold:</strong> <input type="number" step="0.1" value={threshold} />
|
33 |
<small>
|
34 |
The <a href="https://en.wikipedia.org/wiki/Log_probability" target="_blank" rel="noreferrer">logprob</a> threshold.
|
35 |
Tokens with logprobs smaller than this will be marked red.
|
@@ -37,15 +66,15 @@ export default function App() {
|
|
37 |
</label>
|
38 |
<label>
|
39 |
<strong>Context:</strong> <small>Context for the text, which can help GPT3 better rank certain words.</small>
|
40 |
-
<textarea placeholder="A short essay about picnics" value={context} />
|
41 |
</label>
|
42 |
<label>
|
43 |
<strong>Dictionary:</strong>
|
44 |
<small>Known words or phrases. Helpful for uncommon or invented words and names.</small>
|
45 |
-
<textarea placeholder="jujubu eschaton Frodo Baggins" value={wordlist} />
|
46 |
</label>
|
47 |
<label>
|
48 |
-
<strong>Show whole prompt:</strong> <input type="checkbox" checked={showWholePrompt} />
|
49 |
<small>
|
50 |
Show the whole prompt in the token view, instead of just your text. Mostly useful for debugging or curiosity.
|
51 |
</small>
|
@@ -54,8 +83,8 @@ export default function App() {
|
|
54 |
|
55 |
<section id="inner">
|
56 |
{result}
|
57 |
-
<button onClick={
|
58 |
-
{
|
59 |
</button>
|
60 |
|
61 |
<p>
|
@@ -67,6 +96,6 @@ export default function App() {
|
|
67 |
</small>
|
68 |
</p>
|
69 |
</section>
|
70 |
-
|
71 |
)
|
72 |
}
|
|
|
1 |
import React, { useState } from "react"
|
2 |
+
import { TokenChip } from "./TokenChip"
|
3 |
+
|
4 |
+
interface Word {
|
5 |
+
text: string
|
6 |
+
logprob: number
|
7 |
+
}
|
8 |
+
|
9 |
+
async function checkText(text: string): Promise<Word[]> {
|
10 |
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
11 |
+
|
12 |
+
const words = text.split(/\b/)
|
13 |
+
return words.map(word => ({ text: word, logprob: -word.length }))
|
14 |
+
}
|
15 |
|
16 |
export default function App() {
|
17 |
+
const [threshold, setThreshold] = useState(-5.0)
|
18 |
const [context, setContext] = useState("")
|
19 |
const [wordlist, setWordlist] = useState("")
|
20 |
const [showWholePrompt, setShowWholePrompt] = useState(false)
|
|
|
21 |
const [text, setText] = useState("")
|
22 |
+
const [mode, setMode] = useState<"edit" | "check">("edit")
|
23 |
+
const [words, setWords] = useState<Word[]>([])
|
24 |
|
25 |
+
const toggleMode = async () => {
|
26 |
+
if (mode === "edit") {
|
27 |
+
const checkedWords = await checkText(text)
|
28 |
+
setWords(checkedWords)
|
29 |
+
setMode("check")
|
30 |
+
} else {
|
31 |
+
setMode("edit")
|
32 |
+
}
|
33 |
}
|
34 |
|
35 |
let result
|
36 |
|
37 |
+
if (mode === "edit") {
|
38 |
+
result = <textarea value={text} onChange={e => setText(e.target.value)} />
|
39 |
} else {
|
40 |
+
result = (
|
41 |
+
<div className="result">
|
42 |
+
{words.map((word, index) => (
|
43 |
+
<TokenChip
|
44 |
+
key={index}
|
45 |
+
token={word.text}
|
46 |
+
logprob={word.logprob}
|
47 |
+
threshold={threshold}
|
48 |
+
/>
|
49 |
+
))}
|
50 |
+
</div>
|
51 |
+
)
|
52 |
}
|
53 |
|
54 |
return (
|
55 |
+
<main>
|
56 |
<h1>GPTed</h1>
|
57 |
|
58 |
<details>
|
59 |
<summary>Advanced settings</summary>
|
60 |
<label>
|
61 |
+
<strong>Threshold:</strong> <input type="number" step="0.1" value={threshold} onChange={e => setThreshold(Number(e.target.value))} />
|
62 |
<small>
|
63 |
The <a href="https://en.wikipedia.org/wiki/Log_probability" target="_blank" rel="noreferrer">logprob</a> threshold.
|
64 |
Tokens with logprobs smaller than this will be marked red.
|
|
|
66 |
</label>
|
67 |
<label>
|
68 |
<strong>Context:</strong> <small>Context for the text, which can help GPT3 better rank certain words.</small>
|
69 |
+
<textarea placeholder="A short essay about picnics" value={context} onChange={e => setContext(e.target.value)} />
|
70 |
</label>
|
71 |
<label>
|
72 |
<strong>Dictionary:</strong>
|
73 |
<small>Known words or phrases. Helpful for uncommon or invented words and names.</small>
|
74 |
+
<textarea placeholder="jujubu eschaton Frodo Baggins" value={wordlist} onChange={e => setWordlist(e.target.value)} />
|
75 |
</label>
|
76 |
<label>
|
77 |
+
<strong>Show whole prompt:</strong> <input type="checkbox" checked={showWholePrompt} onChange={e => setShowWholePrompt(e.target.checked)} />
|
78 |
<small>
|
79 |
Show the whole prompt in the token view, instead of just your text. Mostly useful for debugging or curiosity.
|
80 |
</small>
|
|
|
83 |
|
84 |
<section id="inner">
|
85 |
{result}
|
86 |
+
<button onClick={toggleMode}>
|
87 |
+
{mode === "edit" ? "Check" : "Edit"}
|
88 |
</button>
|
89 |
|
90 |
<p>
|
|
|
96 |
</small>
|
97 |
</p>
|
98 |
</section>
|
99 |
+
</main>
|
100 |
)
|
101 |
}
|
frontend/src/index.tsx
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
import { createRoot } from "react-dom/client"
|
2 |
import App from "./components/app"
|
3 |
|
@@ -5,4 +6,4 @@ const app = document.getElementById("app")
|
|
5 |
|
6 |
const root = createRoot(app!)
|
7 |
|
8 |
-
root.render(App
|
|
|
1 |
+
import React from "react"
|
2 |
import { createRoot } from "react-dom/client"
|
3 |
import App from "./components/app"
|
4 |
|
|
|
6 |
|
7 |
const root = createRoot(app!)
|
8 |
|
9 |
+
root.render(<App />)
|