@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 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
@@ -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.provider !== 'openai-chat-gpt') {
167
- throw new Error(`Invalid provider ${this.options.completion.provider}`);
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 stop = ((_l = this.options.completion.expert) === null || _l === void 0 ? void 0 : _l.stop) || ['.'];
243
- const resp = yield fetch('https://api.openai.com/v1/chat/completions', {
244
- method: 'POST',
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, AfterSaveFunction } from "adminforth";
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 as Array<AfterSaveFunction>).push(async ({ record, adminUser }: { record: any, adminUser: AdminUser }) => {
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 as Array<AfterSaveFunction>).push(
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 as Array<AfterSaveFunction>).push(
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.provider !== 'openai-chat-gpt') {
213
- throw new Error(`Invalid provider ${this.options.completion.provider}`);
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
- const resp = await fetch('https://api.openai.com/v1/chat/completions', {
315
- method: 'POST',
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/rich-editor",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
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
- * The provider is the name of the plugin that will be used to provide completions.
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.