@oix1987/yjd 2.1.1 → 2.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/README.md CHANGED
@@ -27,12 +27,17 @@ Every preset is built from the same `/core` entry — pick a profile, tree-shake
27
27
  | Preset | Includes | Size |
28
28
  |---|---|---|
29
29
  | Minimal | bold · italic · underline · link | **~16 KB** |
30
- | Bubble | floating bar, no toolbar + slash menu | **~21 KB** |
30
+ | Comment box | bold · italic · link · list · image · mention + `fromTextarea` | **~26 KB** |
31
31
  | Basic | + strike · headings · lists · align | **~25 KB** |
32
32
  | Standard | + colour · image · table · find · code view | **~38 KB** |
33
- | Full | everything (CSS inlined) | **~54 KB** |
33
+ | + AI assistant | any preset + `ai` module (BYO model, no SDK bundled) | **+~2 KB** |
34
+ | Full (all-in-one) | everything, CSS inlined | **~66 KB** |
34
35
 
35
- > For comparison, Quill 2 is ~60 KB. The stylesheet (~8 KB gzip) ships once and is cached, kept out of the JS.
36
+ > All figures are measured gzip. Tree-shake from the `/core` entry to land near the
37
+ > top of the table; the all-in-one default (`import yjd from '@oix1987/yjd'`) is the
38
+ > ~66 KB row because it registers every format/module and inlines the CSS. For
39
+ > comparison, Quill 2 is ~60 KB. The standalone stylesheet is ~10 KB gzip — link it
40
+ > once (and skip `StylesLoader`) to keep it out of the JS.
36
41
 
37
42
  ## Install
38
43
 
