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:
“ydy0615”
2026-02-13 09:24:50 +08:00
parent 16e76e1e90
commit 65d4a57d33
12 changed files with 617 additions and 440 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
OPENAI_API_KEY=ollama
OLLAMA_HOST=http://192.168.0.120:11434
OLLAMA_MODEL=gpt-oss:120b
OLLAMA_MODEL=gpt-oss:20b
Binary file not shown.
+2 -2
View File
@@ -3,8 +3,8 @@ import json
import ollama
from typing import AsyncGenerator
OLLAMA_MODEL = os.getenv('OLLAMA_MODEL', 'gpt-oss:120b')
OLLAMA_HOST = os.getenv('OLLAMA_BASE_URL', 'http://192.168.0.120:11434')
OLLAMA_MODEL = os.getenv('OLLAMA_MODEL', 'gpt-oss:20b')
OLLAMA_HOST = os.getenv('OLLAMA_HOST', 'http://192.168.0.120:11434')
# 移除 /v1/ 后缀(如果有的话),因为 Ollama Python 包使用原生 API
if OLLAMA_HOST.endswith('/v1/'):
+3 -2
View File
@@ -71,7 +71,7 @@ async def create_completion(request: CompletionRequest):
print(f"[Backend] POST /v1/completions called")
print(f"[Backend] Received request - prefix length: {len(request.prefix)}, suffix length: {len(request.suffix)}")
OLLAMA_MODEL = os.getenv('OLLAMA_MODEL', 'gpt-oss:120b')
OLLAMA_MODEL = os.getenv('OLLAMA_MODEL', 'gpt-oss:20b')
OLLAMA_HOST = os.getenv('OLLAMA_HOST', 'http://192.168.0.120:11434')
print(f"[LLM] Using host: {OLLAMA_HOST}, model: {OLLAMA_MODEL}")
@@ -123,8 +123,9 @@ async def create_completion(request: CompletionRequest):
# 返回完整内容
async def generate():
if content:
print(f"[LLM] Yielding full content: {repr(content)}")
yield f"data: {json.dumps({'content': content})}\n\n"
yield f"data: {json.dumps({'done': true})}\n\n"
yield f"data: {json.dumps({'done': True})}\n\n"
return StreamingResponse(generate(), media_type="text/event-stream")
+7
View File
@@ -8,6 +8,12 @@ def build_prompt(prefix: str, suffix: str) -> str:
"""
MAX_CONTEXT_LINES = 30
# 修正:把suffix的第一个字符移到prefix末尾(解决光标位置偏差)
if suffix:
first_char = suffix[0]
prefix = prefix + first_char
suffix = suffix[1:]
prefix_lines = prefix.split('\n')
suffix_lines = suffix.split('\n') if suffix else []
@@ -25,6 +31,7 @@ RULES:
- Match existing style, tone, and terminology
- Maintain logical flow
- Write only the continuation, nothing else
- IMPORTANT: Start continuation directly from ⟨CURSOR⟩ position
TEXT:
{recent_prefix}⟨CURSOR⟩{recent_suffix}