diff --git a/backend/prompt.py b/backend/prompt.py
index ac56694..b49c3de 100644
--- a/backend/prompt.py
+++ b/backend/prompt.py
@@ -331,124 +331,111 @@ def build_inline_system_prompt(language_id: str = "markdown") -> str:
Return only the insertion text that should be placed between PREFIX and SUFFIX.
-Hard constraints you must follow:
-1) Output-only contract:
-- Output insertion text only.
-- No explanations, no meta labels, no wrapper quotes around the whole answer.
+CORE PRINCIPLE: Output insertion text only. No explanations, no meta labels, no wrapper quotes.
-2) Strict math formatting (KaTeX):
-- If you output any math expression, it must be strict KaTeX-compatible math.
-- Every formula must be wrapped with either $...$ (inline) or $$...$$ (block).
-- Never output bare formulas without $ or $$ wrappers.
-- Exception: If CURSOR_IN_FENCED_CODE_BLOCK=true and CURSOR_FENCE_LANGUAGE is latex/tex/katex,
- output raw LaTeX without $ or $$ wrappers.
+PRIORITY 1: CONTEXT AWARENESS (Read these flags from user prompt)
+- CURSOR_IN_FENCED_CODE_BLOCK: Are you inside a code fence?
+- CURSOR_FENCE_LANGUAGE: What language is the current fence?
+- PREFIX_ENDS_WITH_NEWLINE: Does prefix end with newline?
+- SUFFIX_STARTS_WITH_NEWLINE: Does suffix start with newline?
+- MERMAID_CONTEXT: Is this a Mermaid diagram context?
-3) Strict code formatting:
-- Read CURSOR_IN_FENCED_CODE_BLOCK from the user prompt.
-- If CURSOR_IN_FENCED_CODE_BLOCK=true:
- - You are already inside a fenced code block.
- - Never output triple backticks.
- - Output code lines only.
-- If CURSOR_IN_FENCED_CODE_BLOCK=false:
- - Any code output must be in a fenced code block with a language tag:
+PRIORITY 2: SPECIALIZED CONTENT RULES
+
+2.1 Code Block Handling:
+If CURSOR_IN_FENCED_CODE_BLOCK=true:
+ - You are inside a code fence
+ - Output code lines ONLY (no triple backticks)
+ - Use single \\n for code line separation
+
+If CURSOR_IN_FENCED_CODE_BLOCK=false and code needed:
+ - Wrap code in fenced block with language tag:
```{{language}}
- ...
+ code here
```
- - Do not output code snippets as inline backticks.
- - Choose the language tag from context (no default fallback tag instruction).
+ - Never use inline backticks for code snippets
-4) Mermaid-specific completion rules:
-- Read CURSOR_FENCE_LANGUAGE and MERMAID_CONTEXT from the user prompt.
-- If CURSOR_FENCE_LANGUAGE=mermaid:
- - Output Mermaid statements only.
- - Never output triple backticks.
- - Never output prose explanations.
-- If CURSOR_IN_FENCED_CODE_BLOCK=false and MERMAID_CONTEXT=true:
- - Output a complete Mermaid fenced block:
+2.2 Math Formatting (KaTeX):
+- Inline math: wrap with $...$
+- Block math: wrap with $$...$$
+- Never output bare formulas
+- Exception: inside latex/tex/katex fence, output raw LaTeX
+
+2.3 Mermaid Diagrams:
+If CURSOR_FENCE_LANGUAGE=mermaid:
+ - Output Mermaid syntax ONLY
+ - No backticks, no explanations
+
+If MERMAID_CONTEXT=true and outside fence:
+ - Output complete fenced block:
```mermaid
- ...
+ diagram syntax
```
- - Keep Mermaid syntax valid and concise.
-- Never mix Mermaid code and explanatory narration in one output.
-5) Boundary newline repair:
-- Read PREFIX_ENDS_WITH_NEWLINE and SUFFIX_STARTS_WITH_NEWLINE from the user prompt.
-- Carefully reason about whether OUTPUT should start or end with a newline.
-- If PREFIX lacks a required boundary newline, add it at OUTPUT start.
-- If SUFFIX lacks a required boundary newline, add it at OUTPUT end.
-- Ensure PREFIX + OUTPUT + SUFFIX is structurally natural.
+PRIORITY 3: MARKDOWN STRUCTURE
-6) Context stitching:
-- Do not repeat text that already appears at the start of SUFFIX.
-- Preserve nearby language, tone, punctuation, indentation, and markdown structure.
-- Continue existing structures naturally (lists, tables, block quotes, headings).
+3.1 Newline Semantics:
+- Single \\n: soft break (same paragraph, renders as space or )
+- Double \\n\\n: hard break (new paragraph/block)
+- Use \\n\\n for: new paragraphs, before headings, starting lists/tables
+- Use \\n for: continuation within blocks (list items, table cells)
+- Exception: inside code blocks, use \\n freely for code lines
-7) OCR safety:
-- PREFIX may include hidden OCR metadata tags like .
-- Never output any OCR tag.
-- Never output OCR tag fragments such as ."""
+3.2 Boundary Management:
+Check PREFIX_ENDS_WITH_NEWLINE and SUFFIX_STARTS_WITH_NEWLINE:
+- If PREFIX lacks needed newline: start OUTPUT with \\n
+- If SUFFIX lacks needed newline: end OUTPUT with \\n
+- Common cases requiring leading \\n:
+ * Starting a list after "Steps:"
+ * Creating new paragraph after text
+ * Adding heading after paragraph
+- Common cases requiring trailing \\n:
+ * Before new heading
+ * End of section
+
+3.3 Context Stitching:
+- Never repeat text from SUFFIX beginning
+- Match PREFIX tone, style, indentation
+- Continue structures: lists, tables, quotes, headings
+
+PRIORITY 4: HIDDEN CONTEXT
+- OCR metadata like is hidden context
+- Never copy OCR tags to output
+- Use OCR content as semantic hint only
+"""
if language_guidance:
- system_prompt = f"{system_prompt.rstrip()}\n{language_guidance.strip()}"
+ system_prompt = f"{system_prompt.rstrip()}\\n{language_guidance.strip()}"
return system_prompt.strip()
-INLINE_EXAMPLES = """[EX01] Prose continuation
+INLINE_EXAMPLES = """=== CATEGORY A: PROSE CONTINUATION ===
+
+[EX01] Simple prose continuation
The quick brown fox jumps over the lazy dog.
Expected OUTPUT:
-moved quietly and then
+moved quietly and then
-[EX02] Avoid repeating suffix beginning
+[EX02] Avoid repeating suffix
Our launch plan starts with phase one, followed by phase two.
Expected OUTPUT:
-careful internal testing before
+careful internal testing before
+WRONG: phase one starts with (repeats suffix)
-[EX03] Continue markdown checklist
+=== CATEGORY B: MARKDOWN STRUCTURES ===
+
+[EX03] Continue checklist
## TODO
- [ ] Buy milk
- [ ]
Expected OUTPUT:
-Write release notes and share draft with team
+Write release notes and share draft with team
-[EX04] Cursor outside code block, code must use fenced block
-CURSOR_IN_FENCED_CODE_BLOCK=false
-Parse this JSON payload in Python:
-
-Expected OUTPUT:
-```python
-import json
-data = json.loads(payload)
-```
-
-[EX05] Cursor inside fenced code block, do not output fences
-CURSOR_IN_FENCED_CODE_BLOCK=true
-```python
-def add(a, b):
- return
-
-```
-Expected OUTPUT:
-a + b
-
-[EX06] Inline math must use $...$
-The derivative of x^2 is
-.
-Expected OUTPUT:
-$2x$
-
-[EX07] Block math must use $$...$$
-We can write the Gaussian integral as:
-
-Expected OUTPUT:
-$$
-\\int_{-\\infty}^{\\infty} e^{-x^2}\\,dx = \\sqrt{\\pi}
-$$
-
-[EX08] Prefix misses boundary newline; add newline at output start
+[EX04] Start list after header (PREFIX lacks newline)
PREFIX_ENDS_WITH_NEWLINE=false
Deployment steps:
@@ -457,21 +444,7 @@ Expected OUTPUT:
- Build artifact
- Deploy service
-[EX09] Suffix misses boundary newline; add newline at output end
-SUFFIX_STARTS_WITH_NEWLINE=false
-Summary paragraph complete.
-## Next Section
-Expected OUTPUT:
-
-
-[EX10] OCR metadata exists but must never be emitted
-
-The relationship is
-.
-Expected OUTPUT:
-$y = mx + b$
-
-[EX11] Continue markdown table with correct row shape
+[EX05] Continue table row
| Name | Score |
| --- | --- |
| Alice | 92 |
@@ -480,30 +453,91 @@ $y = mx + b$
Expected OUTPUT:
88 |
-[EX12] Mixed text + math + code in one insertion
-CURSOR_IN_FENCED_CODE_BLOCK=false
-Use the area formula and provide a tiny JS helper.
+[EX06] Start new paragraph
+First paragraph ends.
Expected OUTPUT:
-The area is $A = \\pi r^2$.
-```javascript
-const area = (r) => Math.PI * r * r;
+Second paragraph starts.
+WRONG: Second paragraph starts. (missing leading \\n\\n)
+
+[EX07] Add newline before heading
+PREFIX_ENDS_WITH_NEWLINE=false
+End of previous section.
+## Next Heading
+Expected OUTPUT:
+
+WRONG: (would join with heading without separation)
+
+=== CATEGORY C: CODE BLOCKS ===
+
+[EX08] Outside fence: wrap code in fence
+CURSOR_IN_FENCED_CODE_BLOCK=false
+Parse this JSON payload in Python:
+
+Expected OUTPUT:
+```python
+import json
+data = json.loads(payload)
```
+WRONG: import json\\ndata = json.loads(payload) (no fence)
-[EX13] Cursor inside mermaid fence: no backticks, mermaid lines only
+[EX09] Inside fence: output code only
CURSOR_IN_FENCED_CODE_BLOCK=true
+```python
+def add(a, b):
+ return
+
+```
+Expected OUTPUT:
+a + b
+WRONG: ```python\\nreturn a + b\\n``` (duplicate fences)
+
+[EX10] Code inside fence uses single newline
+CURSOR_IN_FENCED_CODE_BLOCK=true
+```python
+def hello():
+
+```
+Expected OUTPUT:
+ print("Hello")
+ return True
+(Note: single \\n between code lines, no markdown rules)
+
+=== CATEGORY D: MATH ===
+
+[EX11] Inline math
+The derivative of x^2 is
+.
+Expected OUTPUT:
+$2x$
+WRONG: 2x (bare formula)
+
+[EX12] Block math
+We can write the Gaussian integral as:
+
+Expected OUTPUT:
+$$
+\\int_{-\\infty}^{\\infty} e^{-x^2}\\,dx = \\sqrt{\\pi}
+$$
+WRONG: \\int... (bare formula without $$)
+
+=== CATEGORY E: MERMAID ===
+
+[EX13] Inside mermaid fence
CURSOR_FENCE_LANGUAGE=mermaid
+CURSOR_IN_FENCED_CODE_BLOCK=true
```mermaid
flowchart TD
-A[Start] -->
+ A[Start] -->
```
Expected OUTPUT:
B{Valid?}
B -->|Yes| C[Done]
+WRONG: ```mermaid\\nB{Valid?}... (duplicate fence)
-[EX14] Mermaid context outside fence: return full mermaid block
+[EX14] Outside fence with mermaid context
CURSOR_IN_FENCED_CODE_BLOCK=false
MERMAID_CONTEXT=true
Please provide a simple release pipeline diagram.
@@ -511,8 +545,18 @@ MERMAID_CONTEXT=true
Expected OUTPUT:
```mermaid
flowchart LR
-Build --> Test --> Deploy
-```"""
+ Build --> Test --> Deploy
+```
+
+=== CATEGORY F: OCR METADATA ===
+
+[EX15] Use OCR as context, never output
+
+The relationship is
+.
+Expected OUTPUT:
+$y = mx + b$
+WRONG: (OCR tag in output)"""
def build_completion_prompts(
@@ -552,35 +596,49 @@ def build_completion_prompts(
preferences_instruction = f"\nUser Preferences:\n{preferences_instruction}"
user_prompt = f"""Current time: {current_time}{location_info}{preferences_instruction}
-Reasoning hint: {thinking_level}
-Editor language id: {safe_language_id}
+Reasoning level: {thinking_level}
+Editor language: {safe_language_id}
-Completion state flags:
+=== STATE FLAGS ===
- CURSOR_IN_FENCED_CODE_BLOCK: {"true" if cursor_in_fenced_code_block else "false"}
- CURSOR_FENCE_LANGUAGE: {cursor_fence_language}
- MERMAID_CONTEXT: {"true" if mermaid_context else "false"}
- PREFIX_ENDS_WITH_NEWLINE: {"true" if prefix_ends_with_newline else "false"}
- SUFFIX_STARTS_WITH_NEWLINE: {"true" if suffix_starts_with_newline else "false"}
-Task:
-- Produce the best insertion text at the cursor between PREFIX and SUFFIX.
-- Keep insertion meaningful and non-empty.
-- Keep insertion concise unless structure requires more content.
+=== TASK ===
+Produce the best insertion text between PREFIX and SUFFIX.
+Requirements:
+- Non-empty and meaningful
+- Concise unless structure needs more
+- Follows markdown rules in system prompt
-Context notes:
-- PREFIX may include OCR metadata after image markdown, e.g.  .
-- OCR metadata is hidden context and must never be copied into output.
-- Preserve local style and formatting.
+=== BOUNDARY DECISION GUIDE ===
-Decision policy:
-- Prioritize seamless join: PREFIX + OUTPUT + SUFFIX must read naturally.
-- Do not repeat SUFFIX-leading text.
-- If uncertain, prefer a complete short phrase/sentence with clear meaning.
+Step 1: Check PREFIX_ENDS_WITH_NEWLINE
+If false, ask: "Does output need to start on a new line?"
+ - YES if PREFIX ends with: ":", "steps:", "items:", heading text, or complete sentence before heading
+ - If YES: start output with \\n
-Comprehensive examples:
+Step 2: Check SUFFIX_STARTS_WITH_NEWLINE
+If false, ask: "Does output need to end with a newline?"
+ - YES if SUFFIX starts with: heading (##), new paragraph, or list marker
+ - If YES: end output with \\n
+
+Step 3: Choose newline type
+ - Use \\n\\n for: new paragraphs, before headings, starting lists
+ - Use \\n for: continuing within blocks, list items, table cells
+ - Exception: inside code fences, use \\n freely
+
+=== CONTEXT NOTES ===
+- OCR metadata (e.g., ) is hidden context, never copy to output
+- Match PREFIX tone, style, and indentation
+- Do not repeat text from SUFFIX beginning
+
+=== EXAMPLES BY CATEGORY ===
{INLINE_EXAMPLES}
-Now produce the insertion.
+=== NOW COMPLETE THE TASK ===
{recent_prefix}
diff --git a/src/components/MilkdownEditor.vue b/src/components/MilkdownEditor.vue
index 2e01c27..a38ad2e 100644
--- a/src/components/MilkdownEditor.vue
+++ b/src/components/MilkdownEditor.vue
@@ -46,7 +46,7 @@
{{ t('uploadFile') }}
-
+
-