@@ -93,6 +98,30 @@ new Editor('#editor', {
93
98
  <link rel="stylesheet" href="@oix1987/yjd/lib/styles.min.css">
94
99
  ```
95
100
 
101
+ ### Lightweight comment box (~26 KB)
102
+
103
+ `Editor.fromTextarea`, `renderStatic` and the Markdown/JSON helpers all live in
104
+ `/core`, so a comment box pulls only the formats/modules you register — not the
105
+ whole editor. Link the stylesheet (don't import `StylesLoader`) to keep CSS out
106
+ of the JS.
107
+
108
+ ```js
109
+ import { Editor, registry, Bold, Italic, Link, List, Image, Mention, Toolbar, History }
110
+ from '@oix1987/yjd/core';
111
+
112
+ [['formats/bold', Bold], ['formats/italic', Italic], ['formats/link', Link],
113
+ ['formats/list', List], ['formats/image', Image],
114
+ ['modules/mention', Mention], ['modules/toolbar', Toolbar], ['modules/history', History]]
115
+ .forEach(([k, v]) => registry.register(k, v));
116
+
117
+ const ed = Editor.fromTextarea('#comment', {
118
+ format: 'markdown',
119
+ toolbar1: [{ group: 'insert', items: ['bold', 'italic', 'link', 'list', 'image', 'emoji'] }],
120
+ mention: { source: (q) => fetchUsers(q) },
121
+ submit: { onEnter: (html) => post(html) },
122
+ });
123
+ ```
124
+
96
125
  ## Options
97
126
 
98
127
  | Option | Type | Description |
@@ -111,7 +140,7 @@ new Editor('#editor', {
111
140
 
112
141
  **Formats** — `bold` · `italic` · `underline` · `strike` · `subscript` · `superscript` · `color` · `background` · `link` · `heading` · `font-family` · `text-size` · `line-height` · `capitalization` · `text-align` · `list` · `indent-increase` · `indent-decrease` · `image` · `video` · `table` · `emoji` · `tag`
113
142
 
114
- **Modules** — `toolbar` · `history` · `slash-menu` · `mention` · `block-toolbar` (bubble bar) · `table-toolbar` · `find-replace` · `code-view` · `resize-handles`
143
+ **Modules** — `toolbar` · `history` · `slash-menu` · `mention` · `ai` (BYO-model assistant) · `block-toolbar` (bubble bar) · `table-toolbar` · `find-replace` · `code-view` · `resize-handles`
115
144
 
116
145
  ## Methods
117
146
 
@@ -201,6 +230,63 @@ Inserts a token that serializes with its id:
201
230
  If a `source` item has no `avatar_url`, pass `icon` (inline SVG) for special entries
202
231
  like “@all”. Menus are portaled to `<body>` but inherit the editor's `--rte-*` theme.
203
232
 
233
+ ### AI assistant (bring your own model)
234
+
235
+ Turn yjd into a "write-with-AI" surface **without bundling any model**. Like
236
+ `mention.source`, you supply a `complete` hook that calls whatever LLM you like
237
+ (Claude, your own endpoint, anything). The module is **inert until you do**, and
238
+ **tree-shakes to 0** when unused — so the AI code never reaches users who don't
239
+ opt in.
240
+
241
+ ```js
242
+ new yjd('#editor', {
243
+ ai: {
244
+ // REQUIRED. Resolve to the generated text. Stream by calling onToken with
245
+ // each chunk; if you only stream, return undefined and chunks are joined.
246
+ complete: async ({ action, prompt, text, signal }, onToken) => {
247
+ const res = await fetch('/api/ai', {
248
+ method: 'POST', signal,
249
+ body: JSON.stringify({ action, prompt, text }),
250
+ });
251
+ return (await res.json()).text;
252
+ },
253
+ autocomplete: true, // optional: inline ghost-text, Tab to accept
254
+ },
255
+ });
256
+ ```
257
+
258
+ **What the user gets:**
259
+
260
+ - **Selection toolbar** — select text and a floating bar offers *Improve · Fix
261
+ spelling & grammar · Shorten · Lengthen · Simplify · Summarize* plus a free-form
262
+ **Ask AI…** box. The result is previewed with **Accept / Retry / Discard** — the
263
+ user always stays in control (nothing overwrites their text until they accept).
264
+ - **Ghost-text autocomplete** (opt-in) — a greyed inline suggestion as they type;
265
+ **Tab** accepts, any other key dismisses. Debounced and request-cancelling, so it
266
+ never blocks typing.
267
+
268
+ Customise the actions, or drive it programmatically:
269
+
270
+ ```js
271
+ ai: { complete, actions: [{ id: 'tr', label: 'Translate → FR', prompt: 'Translate to French.' }] }
272
+
273
+ editor.ai.run('Make this sound friendlier'); // run on the current selection
274
+ editor.on('ai:accept', ({ result }) => {}); // ai:start · ai:done · ai:error · ai:accept · ai:discard
275
+ ```
276
+
277
+ Building your own agent UI? The same primitives are public:
278
+
279
+ ```js
280
+ editor.getSelection(); // { text, html, isEmpty, range }
281
+ editor.replaceSelection(text, { asText: true }); // sanitized, undo-aware
282
+ const s = editor.streamInto(); // token-by-token sink
283
+ s.append('Hel'); s.append('lo'); s.commit(); // or s.cancel() to undo the stream
284
+ ```
285
+
286
+ Nothing the module renders ever lives in the editable DOM, so `getContent()` /
287
+ `getJSON()` / `onChange` stay clean. Menus portal to `<body>` but inherit the
288
+ editor's `--rte-*` theme.
289
+
204
290
  ### Toolbar presets
205
291
 
206
292
  ```js
@@ -237,7 +323,8 @@ renderStatic(post.body_html, document.querySelector('#post'));
237
323
 
238
324
  - **Events** (via `editor.on(name, cb)` / `editor.off(name, cb)`): `change`,
239
325
  `image:upload` · `image:uploaded` · `image:error`, `file:upload` · `file:uploaded`
240
- · `file:error`, `mention:select`, `content:overflow` (when `maxContentSize` exceeded).
326
+ · `file:error`, `mention:select`, `ai:start` · `ai:done` · `ai:accept` · `ai:discard`
327
+ · `ai:error`, `content:overflow` (when `maxContentSize` exceeded).
241
328
  - `editor.editor` is the public contentEditable element (attach your own listeners).
242
329
  - **Markdown dialect** — GFM-ish: headings `#`–`######`, `**bold**`, `*italic*`,
243
330
  `~~strike~~`, `` `code` ``, fenced ``` ``` ```, `>` quotes, `-`/`1.` lists,
package/core.js CHANGED
@@ -62,6 +62,7 @@ export { default as CodeView } from './lib/modules/code-view.js';
62
62
  export { default as FindReplace } from './lib/modules/find-replace.js';
63
63
  export { default as SlashMenu } from './lib/modules/slash-menu.js';
64
64
  export { default as Mention } from './lib/modules/mention.js';
65
+ export { default as Ai } from './lib/modules/ai.js';
65
66
  export { default as ResizeHandles } from './lib/modules/resize-handles.js';
66
67
 
67
68
  // UI