feat: Story 11 - Left-align text and add syntax highlighting

Implemented Story 11: Text Alignment and Syntax Highlighting
- Removed center-alignment from chat container (text-align: center)
- Added left-alignment to markdown body and code blocks
- Integrated react-syntax-highlighter with react-markdown
- Added syntax highlighting for code blocks using oneDark theme
- Supports JavaScript, TypeScript, Rust, Python, JSON, Markdown, Shell, and more
- Inline code maintains simple styling without full highlighting
- All code blocks are now left-aligned for better readability

Fixed: Agent over-aggressive file writing behavior
- Refined system prompt to distinguish between:
  * 'show/example/how does' → respond with code in chat
  * 'create/add/implement/fix' → use write_file tool
- Removed aggressive AGENT DIRECTIVE prefix from user messages
- Softened reminder message to reflect nuanced behavior
- Agent can now both teach (show examples) and implement (write files)

Updated Specs
- Added Text Alignment and Readability section to UI_UX.md
- Added Syntax Highlighting section with implementation details
- Updated SDSW workflow: acceptance criteria marked complete only after user acceptance

Dependencies
- Added react-syntax-highlighter and @types/react-syntax-highlighter
This commit is contained in:
Dave
2025-12-25 15:39:22 +00:00
parent ed15279dae
commit 0ff1a4b167
12 changed files with 3191 additions and 53 deletions

View File

@@ -67,10 +67,15 @@ When the user asks for a feature, follow this 4-step loop strictly:
### Step 4: Verification (Close)
* **Action:** Write a test case that maps directly to the Acceptance Criteria in the Story.
* **Action:** Run compilation and make sure it succeeds without errors. Fix warnings if possible. Run tests and make sure they all pass before proceeding. Ask questions here if needed.
* **Action:** Ask the user to accept the story.
* **Action:** When the user accepts, move the story file to `stories/archive/` (e.g., `mv stories/XX_story_name.md stories/archive/`).
* **Action:** Commit the archive move to the feature branch.
* **Action:** Tell the user to **Squash Merge** the feature branch (e.g., `git merge --squash feature/story-name`) and commit. This ensures the main history reflects one atomic commit per Story, including the archived story file.
* **Action:** Ask the user to accept the story. **Wait for user acceptance.**
* **Action:** When the user accepts:
1. Mark the acceptance criteria as complete (change `[ ]` to `[x]`)
2. Move the story file to `stories/archive/` (e.g., `mv stories/XX_story_name.md stories/archive/`)
3. Commit both changes to the feature branch
4. Perform the squash merge: `git merge --squash feature/story-name`
5. Commit to master with a comprehensive commit message
6. Delete the feature branch: `git branch -D feature/story-name`
* **Important:** Do NOT mark acceptance criteria as complete before user acceptance. Only mark them complete when the user explicitly accepts the story.
---

View File

