forked from tidbcloud/tisqleditor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplaceholder.ts
149 lines (132 loc) · 3.65 KB
/
placeholder.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import { Extension } from '@codemirror/state'
import {
Decoration,
DecorationSet,
EditorView,
ViewPlugin,
ViewUpdate,
WidgetType
} from '@codemirror/view'
import {
activePromptInput,
getAiWidgetOptions,
isPromptInputActive
} from './prompt-input'
import { isAppleOs } from './utils'
const aiPlaceholderTheme = EditorView.baseTheme({
'.cm-ai-placeholder': {
fontSize: '12px',
paddingLeft: '8px',
'& span': {
color: '#0CA6F2',
cursor: 'pointer'
}
},
'&light .cm-ai-placeholder': {
color: '#bbb'
},
'&dark .cm-ai-placeholder': {
color: '#c6c6c6'
},
'&.cm-focused .cm-cursor.cm-ai-placeholder-cursor': {
// borderLeft: '3px solid #0CA6F2'
}
})
class AIPlaceholderWidget extends WidgetType {
constructor(public emptyDoc: boolean) {
super()
}
toDOM(view: EditorView): HTMLElement {
const cmd = isAppleOs() ? 'Command' : 'Ctrl'
const root = document.createElement('span')
root.className = 'cm-ai-placeholder'
if (this.emptyDoc) {
root.innerHTML =
getAiWidgetOptions().placeholderEmptyDocElement ??
`Press '${cmd} + I' or <span>click here</span> to use AI`
} else {
root.innerHTML =
getAiWidgetOptions().placeholderNormalElement ??
`Press '${cmd} + I' to use AI`
}
const btn = root.querySelector('span')
if (btn && btn.innerText === 'click here') {
btn.onclick = () => {
activePromptInput(view, '', false, 'placeholder')
}
}
const cursorEl = document.querySelector(
'.cm-cursorLayer .cm-cursor'
) as HTMLDivElement
if (cursorEl) {
cursorEl.classList.add('cm-ai-placeholder-cursor')
}
return root
}
destroy(_dom: HTMLElement): void {
const cursorEl = document.querySelector(
'.cm-cursorLayer .cm-cursor'
) as HTMLDivElement
if (cursorEl) {
cursorEl.classList.remove('cm-ai-placeholder-cursor')
}
}
}
// container for placeholder widget
const aiPlaceholderPlugin = ViewPlugin.fromClass(
class {
placeholder: DecorationSet = Decoration.none
updatePlaceholder() {
this.placeholder = Decoration.none
// if (isPromptInputActive(this.view)) return
// if (isUnifiedMergeViewActive(this.view)) return;
if (this.view.state.doc.length === 0) {
this.placeholder = Decoration.set([
Decoration.widget({
widget: new AIPlaceholderWidget(true),
side: 1
}).range(0)
])
} else {
// selection
const { from, to } = this.view.state.selection.main
const line = this.view.state.doc.lineAt(from)
// must be empty line
if (line.length === 0 && from === to) {
// a simple way to check last ';'
const beforeText = this.view.state
.sliceDoc(Math.max(0, from - 100), from)
.trim()
if (beforeText.endsWith(';')) {
this.placeholder = Decoration.set([
Decoration.widget({
widget: new AIPlaceholderWidget(false),
side: 1
}).range(from)
])
}
}
}
}
constructor(readonly view: EditorView) {
this.updatePlaceholder()
}
update(v: ViewUpdate) {
// hide placeholder when prompt input is active
if (isPromptInputActive(this.view.state)) {
this.placeholder = Decoration.none
return
}
if (v.selectionSet) {
this.updatePlaceholder()
}
}
get decorations() {
return this.placeholder
}
},
{ decorations: (v) => v.decorations }
)
export function aiPlaceholder(): Extension {
return [aiPlaceholderTheme, aiPlaceholderPlugin]
}