react-smart-paste 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +223 -2
- package/dist/index.cjs +716 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +151 -0
- package/dist/index.d.ts +151 -0
- package/dist/index.js +683 -0
- package/dist/index.js.map +1 -0
- package/dist/server.cjs +172 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +44 -0
- package/dist/server.d.ts +44 -0
- package/dist/server.js +143 -0
- package/dist/server.js.map +1 -0
- package/dist/styles.css +230 -0
- package/package.json +86 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 react-smart-paste contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,224 @@
|
|
|
1
|
-
#
|
|
1
|
+
# react-smart-paste
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Paste any text and let AI fill your form. A React button and hook that read the form, send the pasted text to your model, show the proposed values, and fill the fields once the user approves. MIT licensed.
|
|
4
|
+
|
|
5
|
+
- Works on plain HTML forms: no schema to declare, it reads names, labels, types and options from the page
|
|
6
|
+
- Review before anything changes, per-field tick boxes, and undo
|
|
7
|
+
- Never overwrites what the user already typed (unless you ask it to)
|
|
8
|
+
- Passwords, payment fields, files and hidden inputs are never read, sent or filled
|
|
9
|
+
- Any model: you pass one function that calls your backend, so no API key reaches the browser
|
|
10
|
+
- Fills React-controlled and uncontrolled inputs alike, by setting values the way typing does
|
|
11
|
+
- Falls back to a paste box when the browser blocks clipboard access
|
|
12
|
+
- Ships with `"use client"`, ESM and CommonJS builds, and TypeScript types
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install react-smart-paste
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`react` and `react-dom` (18 or newer) are peer dependencies.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Put the button inside your form and give it a `complete` function that calls your backend:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import { SmartPasteButton, type SmartPasteComplete } from "react-smart-paste";
|
|
28
|
+
import "react-smart-paste/styles.css";
|
|
29
|
+
|
|
30
|
+
const complete: SmartPasteComplete = ({ text, fields, signal }) =>
|
|
31
|
+
fetch("/api/smart-paste", { method: "POST", body: JSON.stringify({ text, fields }), signal });
|
|
32
|
+
|
|
33
|
+
export function ContactForm() {
|
|
34
|
+
return (
|
|
35
|
+
<form>
|
|
36
|
+
<label>
|
|
37
|
+
Name <input name="name" />
|
|
38
|
+
</label>
|
|
39
|
+
<label>
|
|
40
|
+
Email <input name="email" type="email" />
|
|
41
|
+
</label>
|
|
42
|
+
<label>
|
|
43
|
+
Country
|
|
44
|
+
<select name="country">
|
|
45
|
+
<option value="">Choose…</option>
|
|
46
|
+
<option value="in">India</option>
|
|
47
|
+
<option value="us">United States</option>
|
|
48
|
+
</select>
|
|
49
|
+
</label>
|
|
50
|
+
|
|
51
|
+
<SmartPasteButton complete={complete} />
|
|
52
|
+
</form>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The user copies some text (an email signature, an invoice, a message), presses **Smart paste**, checks the list of proposed values, and presses **Fill all**.
|
|
58
|
+
|
|
59
|
+
Every control needs a `name`. Good labels give better results: the model sees each field's label, type, options, placeholder and `autocomplete` hint.
|
|
60
|
+
|
|
61
|
+
## The backend route
|
|
62
|
+
|
|
63
|
+
`react-smart-paste/server` exports the prompt builder with no React code, so it is safe to import on a server. Send its `system` and `user` messages to any model and return the model's text.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// app/api/smart-paste/route.ts (Next.js). Set ANTHROPIC_API_KEY in .env.local
|
|
67
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
68
|
+
import { buildSmartPastePrompt } from "react-smart-paste/server";
|
|
69
|
+
|
|
70
|
+
const client = new Anthropic(); // reads ANTHROPIC_API_KEY on the server
|
|
71
|
+
|
|
72
|
+
export async function POST(req: Request) {
|
|
73
|
+
const { text, fields } = await req.json();
|
|
74
|
+
if (typeof text !== "string" || text.length > 20000 || !Array.isArray(fields)) {
|
|
75
|
+
return new Response("Bad request", { status: 400 });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const { system, user } = buildSmartPastePrompt({ text, fields });
|
|
79
|
+
const message = await client.messages.create({
|
|
80
|
+
model: "claude-opus-5-5",
|
|
81
|
+
max_tokens: 2048,
|
|
82
|
+
system,
|
|
83
|
+
messages: [{ role: "user", content: user }],
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const reply = message.content.map((block) => (block.type === "text" ? block.text : "")).join("");
|
|
87
|
+
return new Response(reply);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The same shape works with OpenAI, Gemini or a local model: pass `system` and `user`, return the text.
|
|
92
|
+
|
|
93
|
+
- Keep the API key in a server-only env variable. Variables named `VITE_*` or `NEXT_PUBLIC_*` are copied into the browser bundle and are not secret.
|
|
94
|
+
- A route like this lets anyone who can reach it spend your AI budget. Put it behind your app's sign-in and add a rate limit.
|
|
95
|
+
|
|
96
|
+
`complete` may return a `Response`, the model's text as a string, or an object of field values you parsed yourself.
|
|
97
|
+
|
|
98
|
+
## Button props
|
|
99
|
+
|
|
100
|
+
| Prop | Type | Default | Description |
|
|
101
|
+
| --- | --- | --- | --- |
|
|
102
|
+
| `complete` | `(request) => Response \| string \| object` | required | Sends `{ text, fields, signal }` to your model. |
|
|
103
|
+
| `form` | `RefObject<HTMLFormElement>` | the enclosing `<form>` | The form to fill, when the button sits outside it. |
|
|
104
|
+
| `overwrite` | `"empty" \| "all"` | `"empty"` | `"empty"` fills blank fields only. `"all"` may replace existing values. |
|
|
105
|
+
| `review` | `boolean` | `true` | Show the proposed values for approval. `false` fills straight away. |
|
|
106
|
+
| `maxLength` | `number` | `20000` | Longest pasted text that will be sent, in characters. |
|
|
107
|
+
| `children` | `ReactNode` | `"Smart paste"` | Button text. |
|
|
108
|
+
| `theme` | `"light" \| "dark" \| "auto"` | `"auto"` | `"auto"` follows the OS setting. |
|
|
109
|
+
| `align` | `"start" \| "end"` | `"start"` | Which side of the button the panel lines up with. |
|
|
110
|
+
| `disabled`, `className` | – | – | Passed to the button / wrapper. |
|
|
111
|
+
| `onApply` | `(changes) => void` | – | Called after fields are filled. |
|
|
112
|
+
| `onError` | `(error) => void` | – | Called when a paste fails. |
|
|
113
|
+
|
|
114
|
+
## The hook
|
|
115
|
+
|
|
116
|
+
For your own button and review UI, use `useSmartPaste`:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
import { useRef } from "react";
|
|
120
|
+
import { useSmartPaste } from "react-smart-paste";
|
|
121
|
+
|
|
122
|
+
function ContactForm() {
|
|
123
|
+
const form = useRef<HTMLFormElement>(null);
|
|
124
|
+
const smart = useSmartPaste({ form, complete });
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<form ref={form}>
|
|
128
|
+
<input name="email" type="email" />
|
|
129
|
+
|
|
130
|
+
<button type="button" onClick={() => smart.paste()}>
|
|
131
|
+
Paste
|
|
132
|
+
</button>
|
|
133
|
+
|
|
134
|
+
{smart.status === "review" && (
|
|
135
|
+
<div>
|
|
136
|
+
{smart.changes.map((change) => (
|
|
137
|
+
<p key={change.name}>
|
|
138
|
+
{change.label}: {change.to}
|
|
139
|
+
</p>
|
|
140
|
+
))}
|
|
141
|
+
<button type="button" onClick={() => smart.accept()}>Fill</button>
|
|
142
|
+
<button type="button" onClick={smart.discard}>Discard</button>
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
{smart.status === "error" && <p role="alert">{smart.error?.message}</p>}
|
|
146
|
+
</form>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
| Returned | Description |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| `status` | `"idle"`, `"working"`, `"review"`, `"applied"`, `"nothing"` (no match) or `"error"`. |
|
|
154
|
+
| `changes` | In `review`, the proposed edits; in `applied`, the edits made. Each has `name`, `label`, `from`, `to`, `value`. |
|
|
155
|
+
| `error` | A `SmartPasteError` with a `code`: `clipboard`, `empty`, `too-long`, `no-form`, `no-fields`, `request` or `response`. |
|
|
156
|
+
| `paste(text?)` | Fill from `text`, or from the clipboard when called with no argument. |
|
|
157
|
+
| `accept(names?)` | Apply the reviewed changes, or only the named fields. |
|
|
158
|
+
| `discard()` / `cancel()` | Drop the proposal, or stop a request in progress. |
|
|
159
|
+
| `undo()` / `canUndo` | Put back the values the last fill replaced. |
|
|
160
|
+
|
|
161
|
+
The hook takes the same `complete`, `overwrite`, `review`, `maxLength`, `onApply` and `onError` options as the button. `form` is a ref or a function returning the form element.
|
|
162
|
+
|
|
163
|
+
Reading the clipboard needs a user gesture and permission, and some browsers refuse it. When that happens `error.code` is `"clipboard"`: show a text box and call `paste(text)` with what the user pastes into it. The button does this for you.
|
|
164
|
+
|
|
165
|
+
## What is read and what is not
|
|
166
|
+
|
|
167
|
+
Fields are found through the form's controls. These are always left out, so they are never described to the model and never filled:
|
|
168
|
+
|
|
169
|
+
- `password`, `file` and `hidden` inputs, and buttons
|
|
170
|
+
- fields whose `autocomplete` is a payment token (`cc-number`, `cc-csc`, …), `current-password`, `new-password` or `one-time-code`
|
|
171
|
+
- disabled and read-only controls
|
|
172
|
+
- anything inside an element with `data-smart-paste="off"`
|
|
173
|
+
|
|
174
|
+
With the default `overwrite="empty"`, fields that already have a value are left out too. The current values of fields are never sent, only their descriptions.
|
|
175
|
+
|
|
176
|
+
## Safety and privacy
|
|
177
|
+
|
|
178
|
+
- **The pasted text goes to your model.** Tell your users, as you would for any AI feature. Nothing is sent until they press the button.
|
|
179
|
+
- **The model's answer is validated before it is shown.** Only known field names are accepted, and each value must fit its field: a real date, a listed option, a well-formed email, an `http(s)` URL. Anything else is dropped. Values are set as plain form values, never as HTML.
|
|
180
|
+
- **Pasted text is treated as data.** The prompt tells the model not to follow instructions inside it, and the validation above limits what a manipulated answer could do to choosing wrong values for visible fields, which the user reviews before filling.
|
|
181
|
+
|
|
182
|
+
## Theming
|
|
183
|
+
|
|
184
|
+
Override any `--rsp-*` variable on `.rsp`:
|
|
185
|
+
|
|
186
|
+
```css
|
|
187
|
+
.rsp {
|
|
188
|
+
--rsp-accent: #7c3aed;
|
|
189
|
+
--rsp-radius: 10px;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Available variables: `bg`, `fg`, `muted`, `border`, `hover`, `accent`, `on-accent`, `danger`, `radius`, `shadow`, `font`.
|
|
194
|
+
|
|
195
|
+
## Lower-level helpers
|
|
196
|
+
|
|
197
|
+
`readFields(form)`, `applyValues(form, values)`, `buildSmartPastePrompt({ text, fields })` and `parseSmartPasteResponse(reply, fields)` are exported for building your own flow.
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
npm install
|
|
203
|
+
npm run build # ESM + CJS + types into dist/
|
|
204
|
+
npm test
|
|
205
|
+
npm run typecheck
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`examples/vite` is a demo form that runs without an API key, using a mock model (plain pattern matching, labelled as such on the page). It installs the packed tarball: after changing the library, run `npm run build && npm pack`, reinstall in `examples/vite`, and restart its dev server.
|
|
209
|
+
|
|
210
|
+
### Landing page
|
|
211
|
+
|
|
212
|
+
`site/` is a static landing page with a live demo. It runs the library straight from `src/`, so it always matches this repository, and its demo uses the same kind of mock model as the example.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
npm --prefix site install
|
|
216
|
+
npm --prefix site run dev # local preview on port 5186
|
|
217
|
+
npm --prefix site run build # static output in site/dist, deployable to any static host
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`.github/workflows/pages.yml` publishes it to GitHub Pages on pushes to `main` that touch `site/` or `src/`. Turn Pages on first: repository Settings → Pages → Source: GitHub Actions.
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
[MIT](./LICENSE)
|