monacopilot 0.10.7 → 0.10.8
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 +19 -4
- package/build/index.d.mts +21 -2
- package/build/index.d.ts +21 -2
- package/build/index.js +1 -1
- package/build/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
- [Max Context Lines](#max-context-lines)
|
|
28
28
|
- [Copilot Options](#copilot-options)
|
|
29
29
|
- [Changing the Provider and Model](#changing-the-provider-and-model)
|
|
30
|
+
- [Dynamically Updating Provider and Model](#dynamically-updating-provider-and-model)
|
|
30
31
|
- [Custom Model](#custom-model)
|
|
31
32
|
- [Completion Request Options](#completion-request-options)
|
|
32
33
|
- [Custom Headers](#custom-headers)
|
|
@@ -113,6 +114,8 @@ registerCompletion(monaco, editor, {
|
|
|
113
114
|
});
|
|
114
115
|
```
|
|
115
116
|
|
|
117
|
+
The `registerCompletion` function returns a `completion` object with useful methods such as `trigger` and `dispose`. The `trigger` method is explained later in this document. The `dispose` method should be used to clean up resources when the completion object is no longer needed. For instance, in a React component, you can call `completion.dispose()` within the `useEffect` cleanup function to ensure proper disposal when the component unmounts.
|
|
118
|
+
|
|
116
119
|
🎉 Congratulations! The AI auto-completion is now connected to the Monaco Editor. Start typing and see completions in the editor.
|
|
117
120
|
|
|
118
121
|
## Register Completion Options
|
|
@@ -127,10 +130,11 @@ registerCompletion(monaco, editor, {
|
|
|
127
130
|
});
|
|
128
131
|
```
|
|
129
132
|
|
|
130
|
-
| Trigger | Description
|
|
131
|
-
| -------------------- |
|
|
132
|
-
| `'onIdle'` (default) |
|
|
133
|
-
| `'onTyping'` |
|
|
133
|
+
| Trigger | Description | Notes |
|
|
134
|
+
| -------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
135
|
+
| `'onIdle'` (default) | Provides completions after a brief pause in typing. | This approach is less resource-intensive, as it only initiates a request when the editor is idle. |
|
|
136
|
+
| `'onTyping'` | Provides completions in real-time as you type. | Best suited for models with low response latency, such as Groq models. This trigger mode initiates additional background requests to deliver real-time suggestions, a method known as predictive caching. |
|
|
137
|
+
| `'onDemand'` | Does not provide completions automatically. | Completions are triggered manually using the `trigger` function from the `registerCompletion` return. This allows for precise control over when completions are provided. |
|
|
134
138
|
|
|
135
139
|
[OnTyping Demo](https://github.com/user-attachments/assets/22c2ce44-334c-4963-b853-01b890b8e39f)
|
|
136
140
|
|
|
@@ -272,6 +276,17 @@ There are other providers and models available. Here is a list:
|
|
|
272
276
|
| OpenAI | `gpt-4o`, `gpt-4o-mini`, `o1-preview`, `o1-mini` |
|
|
273
277
|
| Anthropic | `claude-3.5-Sonnet`, `claude-3-opus`, `claude-3-sonnet`, `claude-3-haiku` |
|
|
274
278
|
|
|
279
|
+
#### Dynamically Updating Provider and Model
|
|
280
|
+
|
|
281
|
+
You can change the provider and model at runtime using the `setModel` method.
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
copilot.setModel({
|
|
285
|
+
provider: 'anthropic',
|
|
286
|
+
model: 'claude-3-haiku',
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
275
290
|
### Custom Model
|
|
276
291
|
|
|
277
292
|
You can use a custom AI model that isn't built into Monacopilot by setting up a `model` when you create a new Copilot. This feature lets you connect to AI models from other services or your own custom-built models.
|
package/build/index.d.mts
CHANGED
|
@@ -54,6 +54,20 @@ type CustomCopilotModel = {
|
|
|
54
54
|
*/
|
|
55
55
|
transformResponse: CustomCopilotModelTransformResponse;
|
|
56
56
|
};
|
|
57
|
+
type SetModelOptions = {
|
|
58
|
+
/**
|
|
59
|
+
* The provider to set (e.g., 'openai', 'anthropic', 'groq').
|
|
60
|
+
*/
|
|
61
|
+
provider: CopilotProvider;
|
|
62
|
+
/**
|
|
63
|
+
* The model to use for copilot AI requests.
|
|
64
|
+
* This can be either:
|
|
65
|
+
* 1. A predefined model name (e.g. 'claude-3-opus'): Use this option if you want to use a model that is built into Monacopilot.
|
|
66
|
+
* If you choose this option, also set the `provider` property to the corresponding provider of the model.
|
|
67
|
+
* 2. A custom model configuration object: Use this option if you want to use a AI model from a third-party service or your own custom model.
|
|
68
|
+
*/
|
|
69
|
+
model: CopilotModel | CustomCopilotModel;
|
|
70
|
+
};
|
|
57
71
|
type CustomCopilotModelConfig = (apiKey: string, prompt: {
|
|
58
72
|
system: string;
|
|
59
73
|
user: string;
|
|
@@ -251,8 +265,8 @@ interface CompletionMetadata {
|
|
|
251
265
|
|
|
252
266
|
declare class Copilot {
|
|
253
267
|
private readonly apiKey;
|
|
254
|
-
private
|
|
255
|
-
private
|
|
268
|
+
private provider;
|
|
269
|
+
private model;
|
|
256
270
|
/**
|
|
257
271
|
* Initializes the Copilot instance with an API key and optional configuration.
|
|
258
272
|
* @param apiKey - The API key for the chosen provider.
|
|
@@ -270,6 +284,11 @@ declare class Copilot {
|
|
|
270
284
|
* @returns A promise resolving to the completion response or an error.
|
|
271
285
|
*/
|
|
272
286
|
complete(request: CompletionRequest): Promise<CompletionResponse>;
|
|
287
|
+
/**
|
|
288
|
+
* Sets the model and provider for the Copilot.
|
|
289
|
+
* @param options - The options for setting the model and provider.
|
|
290
|
+
*/
|
|
291
|
+
setModel({ model, provider }: SetModelOptions): void;
|
|
273
292
|
}
|
|
274
293
|
|
|
275
294
|
/**
|
package/build/index.d.ts
CHANGED
|
@@ -54,6 +54,20 @@ type CustomCopilotModel = {
|
|
|
54
54
|
*/
|
|
55
55
|
transformResponse: CustomCopilotModelTransformResponse;
|
|
56
56
|
};
|
|
57
|
+
type SetModelOptions = {
|
|
58
|
+
/**
|
|
59
|
+
* The provider to set (e.g., 'openai', 'anthropic', 'groq').
|
|
60
|
+
*/
|
|
61
|
+
provider: CopilotProvider;
|
|
62
|
+
/**
|
|
63
|
+
* The model to use for copilot AI requests.
|
|
64
|
+
* This can be either:
|
|
65
|
+
* 1. A predefined model name (e.g. 'claude-3-opus'): Use this option if you want to use a model that is built into Monacopilot.
|
|
66
|
+
* If you choose this option, also set the `provider` property to the corresponding provider of the model.
|
|
67
|
+
* 2. A custom model configuration object: Use this option if you want to use a AI model from a third-party service or your own custom model.
|
|
68
|
+
*/
|
|
69
|
+
model: CopilotModel | CustomCopilotModel;
|
|
70
|
+
};
|
|
57
71
|
type CustomCopilotModelConfig = (apiKey: string, prompt: {
|
|
58
72
|
system: string;
|
|
59
73
|
user: string;
|
|
@@ -251,8 +265,8 @@ interface CompletionMetadata {
|
|
|
251
265
|
|
|
252
266
|
declare class Copilot {
|
|
253
267
|
private readonly apiKey;
|
|
254
|
-
private
|
|
255
|
-
private
|
|
268
|
+
private provider;
|
|
269
|
+
private model;
|
|
256
270
|
/**
|
|
257
271
|
* Initializes the Copilot instance with an API key and optional configuration.
|
|
258
272
|
* @param apiKey - The API key for the chosen provider.
|
|
@@ -270,6 +284,11 @@ declare class Copilot {
|
|
|
270
284
|
* @returns A promise resolving to the completion response or an error.
|
|
271
285
|
*/
|
|
272
286
|
complete(request: CompletionRequest): Promise<CompletionResponse>;
|
|
287
|
+
/**
|
|
288
|
+
* Sets the model and provider for the Copilot.
|
|
289
|
+
* @param options - The options for setting the model and provider.
|
|
290
|
+
*/
|
|
291
|
+
setModel({ model, provider }: SetModelOptions): void;
|
|
273
292
|
}
|
|
274
293
|
|
|
275
294
|
/**
|
package/build/index.js
CHANGED
|
@@ -38,7 +38,7 @@ ${p}
|
|
|
38
38
|
${s}
|
|
39
39
|
${m}
|
|
40
40
|
</task>
|
|
41
|
-
`};function j(e){return{system:ye(e),user:Oe(e)}}var ne={"claude-3.5-sonnet":8192,"claude-3-opus":4096,"claude-3-haiku":4096,"claude-3-sonnet":4096};var Re={createRequestBody:(e,t)=>{let n=e==="o1-preview"||e==="o1-mini"?[{role:"user",content:t.user}]:[{role:"system",content:t.system},{role:"user",content:t.user}];return{model:K(e),temperature:M,messages:n}},createHeaders:e=>({"Content-Type":"application/json",Authorization:`Bearer ${e}`}),parseCompletion:e=>e.choices?.length?e.choices[0].message.content:null},Me={createRequestBody:(e,t)=>({model:K(e),temperature:M,messages:[{role:"system",content:t.system},{role:"user",content:t.user}]}),createHeaders:e=>({"Content-Type":"application/json",Authorization:`Bearer ${e}`}),parseCompletion:e=>e.choices?.length?e.choices[0].message.content:null},Ie={createRequestBody:(e,t)=>({model:K(e),temperature:M,system:t.system,messages:[{role:"user",content:t.user}],max_tokens:be(e)}),createHeaders:e=>({"Content-Type":"application/json","x-api-key":e,"anthropic-version":"2023-06-01"}),parseCompletion:e=>!e.content||typeof e.content!="string"?null:e.content},G={openai:Re,groq:Me,anthropic:Ie},re=(e,t,o)=>G[t].createRequestBody(e,o),ie=(e,t)=>G[t].createHeaders(e),se=(e,t)=>G[t].parseCompletion(e),K=e=>X[e],le=e=>Z[e],be=e=>ne[e]||4096;var L=class{constructor(t,o={}){if(!t)throw new Error("Please provide an API key.");this.apiKey=t,this.provider=o.provider??z,this.model=o.model??J,this.validateInputs()}validateInputs(){if(!q.includes(this.provider))throw new Error(`The provider "${this.provider}" is not supported. Please choose a supported provider: ${y(q)}. If you're using a custom model, you don't need to specify a provider.`);if(typeof this.model=="string"&&!F[this.provider].includes(this.model)){let t=y(F[this.provider]);throw new Error(`Model "${this.model}" is not supported by the "${this.provider}" provider. Supported models: ${t}`)}}async complete(t){let{body:o,options:n}=t,{completionMetadata:r}=o,{headers:i={},customPrompt:l}=n??{},c=j(r),a=l?{...c,...l(r)}:c,s=le(this.provider),m,u=ie(this.apiKey,this.provider);if(typeof this.model=="object"&&"config"in this.model){let p=this.model.config(this.apiKey,a);s=p.endpoint??s,m=p.body??{},u={...u,...p.headers}}else m=re(this.model,this.provider,a);u={...u,...i};try{let p=await v.POST(s,m,{headers:u}),d;if(typeof this.model=="object"&&"transformResponse"in this.model){let C=this.model.transformResponse(p);d={completion:C.text??C.completion}}else d={completion:se(p,this.provider)};return d}catch(p){return{error:g(p,"COPILOT_COMPLETION_FETCH_ERROR").message,completion:null}}}};var _=class e{constructor(t){this.formattedCompletion="";this.formattedCompletion=t}static create(t){return new e(t)}setCompletion(t){return this.formattedCompletion=t,this}removeInvalidLineBreaks(){return this.formattedCompletion=this.formattedCompletion.trimEnd(),this}removeMarkdownCodeSyntax(){return this.formattedCompletion=this.removeMarkdownCodeBlocks(this.formattedCompletion),this}removeMarkdownCodeBlocks(t){let o=/```[\s\S]*?```/g,n=t,r;for(;(r=o.exec(t))!==null;){let i=r[0],l=i.split(`
|
|
41
|
+
`};function j(e){return{system:ye(e),user:Oe(e)}}var ne={"claude-3.5-sonnet":8192,"claude-3-opus":4096,"claude-3-haiku":4096,"claude-3-sonnet":4096};var Re={createRequestBody:(e,t)=>{let n=e==="o1-preview"||e==="o1-mini"?[{role:"user",content:t.user}]:[{role:"system",content:t.system},{role:"user",content:t.user}];return{model:K(e),temperature:M,messages:n}},createHeaders:e=>({"Content-Type":"application/json",Authorization:`Bearer ${e}`}),parseCompletion:e=>e.choices?.length?e.choices[0].message.content:null},Me={createRequestBody:(e,t)=>({model:K(e),temperature:M,messages:[{role:"system",content:t.system},{role:"user",content:t.user}]}),createHeaders:e=>({"Content-Type":"application/json",Authorization:`Bearer ${e}`}),parseCompletion:e=>e.choices?.length?e.choices[0].message.content:null},Ie={createRequestBody:(e,t)=>({model:K(e),temperature:M,system:t.system,messages:[{role:"user",content:t.user}],max_tokens:be(e)}),createHeaders:e=>({"Content-Type":"application/json","x-api-key":e,"anthropic-version":"2023-06-01"}),parseCompletion:e=>!e.content||typeof e.content!="string"?null:e.content},G={openai:Re,groq:Me,anthropic:Ie},re=(e,t,o)=>G[t].createRequestBody(e,o),ie=(e,t)=>G[t].createHeaders(e),se=(e,t)=>G[t].parseCompletion(e),K=e=>X[e],le=e=>Z[e],be=e=>ne[e]||4096;var L=class{constructor(t,o={}){if(!t)throw new Error("Please provide an API key.");this.apiKey=t,this.provider=o.provider??z,this.model=o.model??J,this.validateInputs()}validateInputs(){if(!q.includes(this.provider))throw new Error(`The provider "${this.provider}" is not supported. Please choose a supported provider: ${y(q)}. If you're using a custom model, you don't need to specify a provider.`);if(typeof this.model=="string"&&!F[this.provider].includes(this.model)){let t=y(F[this.provider]);throw new Error(`Model "${this.model}" is not supported by the "${this.provider}" provider. Supported models: ${t}`)}}async complete(t){let{body:o,options:n}=t,{completionMetadata:r}=o,{headers:i={},customPrompt:l}=n??{},c=j(r),a=l?{...c,...l(r)}:c,s=le(this.provider),m,u=ie(this.apiKey,this.provider);if(typeof this.model=="object"&&"config"in this.model){let p=this.model.config(this.apiKey,a);s=p.endpoint??s,m=p.body??{},u={...u,...p.headers}}else m=re(this.model,this.provider,a);u={...u,...i};try{let p=await v.POST(s,m,{headers:u}),d;if(typeof this.model=="object"&&"transformResponse"in this.model){let C=this.model.transformResponse(p);d={completion:C.text??C.completion}}else d={completion:se(p,this.provider)};return d}catch(p){return{error:g(p,"COPILOT_COMPLETION_FETCH_ERROR").message,completion:null}}}setModel({model:t,provider:o}){this.model=t,this.provider=o,this.validateInputs()}};var _=class e{constructor(t){this.formattedCompletion="";this.formattedCompletion=t}static create(t){return new e(t)}setCompletion(t){return this.formattedCompletion=t,this}removeInvalidLineBreaks(){return this.formattedCompletion=this.formattedCompletion.trimEnd(),this}removeMarkdownCodeSyntax(){return this.formattedCompletion=this.removeMarkdownCodeBlocks(this.formattedCompletion),this}removeMarkdownCodeBlocks(t){let o=/```[\s\S]*?```/g,n=t,r;for(;(r=o.exec(t))!==null;){let i=r[0],l=i.split(`
|
|
42
42
|
`).slice(1,-1).join(`
|
|
43
43
|
`);n=n.replace(i,l)}return n.trim()}removeExcessiveNewlines(){return this.formattedCompletion=this.formattedCompletion.replace(/\n{3,}/g,`
|
|
44
44
|
|
package/build/index.mjs
CHANGED
|
@@ -38,7 +38,7 @@ ${p}
|
|
|
38
38
|
${s}
|
|
39
39
|
${m}
|
|
40
40
|
</task>
|
|
41
|
-
`};function U(e){return{system:Ce(e),user:ge(e)}}var te={"claude-3.5-sonnet":8192,"claude-3-opus":4096,"claude-3-haiku":4096,"claude-3-sonnet":4096};var he={createRequestBody:(e,t)=>{let n=e==="o1-preview"||e==="o1-mini"?[{role:"user",content:t.user}]:[{role:"system",content:t.system},{role:"user",content:t.user}];return{model:j(e),temperature:M,messages:n}},createHeaders:e=>({"Content-Type":"application/json",Authorization:`Bearer ${e}`}),parseCompletion:e=>e.choices?.length?e.choices[0].message.content:null},fe={createRequestBody:(e,t)=>({model:j(e),temperature:M,messages:[{role:"system",content:t.system},{role:"user",content:t.user}]}),createHeaders:e=>({"Content-Type":"application/json",Authorization:`Bearer ${e}`}),parseCompletion:e=>e.choices?.length?e.choices[0].message.content:null},Ee={createRequestBody:(e,t)=>({model:j(e),temperature:M,system:t.system,messages:[{role:"user",content:t.user}],max_tokens:xe(e)}),createHeaders:e=>({"Content-Type":"application/json","x-api-key":e,"anthropic-version":"2023-06-01"}),parseCompletion:e=>!e.content||typeof e.content!="string"?null:e.content},V={openai:he,groq:fe,anthropic:Ee},oe=(e,t,o)=>V[t].createRequestBody(e,o),ne=(e,t)=>V[t].createHeaders(e),re=(e,t)=>V[t].parseCompletion(e),j=e=>W[e],ie=e=>J[e],xe=e=>te[e]||4096;var G=class{constructor(t,o={}){if(!t)throw new Error("Please provide an API key.");this.apiKey=t,this.provider=o.provider??X,this.model=o.model??Y,this.validateInputs()}validateInputs(){if(!k.includes(this.provider))throw new Error(`The provider "${this.provider}" is not supported. Please choose a supported provider: ${y(k)}. If you're using a custom model, you don't need to specify a provider.`);if(typeof this.model=="string"&&!H[this.provider].includes(this.model)){let t=y(H[this.provider]);throw new Error(`Model "${this.model}" is not supported by the "${this.provider}" provider. Supported models: ${t}`)}}async complete(t){let{body:o,options:n}=t,{completionMetadata:r}=o,{headers:i={},customPrompt:l}=n??{},c=U(r),a=l?{...c,...l(r)}:c,s=ie(this.provider),m,u=ne(this.apiKey,this.provider);if(typeof this.model=="object"&&"config"in this.model){let p=this.model.config(this.apiKey,a);s=p.endpoint??s,m=p.body??{},u={...u,...p.headers}}else m=oe(this.model,this.provider,a);u={...u,...i};try{let p=await v.POST(s,m,{headers:u}),d;if(typeof this.model=="object"&&"transformResponse"in this.model){let C=this.model.transformResponse(p);d={completion:C.text??C.completion}}else d={completion:re(p,this.provider)};return d}catch(p){return{error:g(p,"COPILOT_COMPLETION_FETCH_ERROR").message,completion:null}}}};var L=class e{constructor(t){this.formattedCompletion="";this.formattedCompletion=t}static create(t){return new e(t)}setCompletion(t){return this.formattedCompletion=t,this}removeInvalidLineBreaks(){return this.formattedCompletion=this.formattedCompletion.trimEnd(),this}removeMarkdownCodeSyntax(){return this.formattedCompletion=this.removeMarkdownCodeBlocks(this.formattedCompletion),this}removeMarkdownCodeBlocks(t){let o=/```[\s\S]*?```/g,n=t,r;for(;(r=o.exec(t))!==null;){let i=r[0],l=i.split(`
|
|
41
|
+
`};function U(e){return{system:Ce(e),user:ge(e)}}var te={"claude-3.5-sonnet":8192,"claude-3-opus":4096,"claude-3-haiku":4096,"claude-3-sonnet":4096};var he={createRequestBody:(e,t)=>{let n=e==="o1-preview"||e==="o1-mini"?[{role:"user",content:t.user}]:[{role:"system",content:t.system},{role:"user",content:t.user}];return{model:j(e),temperature:M,messages:n}},createHeaders:e=>({"Content-Type":"application/json",Authorization:`Bearer ${e}`}),parseCompletion:e=>e.choices?.length?e.choices[0].message.content:null},fe={createRequestBody:(e,t)=>({model:j(e),temperature:M,messages:[{role:"system",content:t.system},{role:"user",content:t.user}]}),createHeaders:e=>({"Content-Type":"application/json",Authorization:`Bearer ${e}`}),parseCompletion:e=>e.choices?.length?e.choices[0].message.content:null},Ee={createRequestBody:(e,t)=>({model:j(e),temperature:M,system:t.system,messages:[{role:"user",content:t.user}],max_tokens:xe(e)}),createHeaders:e=>({"Content-Type":"application/json","x-api-key":e,"anthropic-version":"2023-06-01"}),parseCompletion:e=>!e.content||typeof e.content!="string"?null:e.content},V={openai:he,groq:fe,anthropic:Ee},oe=(e,t,o)=>V[t].createRequestBody(e,o),ne=(e,t)=>V[t].createHeaders(e),re=(e,t)=>V[t].parseCompletion(e),j=e=>W[e],ie=e=>J[e],xe=e=>te[e]||4096;var G=class{constructor(t,o={}){if(!t)throw new Error("Please provide an API key.");this.apiKey=t,this.provider=o.provider??X,this.model=o.model??Y,this.validateInputs()}validateInputs(){if(!k.includes(this.provider))throw new Error(`The provider "${this.provider}" is not supported. Please choose a supported provider: ${y(k)}. If you're using a custom model, you don't need to specify a provider.`);if(typeof this.model=="string"&&!H[this.provider].includes(this.model)){let t=y(H[this.provider]);throw new Error(`Model "${this.model}" is not supported by the "${this.provider}" provider. Supported models: ${t}`)}}async complete(t){let{body:o,options:n}=t,{completionMetadata:r}=o,{headers:i={},customPrompt:l}=n??{},c=U(r),a=l?{...c,...l(r)}:c,s=ie(this.provider),m,u=ne(this.apiKey,this.provider);if(typeof this.model=="object"&&"config"in this.model){let p=this.model.config(this.apiKey,a);s=p.endpoint??s,m=p.body??{},u={...u,...p.headers}}else m=oe(this.model,this.provider,a);u={...u,...i};try{let p=await v.POST(s,m,{headers:u}),d;if(typeof this.model=="object"&&"transformResponse"in this.model){let C=this.model.transformResponse(p);d={completion:C.text??C.completion}}else d={completion:re(p,this.provider)};return d}catch(p){return{error:g(p,"COPILOT_COMPLETION_FETCH_ERROR").message,completion:null}}}setModel({model:t,provider:o}){this.model=t,this.provider=o,this.validateInputs()}};var L=class e{constructor(t){this.formattedCompletion="";this.formattedCompletion=t}static create(t){return new e(t)}setCompletion(t){return this.formattedCompletion=t,this}removeInvalidLineBreaks(){return this.formattedCompletion=this.formattedCompletion.trimEnd(),this}removeMarkdownCodeSyntax(){return this.formattedCompletion=this.removeMarkdownCodeBlocks(this.formattedCompletion),this}removeMarkdownCodeBlocks(t){let o=/```[\s\S]*?```/g,n=t,r;for(;(r=o.exec(t))!==null;){let i=r[0],l=i.split(`
|
|
42
42
|
`).slice(1,-1).join(`
|
|
43
43
|
`);n=n.replace(i,l)}return n.trim()}removeExcessiveNewlines(){return this.formattedCompletion=this.formattedCompletion.replace(/\n{3,}/g,`
|
|
44
44
|
|