diff --git a/claude_cli.go b/claude_cli.go
index 6a071bd..a45e517 100644
--- a/claude_cli.go
+++ b/claude_cli.go
@@ -108,7 +108,6 @@ func SpawnCLI(projectDir string) (*CLIProcess, error) {
"--input-format", "stream-json",
"--output-format", "stream-json",
"--verbose",
- "--include-partial-messages",
}
cmd := exec.Command("claude", args...)
diff --git a/fragments.go b/fragments.go
index c59f99d..7c8f5a6 100644
--- a/fragments.go
+++ b/fragments.go
@@ -8,9 +8,13 @@ import (
)
// FragmentUserMessage returns an HTML fragment for a user message.
-func FragmentUserMessage(text string) string {
+func FragmentUserMessage(text string, isPlan bool) string {
escaped := html.EscapeString(text)
- return fmt.Sprintf(`
`, escaped)
+ modeClass := "message-user"
+ if isPlan {
+ modeClass = "message-user message-user-plan"
+ }
+ return fmt.Sprintf(``, modeClass, escaped)
}
// FragmentAssistantStart returns the opening tag for an assistant message with streaming.
diff --git a/fragments_test.go b/fragments_test.go
index f6dfa41..e3b3bcd 100644
--- a/fragments_test.go
+++ b/fragments_test.go
@@ -6,7 +6,7 @@ import (
)
func TestFragmentUserMessage(t *testing.T) {
- f := FragmentUserMessage("Hello ")
+ f := FragmentUserMessage("Hello ", false)
if !strings.Contains(f, "message-user") {
t.Error("missing message-user class")
}
@@ -16,6 +16,15 @@ func TestFragmentUserMessage(t *testing.T) {
if !strings.Contains(f, `hx-swap-oob="beforeend"`) {
t.Error("missing OOB swap")
}
+ if strings.Contains(f, "message-user-plan") {
+ t.Error("should not have plan class in code mode")
+ }
+
+ // Plan mode
+ fp := FragmentUserMessage("analyze this", true)
+ if !strings.Contains(fp, "message-user-plan") {
+ t.Error("missing plan class in plan mode")
+ }
}
func TestFragmentAssistantStart(t *testing.T) {
diff --git a/static/style.css b/static/style.css
index 5c0b6b2..8428146 100644
--- a/static/style.css
+++ b/static/style.css
@@ -107,6 +107,15 @@ a:hover {
background: var(--accent-hover);
}
+.btn-secondary {
+ background: var(--bg-tertiary);
+ margin-right: 0.5rem;
+}
+
+.btn-secondary:hover {
+ background: var(--border);
+}
+
.btn-full {
width: 100%;
}
@@ -121,6 +130,22 @@ a:hover {
text-align: center;
}
+.success-msg {
+ background: rgba(76, 175, 80, 0.15);
+ color: var(--success);
+ padding: 0.6rem 1rem;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+ font-size: 0.9rem;
+ text-align: center;
+}
+
+.form-footer {
+ margin-top: 1rem;
+ text-align: center;
+ font-size: 0.85rem;
+}
+
/* Projects grid */
.projects-container {
max-width: 1000px;
@@ -352,6 +377,59 @@ a:hover {
80%, 100% { opacity: 1; }
}
+/* Mode bar */
+.mode-bar {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.3rem 0;
+ margin-bottom: 0.3rem;
+}
+
+.mode-indicator {
+ display: flex;
+ gap: 0;
+ border: 1px solid var(--border);
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.mode-label {
+ padding: 0.2rem 0.6rem;
+ font-size: 0.75rem;
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
+ color: var(--text-muted);
+ cursor: pointer;
+ transition: all 0.15s;
+ user-select: none;
+}
+
+.mode-label:hover {
+ color: var(--text-secondary);
+ background: rgba(255, 255, 255, 0.05);
+}
+
+.mode-label.mode-active {
+ background: var(--accent);
+ color: #fff;
+}
+
+.mode-hint {
+ font-size: 0.65rem;
+ color: var(--text-muted);
+ font-family: 'JetBrains Mono', 'Fira Code', monospace;
+}
+
+/* Plan mode user messages */
+.message-user-plan {
+ color: #64b5f6 !important;
+}
+
+.message-user-plan::before {
+ content: "⊞ " !important;
+ color: #64b5f6 !important;
+}
+
/* Input area — terminal prompt */
.chat-input-area {
padding: 0.5rem 1rem;
diff --git a/templates/chat.html b/templates/chat.html
index caf8d48..156be10 100644
--- a/templates/chat.html
+++ b/templates/chat.html
@@ -42,7 +42,15 @@