refactor(editor): migrate to ProseMirror Mark-based ghost text system
- Replace overlay-based GhostTextOverlay.vue with ProseMirror Mark system - Add AI toggle button with enable/disable functionality - Implement new copilotPlugin.ts using copilotGhostMark for inline suggestions - Fix cursor position offset in prompt.py by moving first suffix char to prefix - Improve API error handling with abort signal support and debug logging - Update model configuration from gpt-oss:120b to gpt-oss:20b - Add button tooltips and improve editor styling - Remove deprecated inlineSuggestionPlugin.ts - Update README with new architecture diagram and feature documentation
This commit is contained in:
@@ -1,30 +1,62 @@
|
||||
export async function fetchSuggestion(prefix, suffix, apiUrl = 'http://localhost:8000/v1/completions') {
|
||||
const res = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ prefix, suffix, languageId: 'markdown' }),
|
||||
})
|
||||
import { DEBUG, API_URL } from './config.js'
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`HTTP ${res.status}`)
|
||||
}
|
||||
export async function fetchSuggestion(prefix, suffix, signal, apiUrl = API_URL) {
|
||||
if (DEBUG) console.log('[Debug] fetchSuggestion called with prefix length:', prefix.length, 'suffix length:', suffix.length)
|
||||
try {
|
||||
const res = await fetch(apiUrl, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ prefix, suffix, languageId: 'markdown' }),
|
||||
signal
|
||||
})
|
||||
|
||||
const reader = res.body?.getReader()
|
||||
if (!reader) throw new Error('No reader available')
|
||||
if (DEBUG) console.log('[Debug] fetchSuggestion response status:', res.status)
|
||||
if (!res.ok) {
|
||||
const errorText = await res.text()
|
||||
throw new Error(`HTTP ${res.status}: ${errorText}`)
|
||||
}
|
||||
|
||||
let text = ''
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
const chunk = new TextDecoder().decode(value)
|
||||
const lines = chunk.split('\n').filter(l => l.startsWith('data: '))
|
||||
for (const line of lines) {
|
||||
try {
|
||||
const data = JSON.parse(line.slice(6))
|
||||
if (data.content) text += data.content
|
||||
if (data.done || data.error) break
|
||||
} catch (e) {}
|
||||
const reader = res.body?.getReader()
|
||||
if (!reader) {
|
||||
if (DEBUG) console.log('[Debug] No reader available')
|
||||
throw new Error('No reader available')
|
||||
}
|
||||
|
||||
let text = ''
|
||||
let buffer = ''
|
||||
while (true) {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
buffer += new TextDecoder().decode(value)
|
||||
|
||||
const lines = buffer.split('\n')
|
||||
buffer = lines.pop() || ''
|
||||
|
||||
for (const line of lines) {
|
||||
if (!line.startsWith('data: ')) continue
|
||||
const jsonStr = line.slice(6).trim()
|
||||
if (!jsonStr) continue
|
||||
try {
|
||||
const data = JSON.parse(jsonStr)
|
||||
if (data.content) {
|
||||
text += data.content
|
||||
if (DEBUG) console.log('[Debug] Added content:', data.content)
|
||||
}
|
||||
if (data.done || data.error) break
|
||||
} catch (e) {
|
||||
if (DEBUG) console.warn('[Debug] JSON parse error for:', jsonStr.substring(0, 50))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (DEBUG) console.log('[Debug] Final suggestion text:', text.substring(0, 100))
|
||||
return text
|
||||
} catch (e) {
|
||||
if (e.name === 'AbortError') {
|
||||
if (DEBUG) console.log('[Debug] Request aborted')
|
||||
} else {
|
||||
if (DEBUG) console.error('[Debug] fetchSuggestion error:', e)
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user