@adminforth/rich-editor 1.0.16 → 1.0.18
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/ChangeLog.md +12 -0
- package/custom/quillEditor.vue +2 -2
- package/dist/custom/quillEditor.vue +2 -2
- package/dist/index.js +13 -28
- package/dist/types.js +0 -14
- package/index.ts +13 -29
- package/package.json +1 -1
- package/types.ts +4 -24
package/ChangeLog.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
## [1.0.18] - 2024-12-26
|
|
1
2
|
|
|
3
|
+
### Improved
|
|
4
|
+
|
|
5
|
+
- COmpatibility with latest AdminForth, remove dependency of hook types
|
|
6
|
+
|
|
7
|
+
## [1.0.17] - 2024-12-06
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Reworked completion for adapter
|
|
2
12
|
|
|
3
13
|
## [1.0.12] - 2021-10-07
|
|
14
|
+
|
|
4
15
|
### Added
|
|
16
|
+
|
|
5
17
|
- Rate limiting for openai requests support
|
package/custom/quillEditor.vue
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
2
|
<div
|
|
4
3
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500
|
|
5
4
|
focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
|
|
@@ -200,6 +199,7 @@ onMounted(() => {
|
|
|
200
199
|
|
|
201
200
|
quill = new Quill(editor.value as HTMLElement, {
|
|
202
201
|
theme: "snow",
|
|
202
|
+
readOnly:props.column?.editReadonly,
|
|
203
203
|
placeholder: 'Type here...',
|
|
204
204
|
// formats : ['complete'],
|
|
205
205
|
modules: {
|
|
@@ -431,7 +431,7 @@ function approveCompletion(type: 'all' | 'word') {
|
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
async function startCompletion() {
|
|
434
|
-
if (!props.meta.shouldComplete) {
|
|
434
|
+
if (!props.meta.shouldComplete || props.column?.editReadonly ) {
|
|
435
435
|
return;
|
|
436
436
|
}
|
|
437
437
|
completion.value = null;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
|
|
3
2
|
<div
|
|
4
3
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500
|
|
5
4
|
focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400
|
|
@@ -200,6 +199,7 @@ onMounted(() => {
|
|
|
200
199
|
|
|
201
200
|
quill = new Quill(editor.value as HTMLElement, {
|
|
202
201
|
theme: "snow",
|
|
202
|
+
readOnly:props.column?.editReadonly,
|
|
203
203
|
placeholder: 'Type here...',
|
|
204
204
|
// formats : ['complete'],
|
|
205
205
|
modules: {
|
|
@@ -431,7 +431,7 @@ function approveCompletion(type: 'all' | 'word') {
|
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
async function startCompletion() {
|
|
434
|
-
if (!props.meta.shouldComplete) {
|
|
434
|
+
if (!props.meta.shouldComplete || props.column?.editReadonly ) {
|
|
435
435
|
return;
|
|
436
436
|
}
|
|
437
437
|
completion.value = null;
|
package/dist/index.js
CHANGED
|
@@ -112,7 +112,7 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
112
112
|
});
|
|
113
113
|
})));
|
|
114
114
|
});
|
|
115
|
-
resourceConfig.hooks.create.afterSave.push((_d) => __awaiter(this, [_d], void 0, function* ({ record, adminUser }) {
|
|
115
|
+
(resourceConfig.hooks.create.afterSave).push((_d) => __awaiter(this, [_d], void 0, function* ({ record, adminUser }) {
|
|
116
116
|
// find all s3Paths in the html
|
|
117
117
|
const s3Paths = getAttachmentPathes(record[this.options.htmlFieldName]);
|
|
118
118
|
process.env.HEAVY_DEBUG && console.log('📸 Found s3Paths', s3Paths);
|
|
@@ -122,7 +122,7 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
122
122
|
}));
|
|
123
123
|
// after edit we need to delete attachments that are not in the html anymore
|
|
124
124
|
// and add new ones
|
|
125
|
-
resourceConfig.hooks.edit.afterSave.push((_e) => __awaiter(this, [_e], void 0, function* ({ recordId, record, adminUser }) {
|
|
125
|
+
(resourceConfig.hooks.edit.afterSave).push((_e) => __awaiter(this, [_e], void 0, function* ({ recordId, record, adminUser }) {
|
|
126
126
|
process.env.HEAVY_DEBUG && console.log('⚓ Cought hook', recordId, 'rec', record);
|
|
127
127
|
if (record[this.options.htmlFieldName] === undefined) {
|
|
128
128
|
// field was not changed, do nothing
|
|
@@ -147,7 +147,7 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
147
147
|
return { ok: true };
|
|
148
148
|
}));
|
|
149
149
|
// after delete we need to delete all attachments
|
|
150
|
-
resourceConfig.hooks.delete.afterSave.push((_f) => __awaiter(this, [_f], void 0, function* ({ record, adminUser }) {
|
|
150
|
+
(resourceConfig.hooks.delete.afterSave).push((_f) => __awaiter(this, [_f], void 0, function* ({ record, adminUser }) {
|
|
151
151
|
const existingAparts = yield adminforth.resource(this.options.attachments.attachmentResource).list([
|
|
152
152
|
Filters.EQ(this.options.attachments.attachmentRecordIdFieldName, record[editorRecordPkField.name]),
|
|
153
153
|
Filters.EQ(this.options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId)
|
|
@@ -161,10 +161,14 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
163
|
validateConfigAfterDiscover(adminforth, resourceConfig) {
|
|
164
|
+
var _a, _b;
|
|
164
165
|
this.adminforth = adminforth;
|
|
166
|
+
if ((_a = this.options.completion) === null || _a === void 0 ? void 0 : _a.adapter) {
|
|
167
|
+
(_b = this.options.completion) === null || _b === void 0 ? void 0 : _b.adapter.validate();
|
|
168
|
+
}
|
|
165
169
|
// optional method where you can safely check field types after database discovery was performed
|
|
166
|
-
if (this.options.completion && this.options.completion.
|
|
167
|
-
throw new Error(`
|
|
170
|
+
if (this.options.completion && !this.options.completion.adapter) {
|
|
171
|
+
throw new Error(`Completion adapter is required`);
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
instanceUniqueRepresentation(pluginOptions) {
|
|
@@ -206,7 +210,7 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
206
210
|
method: 'POST',
|
|
207
211
|
path: `/plugin/${this.pluginInstanceId}/doComplete`,
|
|
208
212
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, headers }) {
|
|
209
|
-
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
213
|
+
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
210
214
|
const { record } = body;
|
|
211
215
|
if ((_b = this.options.completion.rateLimit) === null || _b === void 0 ? void 0 : _b.limit) {
|
|
212
216
|
// rate limit
|
|
@@ -239,28 +243,9 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
239
243
|
"Be short, clear and precise. No quotes. Don't talk to me. Just write text\n";
|
|
240
244
|
}
|
|
241
245
|
process.env.HEAVY_DEBUG && console.log('🪲 OpenAI Prompt 🧠', content);
|
|
242
|
-
const
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
headers: {
|
|
246
|
-
'Content-Type': 'application/json',
|
|
247
|
-
'Authorization': `Bearer ${this.options.completion.params.apiKey}`
|
|
248
|
-
},
|
|
249
|
-
body: JSON.stringify({
|
|
250
|
-
model: this.options.completion.params.model || 'gpt-4o-mini',
|
|
251
|
-
messages: [
|
|
252
|
-
{
|
|
253
|
-
role: 'user',
|
|
254
|
-
content,
|
|
255
|
-
}
|
|
256
|
-
],
|
|
257
|
-
temperature: ((_m = this.options.completion.expert) === null || _m === void 0 ? void 0 : _m.temperature) || 0.7,
|
|
258
|
-
max_tokens: ((_o = this.options.completion.expert) === null || _o === void 0 ? void 0 : _o.maxTokens) || 50,
|
|
259
|
-
stop,
|
|
260
|
-
})
|
|
261
|
-
});
|
|
262
|
-
const data = yield resp.json();
|
|
263
|
-
let suggestion = data.choices[0].message.content + (data.choices[0].finish_reason === 'stop' ? (stop[0] === '.' && stop.length === 1 ? '. ' : '') : '');
|
|
246
|
+
const { content: respContent, finishReason } = yield this.options.completion.adapter.complete(content, (_m = (_l = this.options.completion) === null || _l === void 0 ? void 0 : _l.expert) === null || _m === void 0 ? void 0 : _m.stop, (_p = (_o = this.options.completion) === null || _o === void 0 ? void 0 : _o.expert) === null || _p === void 0 ? void 0 : _p.maxTokens);
|
|
247
|
+
const stop = ((_q = this.options.completion.expert) === null || _q === void 0 ? void 0 : _q.stop) || ['.'];
|
|
248
|
+
let suggestion = respContent + (finishReason === 'stop' ? (stop[0] === '.' && stop.length === 1 ? '. ' : '') : '');
|
|
264
249
|
if (suggestion.startsWith(currentVal)) {
|
|
265
250
|
suggestion = suggestion.slice(currentVal.length);
|
|
266
251
|
}
|
package/dist/types.js
CHANGED
|
@@ -1,15 +1 @@
|
|
|
1
|
-
// example options ussage:
|
|
2
|
-
//{
|
|
3
|
-
// htmlFieldName: 'description',
|
|
4
|
-
// completion: {
|
|
5
|
-
// provider: 'openai-chat-gpt',
|
|
6
|
-
// params: {
|
|
7
|
-
// apiKey: process.env.OPENAI_API_KEY as string,
|
|
8
|
-
// model: 'gpt-4o',
|
|
9
|
-
// },
|
|
10
|
-
// expert: {
|
|
11
|
-
// debounceTime: 250,
|
|
12
|
-
// }
|
|
13
|
-
// }
|
|
14
|
-
//}
|
|
15
1
|
export {};
|
package/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import type { IAdminForth, IHttpServer, AdminForthResource, AdminUser
|
|
2
|
+
import type { IAdminForth, IHttpServer, AdminForthResource, AdminUser } from "adminforth";
|
|
3
3
|
import type { PluginOptions } from './types.js';
|
|
4
4
|
import { AdminForthPlugin, Filters, RateLimiter } from "adminforth";
|
|
5
5
|
import * as cheerio from 'cheerio';
|
|
@@ -142,7 +142,7 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
|
|
145
|
-
(resourceConfig.hooks.create.afterSave
|
|
145
|
+
(resourceConfig.hooks.create.afterSave).push(async ({ record, adminUser }: { record: any, adminUser: AdminUser }) => {
|
|
146
146
|
// find all s3Paths in the html
|
|
147
147
|
const s3Paths = getAttachmentPathes(record[this.options.htmlFieldName])
|
|
148
148
|
|
|
@@ -157,7 +157,7 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
157
157
|
|
|
158
158
|
// after edit we need to delete attachments that are not in the html anymore
|
|
159
159
|
// and add new ones
|
|
160
|
-
(resourceConfig.hooks.edit.afterSave
|
|
160
|
+
(resourceConfig.hooks.edit.afterSave).push(
|
|
161
161
|
async ({ recordId, record, adminUser }: { recordId: any, record: any, adminUser: AdminUser }) => {
|
|
162
162
|
process.env.HEAVY_DEBUG && console.log('⚓ Cought hook', recordId, 'rec', record);
|
|
163
163
|
if (record[this.options.htmlFieldName] === undefined) {
|
|
@@ -188,7 +188,7 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
188
188
|
);
|
|
189
189
|
|
|
190
190
|
// after delete we need to delete all attachments
|
|
191
|
-
(resourceConfig.hooks.delete.afterSave
|
|
191
|
+
(resourceConfig.hooks.delete.afterSave).push(
|
|
192
192
|
async ({ record, adminUser }: { record: any, adminUser: AdminUser }) => {
|
|
193
193
|
const existingAparts = await adminforth.resource(this.options.attachments.attachmentResource).list(
|
|
194
194
|
[
|
|
@@ -202,15 +202,18 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
202
202
|
|
|
203
203
|
return { ok: true };
|
|
204
204
|
}
|
|
205
|
-
|
|
205
|
+
);
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
208
|
|
|
209
209
|
validateConfigAfterDiscover(adminforth: IAdminForth, resourceConfig: AdminForthResource) {
|
|
210
210
|
this.adminforth = adminforth;
|
|
211
|
+
if (this.options.completion?.adapter) {
|
|
212
|
+
this.options.completion?.adapter.validate();
|
|
213
|
+
}
|
|
211
214
|
// optional method where you can safely check field types after database discovery was performed
|
|
212
|
-
if (this.options.completion && this.options.completion.
|
|
213
|
-
throw new Error(`
|
|
215
|
+
if (this.options.completion && !this.options.completion.adapter) {
|
|
216
|
+
throw new Error(`Completion adapter is required`);
|
|
214
217
|
}
|
|
215
218
|
|
|
216
219
|
}
|
|
@@ -310,29 +313,10 @@ export default class RichEditorPlugin extends AdminForthPlugin {
|
|
|
310
313
|
}
|
|
311
314
|
|
|
312
315
|
process.env.HEAVY_DEBUG && console.log('🪲 OpenAI Prompt 🧠', content);
|
|
316
|
+
const { content: respContent, finishReason } = await this.options.completion.adapter.complete(content, this.options.completion?.expert?.stop, this.options.completion?.expert?.maxTokens);
|
|
313
317
|
const stop = this.options.completion.expert?.stop || ['.'];
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
headers: {
|
|
317
|
-
'Content-Type': 'application/json',
|
|
318
|
-
'Authorization': `Bearer ${this.options.completion.params.apiKey}`
|
|
319
|
-
},
|
|
320
|
-
body: JSON.stringify({
|
|
321
|
-
model: this.options.completion.params.model || 'gpt-4o-mini',
|
|
322
|
-
messages: [
|
|
323
|
-
{
|
|
324
|
-
role: 'user',
|
|
325
|
-
content,
|
|
326
|
-
}
|
|
327
|
-
],
|
|
328
|
-
temperature: this.options.completion.expert?.temperature || 0.7,
|
|
329
|
-
max_tokens: this.options.completion.expert?.maxTokens || 50,
|
|
330
|
-
stop,
|
|
331
|
-
})
|
|
332
|
-
});
|
|
333
|
-
const data = await resp.json();
|
|
334
|
-
let suggestion = data.choices[0].message.content + (
|
|
335
|
-
data.choices[0].finish_reason === 'stop' ? (
|
|
318
|
+
let suggestion = respContent + (
|
|
319
|
+
finishReason === 'stop' ? (
|
|
336
320
|
stop[0] === '.' && stop.length === 1 ? '. ' : ''
|
|
337
321
|
) : ''
|
|
338
322
|
);
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { CompletionAdapter } from "adminforth";
|
|
2
|
+
|
|
1
3
|
// example options ussage:
|
|
2
4
|
//{
|
|
3
5
|
// htmlFieldName: 'description',
|
|
@@ -52,27 +54,10 @@ export interface PluginOptions {
|
|
|
52
54
|
*/
|
|
53
55
|
completion?: {
|
|
54
56
|
/**
|
|
55
|
-
*
|
|
56
|
-
*/
|
|
57
|
-
provider: string;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The params are the parameters that will be passed to the completion provider.
|
|
57
|
+
* Adapter for completion.
|
|
61
58
|
*/
|
|
62
|
-
params: {
|
|
63
|
-
/**
|
|
64
|
-
* OpenAI API key. Go to https://platform.openai.com/, go to Dashboard -> API keys -> Create new secret key
|
|
65
|
-
* Paste value in your .env file OPENAI_API_KEY=your_key
|
|
66
|
-
* Set openAiApiKey: process.env.OPENAI_API_KEY to access it
|
|
67
|
-
*/
|
|
68
|
-
apiKey: string;
|
|
69
59
|
|
|
70
|
-
|
|
71
|
-
* Model name. Go to https://platform.openai.com/docs/models, select model and copy name.
|
|
72
|
-
* Default is `gpt-4o-mini`. Use e.g. more expensive `gpt-4o` for more powerful model.
|
|
73
|
-
*/
|
|
74
|
-
model?: string;
|
|
75
|
-
}
|
|
60
|
+
adapter: CompletionAdapter;
|
|
76
61
|
|
|
77
62
|
/**
|
|
78
63
|
* Expert settings
|
|
@@ -83,11 +68,6 @@ export interface PluginOptions {
|
|
|
83
68
|
*/
|
|
84
69
|
maxTokens?: number;
|
|
85
70
|
|
|
86
|
-
/**
|
|
87
|
-
* Temperature (0-1). Lower is more deterministic, higher is more unpredicted creative. Default is 0.7.
|
|
88
|
-
*/
|
|
89
|
-
temperature?: number;
|
|
90
|
-
|
|
91
71
|
/**
|
|
92
72
|
* Maximum number of last characters which will be used for completion for target field. Default is 500.
|
|
93
73
|
* Higher value will give better context but will cost more.
|