richkit 0.0.0-stage → 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 richkit 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,205 @@
1
- # Temporary Holding Version
1
+ # richkit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A drop-in rich text editor React component. Works in Vite, Next.js (App Router and Pages Router) and any React 18+ app. MIT licensed.
4
+
5
+ - One component, one stylesheet
6
+ - Headings, lists, task lists, tables with resizable columns, images, links, code blocks
7
+ - Text colour, highlight, alignment, and a floating format bar on selection
8
+ - Image upload hook (button, paste and drag-and-drop), word / character counter, fullscreen mode
9
+ - Built-in AI assistant: rewrite, shorten, expand, translate or ask anything, with a preview before anything changes. Works with Claude, OpenAI, Gemini or your own backend
10
+ - Controlled or uncontrolled, HTML in and HTML out
11
+ - SSR-safe: ships with `"use client"` and renders on the client without hydration warnings
12
+ - Configurable toolbar, light / dark / auto themes, CSS variables for everything else
13
+ - TypeScript types included, ESM and CommonJS builds
14
+ - Built on [Tiptap](https://tiptap.dev) / ProseMirror, so you can add any Tiptap extension
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install richkit
20
+ ```
21
+
22
+ `react` and `react-dom` (18 or newer) are peer dependencies.
23
+
24
+ ## Usage
25
+
26
+ ```tsx
27
+ import { useState } from "react";
28
+ import { RichTextEditor } from "richkit";
29
+ import "richkit/styles.css";
30
+
31
+ export function Notes() {
32
+ const [html, setHtml] = useState("<p>Hello</p>");
33
+ return <RichTextEditor value={html} onChange={setHtml} placeholder="Write something…" />;
34
+ }
35
+ ```
36
+
37
+ ### Next.js
38
+
39
+ Import the stylesheet once in `app/layout.tsx` (or `pages/_app.tsx`):
40
+
41
+ ```tsx
42
+ import "richkit/styles.css";
43
+ ```
44
+
45
+ Then use the component anywhere. In a Server Component you can render it directly as long as you only pass serialisable props (`defaultValue`, `placeholder`, …). To pass `onChange`, put it in a file that starts with `"use client"`.
46
+
47
+ ## Props
48
+
49
+ | Prop | Type | Default | Description |
50
+ | --- | --- | --- | --- |
51
+ | `value` | `string` | – | Controlled HTML value. |
52
+ | `defaultValue` | `string` | `""` | Initial HTML for uncontrolled use. |
53
+ | `onChange` | `(html, editor) => void` | – | Fires on every change. An empty document is reported as `""`. |
54
+ | `onFocus` / `onBlur` | `(editor) => void` | – | Focus events. |
55
+ | `placeholder` | `string` | `""` | Shown while empty. |
56
+ | `editable` | `boolean` | `true` | `false` makes it read-only. |
57
+ | `autoFocus` | `boolean` | `false` | Focus on mount. |
58
+ | `toolbar` | `ToolbarItem[] \| false` | all items | Buttons in order; `"\|"` is a separator; `false` hides the toolbar. |
59
+ | `bubbleMenu` | `boolean` | `true` | Floating format bar over selected text. |
60
+ | `onImageUpload` | `(file: File) => Promise<string>` | – | Upload a file and resolve to its URL. Enables the Upload button, paste and drop. |
61
+ | `ai` | `AIConfig` | – | Turns on the AI assistant. See [AI assistant](#ai-assistant). |
62
+ | `maxLength` | `number` | – | Hard character limit. |
63
+ | `showCount` | `boolean` | `true` if `maxLength` is set | Word / character counter in the footer. |
64
+ | `theme` | `"light" \| "dark" \| "auto"` | `"light"` | `"auto"` follows the OS setting. |
65
+ | `minHeight` / `maxHeight` | `number \| string` | `140px` / none | Size of the editing area. |
66
+ | `extensions` | Tiptap `Extensions` | – | Extra Tiptap extensions. |
67
+ | `className`, `style`, `id`, `aria-label` | – | – | Applied to the wrapper / editing area. |
68
+
69
+ Toolbar items: `ai` (shown only when the `ai` prop is set), `undo`, `redo`, `heading`, `bold`, `italic`, `underline`, `strike`, `code`, `textColor`, `highlight`, `align`, `bulletList`, `orderedList`, `taskList`, `blockquote`, `codeBlock`, `link`, `image`, `table`, `horizontalRule`, `clearFormatting`, `fullscreen`, `|`.
70
+
71
+ ```tsx
72
+ <RichTextEditor toolbar={["bold", "italic", "|", "bulletList", "link"]} />
73
+ ```
74
+
75
+ ## Images
76
+
77
+ Without `onImageUpload`, images are added by URL only. Provide it to enable uploads from the toolbar, the clipboard and drag-and-drop:
78
+
79
+ ```tsx
80
+ <RichTextEditor
81
+ onImageUpload={async (file) => {
82
+ const body = new FormData();
83
+ body.append("file", file);
84
+ const res = await fetch("/api/upload", { method: "POST", body });
85
+ return (await res.json()).url;
86
+ }}
87
+ />
88
+ ```
89
+
90
+ ## AI assistant
91
+
92
+ Set the `ai` prop and the editor gains an **AI** button in the toolbar and the selection bubble menu (shortcut: Ctrl/Cmd+J).
93
+
94
+ - With text selected: Improve writing, Fix grammar, Shorten, Expand, Simplify, Professional, Casual, Translate, or a typed instruction.
95
+ - With nothing selected: Continue writing, Summarise document, or a typed instruction; the result goes in at the cursor.
96
+ - The answer streams into a preview. Nothing in the document changes until the writer presses **Replace selection** or **Insert**, and one undo reverts it.
97
+
98
+ ### With the user's own API key
99
+
100
+ ```tsx
101
+ <RichTextEditor ai={{ provider: "claude", apiKey: userKey }} />
102
+ <RichTextEditor ai={{ provider: "openai", apiKey: userKey, model: "…" }} />
103
+ <RichTextEditor ai={{ provider: "gemini", apiKey: userKey, model: "…" }} />
104
+ ```
105
+
106
+ | Provider | `model` | Notes |
107
+ | --- | --- | --- |
108
+ | `claude` | optional, defaults to `claude-opus-5-5` | On the default model, a request the API declines is retried on a fallback model automatically. |
109
+ | `openai` | required | Any Chat Completions model id. |
110
+ | `gemini` | required | Any `generateContent` model id. |
111
+
112
+ `baseURL` is also accepted, to send requests through a proxy.
113
+
114
+ > **Whose key?** The key is sent from the browser straight to the provider, so anyone using the page can read it. That is fine for a key the person typed in themselves ("bring your own key"). Never ship your own key this way: keep it on your server and use `complete` below. richkit does not store the key.
115
+
116
+ ### With your own backend
117
+
118
+ ```tsx
119
+ <RichTextEditor
120
+ ai={{
121
+ complete: ({ action, prompt, selection, document, signal }) =>
122
+ fetch("/api/ai", {
123
+ method: "POST",
124
+ body: JSON.stringify({ action, prompt, selection, document }),
125
+ signal,
126
+ }),
127
+ }}
128
+ />
129
+ ```
130
+
131
+ `complete` may return a `Response`, a `ReadableStream`, an async iterable of strings, or a plain string. Whatever text it yields should be an HTML fragment.
132
+
133
+ On the server, `buildAIPrompt` gives you the same prompts the built-in providers use:
134
+
135
+ ```ts
136
+ import { buildAIPrompt } from "richkit";
137
+
138
+ const { system, user } = buildAIPrompt({ action, prompt, selection, document });
139
+ // send `system` and `user` to any model, and stream its text back
140
+ ```
141
+
142
+ ### Safety
143
+
144
+ Model output is treated as untrusted. Before it is previewed or inserted it is parsed in an inert document, stripped of scripts, event handlers, unsafe links and images (a remote image URL can leak document text), and then passed through the editor's schema so only supported formatting survives.
145
+
146
+ ## Ref API
147
+
148
+ ```tsx
149
+ const ref = useRef<RichTextEditorRef>(null);
150
+
151
+ <RichTextEditor ref={ref} />;
152
+
153
+ ref.current?.getHTML();
154
+ ref.current?.getText();
155
+ ref.current?.getJSON();
156
+ ref.current?.focus();
157
+ ref.current?.clear();
158
+ ref.current?.editor; // the underlying Tiptap editor
159
+ ```
160
+
161
+ ## Theming
162
+
163
+ Override any `--richkit-*` variable:
164
+
165
+ ```css
166
+ .richkit {
167
+ --richkit-accent: #7c3aed;
168
+ --richkit-radius: 12px;
169
+ --richkit-font: "Inter", sans-serif;
170
+ }
171
+ ```
172
+
173
+ Available variables: `bg`, `fg`, `muted`, `border`, `toolbar-bg`, `hover`, `active-bg`, `accent`, `danger`, `code-bg`, `shadow`, `radius`, `font`, `mono`, `min-height`, `max-height`, `sticky-top`.
174
+
175
+ The default font is Trebuchet MS (with system fallbacks). To use your app's own font instead, set `--richkit-font: inherit`.
176
+
177
+ The toolbar sticks to the top of the page while a long document scrolls. If your app has a fixed header, set `--richkit-sticky-top` to its height.
178
+
179
+ ## Security
180
+
181
+ The editor only produces markup its schema allows, but the HTML you store is still user input. Sanitise it on the server before rendering it outside the editor.
182
+
183
+ ## Development
184
+
185
+ ```bash
186
+ npm install
187
+ npm run build # ESM + CJS + types into dist/
188
+ npm test
189
+ npm run typecheck
190
+ ```
191
+
192
+ The example apps in `examples/vite` and `examples/next` install the packed tarball, so they exercise exactly what gets published. After changing the library, run `npm run sync:examples` and restart the example's dev server.
193
+
194
+ ### Landing page
195
+
196
+ `site/` is a static landing page with a live demo of the editor.
197
+
198
+ ```bash
199
+ npm --prefix site run dev # local preview
200
+ npm --prefix site run build # static output in site/dist, deployable to any static host
201
+ ```
202
+
203
+ ## License
204
+
205
+ [MIT](./LICENSE)