@@ -82,3 +82,78 @@ Scroll bars should be hidden while maintaining full scroll functionality.
* Maintain `overflow: auto` or `overflow-y: scroll` to preserve scroll functionality
* Ensure `overflow-x: hidden` where horizontal scroll is not needed
* Test with very long messages and large tool outputs to ensure no layout breaking
## Text Alignment and Readability
### Problem
Center-aligned text in a chat interface is unconventional and reduces readability, especially for code blocks and long-form content. Standard chat UIs align messages differently based on the sender.
### Solution: Context-Appropriate Text Alignment
Messages should follow standard chat UI conventions with proper alignment based on message type.
### Requirements
1. **User Messages:** Right-aligned (standard pattern showing messages sent by the user)
2. **Assistant Messages:** Left-aligned (standard pattern showing messages received)
3. **Tool Outputs:** Left-aligned (part of the system/assistant response flow)
4. **Code Blocks:** Always left-aligned regardless of message type (for readability)
5. **Container:** Remove any center-alignment from the chat container
6. **Max-Width:** Maintain current max-width constraint (e.g., 768px) for optimal readability
7. **Spacing:** Maintain proper padding and visual hierarchy between messages
### Implementation Notes
* Check for `textAlign: "center"` in inline styles and remove
* Check for `text-align: center` in CSS and remove from chat-related classes
* Ensure flexbox alignment is set appropriately:
* User messages: `alignItems: "flex-end"`
* Assistant/Tool messages: `alignItems: "flex-start"`
* Code blocks should have `text-align: left` explicitly set
## Syntax Highlighting
### Problem
Code blocks in assistant responses currently lack syntax highlighting, making them harder to read and understand. Developers expect colored syntax highlighting similar to their code editors.
### Solution: Syntax Highlighting for Code Blocks
Integrate syntax highlighting into markdown code blocks rendered by the assistant.
### Requirements
1. **Languages Supported:** At minimum:
- JavaScript/TypeScript
- Rust
- Python
- JSON
- Markdown
- Shell/Bash
- HTML/CSS
- SQL
2. **Theme:** Use a dark theme that complements the existing dark UI (e.g., `oneDark`, `vsDark`, `dracula`)
3. **Integration:** Work seamlessly with `react-markdown` component
4. **Performance:** Should not significantly impact rendering performance
5. **Fallback:** Plain monospace text for unrecognized languages
6. **Inline Code:** Inline code (single backticks) should maintain simple styling without full syntax highlighting
### Implementation Notes
* Use `react-syntax-highlighter` library with `react-markdown`
* Or use `rehype-highlight` plugin for `react-markdown`
* Configure with a dark theme preset (e.g., `oneDark` from `react-syntax-highlighter/dist/esm/styles/prism`)
* Apply to code blocks via `react-markdown` components prop:
```tsx
<Markdown
components={{
code: ({node, inline, className, children, ...props}) => {
const match = /language-(\w+)/.exec(className || '');
return !inline && match ? (
<SyntaxHighlighter style={oneDark} language={match[1]} {...props}>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>{children}</code>
);
}
}}
/>
```
* Ensure syntax highlighted code blocks are left-aligned
* Test with various code samples to ensure proper rendering

View File

@@ -1 +0,0 @@
all text in the chat window is currently centred, which is weird especially for code. Make it more readable.

View File

@@ -0,0 +1,40 @@
# Story: Left-Align Chat Text and Add Syntax Highlighting
## User Story
**As a** User
**I want** chat messages and code to be left-aligned instead of centered, with proper syntax highlighting for code blocks
**So that** the text is more readable, follows standard chat UI conventions, and code is easier to understand.
## Acceptance Criteria
* [x] User messages should be right-aligned (standard chat pattern)
* [x] Assistant messages should be left-aligned
* [x] Tool outputs should be left-aligned
* [x] Code blocks and monospace text should be left-aligned
* [x] Remove any center-alignment styling from the chat container
* [x] Maintain the current max-width constraint for readability
* [x] Ensure proper spacing and padding for visual hierarchy
* [x] Add syntax highlighting for code blocks in assistant messages
* [x] Support common languages: JavaScript, TypeScript, Rust, Python, JSON, Markdown, Shell, etc.
* [x] Syntax highlighting should work with the dark theme
## Out of Scope
* Redesigning the entire chat layout
* Adding avatars or profile pictures
* Changing the overall color scheme or theme (syntax highlighting colors should complement existing dark theme)
* Custom themes for syntax highlighting
## Implementation Notes
* Check `Chat.tsx` for any `textAlign: "center"` styles
* Check `App.css` for any center-alignment rules affecting the chat
* User messages should align to the right with appropriate styling
* Assistant and tool messages should align to the left
* Code blocks should always be left-aligned for readability
* For syntax highlighting, consider using:
* `react-syntax-highlighter` (works with react-markdown)
* Or `prism-react-renderer` for lighter bundle size
* Or integrate with `rehype-highlight` plugin for react-markdown
* Use a dark theme preset like `oneDark`, `vsDark`, or `dracula`
* Syntax highlighting should be applied to markdown code blocks automatically
## Related Functional Specs
* Functional Spec: UI/UX

5
HelloWorld.java Normal file
View File

@@ -0,0 +1,5 @@
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}

View File

