Skip to content

Commit

Permalink
[fix](copilot.service.ts): Remove deprecated imports and update key e…
Browse files Browse the repository at this point in the history
…vent handling logic

[feat](copilot.service.ts): Add new dependencies and features to copilotPlugin
[fix](copilot.service.ts): Update renderHint function to use new widget view component
[fix](copilot.service.ts): Throttle API calls to prevent excessive requests
[fix](copilot.service.ts): Update fetchAIHint function to use the config from localStorage
[fix](copilot.service.ts): Update API request headers to use token from localStorage
[fix](copilot-widget.component.ts): Update template to use updated CSS class
[fix](top-bar.component.ts): Update selector name to 'top-bar'
[fix](collaborative-editing.component.ts): Update import statement for component
[fix](workGround.component.html): Update component selector to use 'top-bar'
[fix](workGround.component.ts): Update import statement for component
[fix](workGround.component.ts): Pass provider to copilotPlugin in the configuration
[fix](README.md): Mark 'plugin-copilot' as supported
  • Loading branch information
ousc committed Jan 10, 2024
1 parent ecc95e7 commit 281c705
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 53 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Angular adapter for ProseMirror, only supports Angular 17+.(now this library is
- [X] `plugin-trailing`**(supported)**
- [X] `plugin-upload`**(supported)**
- [X] `plugin-collab`**(supported)**
- [ ] `plugin-copilot`**(planned)**
- [X] `plugin-copilot`**(supported)**

usage of plugins can be found in [example](https://github.com/ousc/ng-milkdown/tree/main/src/app/components);

Expand Down
13 changes: 13 additions & 0 deletions src/app/components/copilot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copilot Plugin

This plugin allows you to use Copilot in your app.

LocalStorage is used to store the model and the api key.

Set them before using the plugin.

```typescript
window.localStorage.setItem('openai-api-url', 'https://api.openai.com/v1/completions');
window.localStorage.setItem('openai-api-token', 'sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
window.localStorage.setItem('openai-api-config', '{"model":"davinci-002","max_tokens":7,"temperature":0,"top_p":1,"n":1,"stream":false,"logprobs":null}');
```
2 changes: 1 addition & 1 deletion src/app/components/copilot/copilot-widget.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {NgMilkdownWidgetComp} from "../../../../projects/ng-milkdown/src/lib/dir

@Component({
selector: 'copilot-widget',
template: `<span class="text-gray-50">[提示:{{message}}]</span>`,
template: `<span class="text-gray-400 dark:text-gray-50">{{ message }}</span>`,
styles: [],
standalone: true
})
Expand Down
59 changes: 25 additions & 34 deletions src/app/components/copilot/copilot.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {$prose} from '@milkdown/utils';
import {Plugin, PluginKey} from '@milkdown/prose/state';
import {Decoration, DecorationSet} from "@milkdown/prose/view";
import {DecorationSet} from "@milkdown/prose/view";
import {Ctx} from "@milkdown/ctx";
import {editorViewCtx, parserCtx, serializerCtx} from "@milkdown/core";
import {DOMSerializer, DOMParser} from "prosemirror-model";
import {DOMParser, DOMSerializer} from "prosemirror-model";
import {Injectable} from "@angular/core";
import {HttpClient} from "@angular/common/http";
import {NgMilkdownProvider} from "../../../../projects/ng-milkdown/src/lib/component/ng-milkdown-provider.component";
import {CopilotWidget} from "./copilot-widget.component";
import {throttle} from "../../shared/debounce_throttle";

@Injectable()
export class CopilotService {
Expand All @@ -20,14 +23,13 @@ export class CopilotService {
}

keyDownHandler(ctx: Ctx, event: KeyboardEvent) {
if (event.key === 'Enter' || event.code === 'Space') {
if (event.key === 'Enter' || event.code === 'Space' || event.code === 'Backspace' || event.code === 'Delete') {
this.getHint(ctx)
return;
}
if (event.key === "Tab") {
// prevent the browser from focusing on the next element.
event.preventDefault();

this.applyHint(ctx);
return;
}
Expand All @@ -36,7 +38,7 @@ export class CopilotService {
}

copilotPluginKey = new PluginKey('milkdown-copilot');
copilotPlugin = $prose((ctx) => new Plugin({
copilotPlugin = (provider: NgMilkdownProvider) => $prose((ctx) => new Plugin({
key: this.copilotPluginKey,
props: {
handleKeyDown: (view, event) => {
Expand All @@ -54,32 +56,24 @@ export class CopilotService {
apply:(tr, value, _prevState, state)=> {
if (!this.enabled) return value;
const message = tr.getMeta(this.copilotPluginKey);
console.log(message)
if (typeof message !== 'string') return value;

if (message.length === 0) {
return {...this.initialState};
}

const {to} = tr.selection;
const widget = Decoration.widget(to + 1, () => this.renderHint(message));
console.log(widget)
const hint = provider.createWidgetView({as: "span", component: CopilotWidget});
return {
deco: DecorationSet.create(state.doc, [widget]),
message,
deco: DecorationSet.create(tr.doc, [
hint(to, {message}),
]),
message
};
}
}
}))

renderHint(message: string) {
const dom = document.createElement('pre');
dom.className = "copilot-hint"
dom.innerHTML = message;
console.log(dom)
return dom;
}

getHint(ctx: Ctx) {
const view = ctx.get(editorViewCtx);
const {state} = view;
Expand All @@ -92,12 +86,12 @@ export class CopilotService {
if (!doc) return;

const markdown = serializer(doc);
this.fetchAIHint(markdown).subscribe((hint: any) => {
const tr = view.state.tr;
console.log({hint: hint.choices[0].text})
console.log(tr);
view.dispatch(tr.setMeta(this.copilotPluginKey, hint.choices[0].text))
});
throttle(() => {
this.fetchAIHint(markdown).subscribe((hint: any) => {
const tr = view.state.tr;
view.dispatch(tr.setMeta(this.copilotPluginKey, hint.choices[0].text))
});
}, 2000);
}

applyHint(ctx: Ctx) {
Expand Down Expand Up @@ -127,18 +121,15 @@ export class CopilotService {
}

fetchAIHint(prompt: string) {
return this.http.post(localStorage.getItem('openai-api-url'), {
model: 'davinci',
prompt,
"max_tokens": 7,
"temperature": 0,
"top_p": 1,
"n": 1,
"stream": false,
"logprobs": null
const config = JSON.parse(localStorage.getItem('openai-api-config') ?? "{}"); // '{"model":"davinci-002","max_tokens":7,"temperature":0,"top_p":1,"n":1,"stream":false,"logprobs":null}'
const url = localStorage.getItem('openai-api-url');
const token = localStorage.getItem('openai-api-token');
return this.http.post(url, {
...config,
prompt
}, {
headers: {
Authorization: 'Bearer ' + localStorage.getItem('openai-api-token')
Authorization: 'Bearer ' + token
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/top-bar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {NgMilkdownProvider} from "../../../projects/ng-milkdown/src/lib/componen
import {CopilotService} from "./copilot/copilot.service";

@Component({
selector: 'tool-bar',
selector: 'top-bar',
template: `
<div class="absolute top-0 h-10 w-full border-b border-nord4 dark:divide-gray-600 dark:border-gray-600">
<div class="prose mx-auto flex">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {RouterLink, RouterOutlet} from "@angular/router";
import {FormsModule} from "@angular/forms";
import {NgMilkdown} from "../../../../projects/ng-milkdown/src/lib/ng-milkdown.component";
import {HttpClientModule} from "@angular/common/http";
import {ToolBarComponent} from "../../components/tool-bar.component";
import {TopBarComponent} from "../../components/top-bar.component";
import {Editor, editorViewCtx, editorViewOptionsCtx} from "@milkdown/core";
import {collab, collabServiceCtx} from "@milkdown/plugin-collab";
import {WebsocketProvider} from 'y-websocket';
Expand Down Expand Up @@ -97,7 +97,7 @@ export interface DialogData {
}
`
],
imports: [CommonModule, RouterOutlet, FormsModule, NgMilkdownProvider, NgMilkdown, HttpClientModule, ToolBarComponent, RouterLink],
imports: [CommonModule, RouterOutlet, FormsModule, NgMilkdownProvider, NgMilkdown, HttpClientModule, TopBarComponent, RouterLink],
standalone: true
})
export class CollaborativeEditingComponent implements OnDestroy{
Expand Down
2 changes: 1 addition & 1 deletion src/app/routes/work-ground/workGround.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<article class="prose lg:prose-xl"></article>
<div class="relative h-full pt-10">
@if (editor && !loading) {
<tool-bar class="opacity-80" [provider]="provider"/>
<top-bar class="opacity-80" [provider]="provider"/>
}
<div class="h-full overflow-auto overscroll-none ctn flex flex-col px-4">
<ng-milkdown-provider #provider>
Expand Down
26 changes: 13 additions & 13 deletions src/app/routes/work-ground/workGround.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {upload} from "@milkdown/plugin-upload";
import {tableSelectorPlugin} from "../../components/table-selector/tableSelectorPlugin";
import {TableTooltip, tableTooltip, tableTooltipCtx} from '../../components/table-selector/table-tooltip.component';
import {MathBlock} from "../../components/math-block.component";
import {ToolBarComponent} from "../../components/tool-bar.component";
import {TopBarComponent} from "../../components/top-bar.component";
import {CopilotService} from "../../components/copilot/copilot.service";
import {
NgMilkdownProvider
Expand All @@ -45,7 +45,7 @@ import {Spinner} from "../../components/spinner.component";
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet, FormsModule, NgMilkdown, HttpClientModule, ToolBarComponent, NgMilkdownProvider, Spinner],
imports: [CommonModule, RouterOutlet, FormsModule, NgMilkdown, HttpClientModule, TopBarComponent, NgMilkdownProvider, Spinner],
templateUrl: './workGround.component.html',
styleUrl: './workGround.component.scss',
providers: [CopilotService]
Expand Down Expand Up @@ -106,15 +106,6 @@ export class WorkGroundComponent implements OnInit {
});
}
},
{
plugin: indent,
config: ctx => {
ctx.set(indentConfig.key as any, {
type: 'space',
size: 4,
});
}
},
{
plugin: this.tooltip,
config: ctx => {
Expand Down Expand Up @@ -149,7 +140,7 @@ export class WorkGroundComponent implements OnInit {
}
},
linkPlugin(this.provider),
this.copilotService.copilotPlugin,
this.copilotService.copilotPlugin(this.provider),
tableTooltip,
{
plugin: tableTooltipCtx,
Expand All @@ -162,6 +153,15 @@ export class WorkGroundComponent implements OnInit {
}
},
tableSelectorPlugin(this.provider),
{
plugin: indent,
config: ctx => {
ctx.set(indentConfig.key as any, {
type: 'indent',
size: 4,
});
}
},
];
this.value = markdown;
});
Expand All @@ -179,7 +179,7 @@ export class WorkGroundComponent implements OnInit {
plugins: NgMilkdownPlugin[] = null;

onChange(markdownText: any) {
console.log('markdown changed!', {markdownText})
// console.log('markdown changed!', {markdownText})
}

config = (ctx: any) => {
Expand Down
33 changes: 33 additions & 0 deletions src/app/shared/debounce_throttle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export function debounce(fn: Function, delay = 800, ...args: any[]) {
// @ts-ignore
let context = this;
if (timer !== null) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(context, args);
timer = null
}, delay)
}


let timer: number = null;
let startTime = Date.now();

export function throttle(fn: Function, wait = 800, ...args: any[]) {
let curTime = Date.now();
let remaining = wait - (curTime - startTime);
// @ts-ignore
let context = this;

clearTimeout(timer);

if (remaining <= 0) {
fn.apply(context, args);
startTime = Date.now();
} else {
timer = setTimeout(()=>{
fn.apply(context, args);
}, remaining); // 如果小于wait 保证在差值时间后执行
}
}

0 comments on commit 281c705

Please sign in to comment.