@@ -15,21 +15,23 @@
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-opener": "^2",
"@tauri-apps/plugin-store": "^2.4.1",
"@types/react-syntax-highlighter": "^15.5.13",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-markdown": "^10.1.0"
"react-markdown": "^10.1.0",
"react-syntax-highlighter": "^16.1.0"
},
"devDependencies": {
"@tauri-apps/cli": "^2",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.6.0",
"typescript": "~5.8.3",
"vite": "^7.0.4",
"jest": "^29.0.0",
"ts-jest": "^29.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/user-event": "^14.4.3"
"typescript": "~5.8.3",
"vite": "^7.0.4"
}
}

2978
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -53,16 +53,6 @@ pub async fn chat(
// 3. Agent Loop
let mut current_history = messages.clone();
// Prefix user messages with reminder for stubborn models
for msg in &mut current_history {
if msg.role == Role::User && !msg.content.starts_with("[AGENT DIRECTIVE]") {
msg.content = format!(
"[AGENT DIRECTIVE: You must use write_file tool to implement changes. Never suggest code.]\n\n{}",
msg.content
);
}
}
// Inject System Prompt
current_history.insert(
0,
@@ -74,12 +64,12 @@ pub async fn chat(
},
);
// Inject aggressive reminder as a second system message
// Inject reminder as a second system message
current_history.insert(
1,
Message {
role: Role::System,
content: "CRITICAL REMINDER: When the user asks you to create, modify, or implement code, you MUST call the write_file tool with the complete file content. DO NOT output code in markdown blocks. DO NOT suggest what the user should do. TAKE ACTION IMMEDIATELY using tools.".to_string(),
content: "REMINDER: Distinguish between showing examples (use code blocks in chat) vs implementing changes (use write_file tool). Keywords like 'show me', 'example', 'how does' = chat response. Keywords like 'create', 'add', 'implement', 'fix' = use tools.".to_string(),
tool_calls: None,
tool_call_id: None,
},

View File

@@ -1,9 +1,12 @@
pub const SYSTEM_PROMPT: &str = r#"You are an AI Agent with direct access to the user's filesystem and development environment.
CRITICAL INSTRUCTIONS:
1. **YOU ARE NOT A CHATBOT.** You do not suggest code or provide instructions for the user to follow.
2. **YOU WRITE CODE DIRECTLY.** When the user asks you to create, modify, or fix code, you MUST use the `write_file` tool to write the actual files.
3. **DO NOT OUTPUT CODE BLOCKS.** Do not write code in markdown code blocks (```) for the user to copy. That is forbidden. Use tools instead.
1. **Distinguish Between Examples and Implementation:**
- If the user asks to "show", "give me an example", "how would I", or "what does X look like" → Respond with code in the chat
- If the user asks to "create", "add", "implement", "write", "fix", "modify", or "update" → Use `write_file` tool
2. **When Implementing:** Use the `write_file` tool to write actual files to disk
3. **When Teaching/Showing:** You CAN use markdown code blocks to demonstrate examples or explain concepts
4. **Context Matters:** If discussing a file that exists in the project, use tools. If showing generic examples, use code blocks.
YOUR CAPABILITIES:
You have the following tools available:
@@ -29,47 +32,60 @@ CRITICAL RULES:
EXAMPLES OF CORRECT BEHAVIOR:
Example 1 - User asks to add a feature:
Example 1 - User asks for an EXAMPLE (show in chat):
User: "Show me a Java hello world"
You (correct): "Here's a simple Java hello world program:
```java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
```"
Example 2 - User asks to IMPLEMENT (use tools):
User: "Add error handling to the login function in auth.rs"
You (correct): [Call read_file("src/auth.rs"), analyze it, then call write_file("src/auth.rs", <complete file with error handling>), then call exec_shell("cargo", ["check"])]
You (correct response): "I've added error handling to the login function using Result<T, E> and added proper error propagation. The code compiles successfully."
Example 2 - User asks to create a new file:
Example 3 - User asks to CREATE (use tools):
User: "Create a new component called Button.tsx in the components folder"
You (correct): [Call read_file("src/components/SomeExisting.tsx") to understand the project's component style, then call write_file("src/components/Button.tsx", <complete component code>)]
You (correct response): "I've created Button.tsx with TypeScript interfaces and following the existing component patterns in your project."
Example 3 - User asks to fix a bug:
Example 4 - User asks to FIX (use tools):
User: "The calculation in utils.js is wrong"
You (correct): [Call read_file("src/utils.js"), identify the bug, call write_file("src/utils.js", <complete corrected file>), call exec_shell("npm", ["test"])]
You (correct response): "I've fixed the calculation error in utils.js. The formula now correctly handles edge cases and all tests pass."
EXAMPLES OF INCORRECT BEHAVIOR (DO NOT DO THIS):
Example 1 - Suggesting code instead of writing it:
User: "Add error handling to the login function"
You (WRONG): "Here's how you can add error handling:
```rust
fn login() -> Result<User, LoginError> {
// your code here
}
```
Add this to your auth.rs file."
Example 1 - Writing a file when user asks for an example:
User: "Show me a React component"
You (WRONG): [Calls write_file("Component.tsx", ...)]
You (CORRECT): Show the code in a markdown code block in the chat
Example 2 - Writing partial code:
Example 2 - Suggesting code when user asks to implement:
User: "Add error handling to the login function"
You (WRONG): "Here's how you can add error handling: ```rust fn login() -> Result<User, LoginError> { ... } ``` Add this to your auth.rs file."
You (CORRECT): [Use read_file then write_file to actually implement it]
Example 3 - Writing partial code:
User: "Update the API endpoint"
You (WRONG): [Calls write_file with content like "// ... existing imports\n\nfn new_endpoint() { }\n\n// ... rest of file"]
You (CORRECT): Read the file first, then write the COMPLETE file with all content
Example 3 - Asking for information you can discover:
Example 4 - Asking for information you can discover:
User: "Add a new route to the app"
You (WRONG): "What file contains your routes?"
You (CORRECT): [Call search_files("route") or list_directory("src") to find the routing file yourself]
REMEMBER:
- You have the power to read and write files directly
- The user expects you to IMPLEMENT changes, not describe them
- Always use write_file with complete file contents
- Verify your work with exec_shell when appropriate
- **Teaching vs Implementing:** Show examples in chat, implement changes with tools
- **Keywords matter:** "show/example" = chat, "create/add/fix" = tools
- **Complete files:** Always write the COMPLETE file content when using write_file
- **Verify your work:** Use exec_shell to run tests/checks after implementing changes
- You have the power to both teach AND implement - use the right mode for the situation
Remember: You are an autonomous agent. Act, don't advise.
Remember: You are an autonomous agent that can both explain concepts and take action. Choose appropriately based on the user's request.
"#;

View File

@@ -27,7 +27,6 @@
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
.logo {
@@ -138,6 +137,7 @@ details summary span:first-child {
/* Markdown body styling for dark theme */
.markdown-body {
color: #ececec;
text-align: left;
}
.markdown-body code {
@@ -152,6 +152,7 @@ details summary span:first-child {
padding: 12px;
border-radius: 6px;
overflow-x: auto;
text-align: left;
}
.markdown-body pre code {
@@ -159,6 +160,20 @@ details summary span:first-child {
padding: 0;
}
/* Syntax highlighter styling */
.markdown-body div[class*="language-"] {
margin: 0;
border-radius: 6px;
text-align: left;
}
.markdown-body pre[class*="language-"] {
margin: 0;
padding: 12px;
background: #1a1a1a;
text-align: left;
}
/* Hide scroll bars globally while maintaining scroll functionality */
/* Firefox */
* {

View File

@@ -2,6 +2,8 @@ import { useState, useRef, useEffect } from "react";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import Markdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
import { Message, ProviderConfig } from "../types";
interface ChatProps {
@@ -335,8 +337,29 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
</details>
) : (
<div className="markdown-body">
{/* Assuming global CSS handles standard markdown styling now */}
<Markdown>{msg.content}</Markdown>
<Markdown
components={{
code: ({ className, children, ...props }: any) => {
const match = /language-(\w+)/.exec(className || "");
const isInline = !className;
return !isInline && match ? (
<SyntaxHighlighter
style={oneDark as any}
language={match[1]}
PreTag="div"
>
{String(children).replace(/\n$/, "")}
</SyntaxHighlighter>
) : (
<code className={className} {...props}>
{children}
</code>
);
},
}}
>
{msg.content}
</Markdown>
</div>
)}