feat(design): surface phase PR 3 — Chat surface polish

Per the surface-phase spec for the Chat cluster (ChatView, ChatPanel,
ChatMessage, ChatInputBar, ToolCallCard):

Long-form line-height
- ChatMessage: assistant-bubble .message-content jumps from 1.55 to
  1.7 — chat is a reading surface, not a snippet stream. User
  bubbles stay tighter.
- JournalView: dropped its :deep(.role-assistant .message-content)
  override; ChatMessage handles it now and Journal inherits.

ToolCallCard borders
- Removed the outer 1px border. ToolCallCard always renders inside
  an assistant bubble; the bubble already contains it. Background
  tint differentiates without re-bordering. Per the
  structural-not-decorative rule.
- Error state preserved as a 3px left-edge accent in --color-danger,
  mirroring the assistant bubble's own left-edge pattern.

Button reclassification per Hybrid rule
- ChatView .bulk-link "All"/"None": accent text → --color-text-secondary
  (these are tertiary list-control affordances, not brand moments)
- ChatView .bulk-delete-btn: --color-danger (Error terracotta) →
  --color-action-destructive (Oxblood) per Hybrid; paired with a
  Trash2 icon since destructive should always be reinforced by an
  icon, not just color
- ChatView .btn-delete-conv hover: same Error → Oxblood swap
- .btn-new-conv stays accent (brand moment, correct already)
- .btn-send stays accent gradient (primary brand moment, foundation)

Two-weights-only
- Snapped every font-weight: 600/700 to 500 across ChatView,
  ChatPanel, ChatMessage, ToolCallCard per the doc rule.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 22:42:44 -04:00
parent f20e54a068
commit 4192a64c0f
5 changed files with 44 additions and 33 deletions
+8 -2
View File
@@ -155,7 +155,7 @@ const timingParts = computed((): string[] => {
}
.role-label {
font-size: 0.75rem;
font-weight: 600;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.03em;
}
@@ -182,7 +182,7 @@ const timingParts = computed((): string[] => {
.thinking-summary {
padding: 0.25rem 0.5rem;
font-size: 0.72rem;
font-weight: 600;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--color-text-muted);
@@ -215,6 +215,12 @@ details[open] .thinking-summary::before {
overflow-y: auto;
background: var(--color-bg-secondary);
}
/* Long-form line-height (1.7) on assistant bubbles per the design system —
chat is a reading surface, not a snippet stream. User bubbles stay tighter. */
.role-assistant .message-content {
line-height: 1.7;
}
.message-content {
font-size: 0.95rem;
line-height: 1.55;
+7 -7
View File
@@ -580,7 +580,7 @@ defineExpose({ focus, prefill, send, contextCount, sidebarOpen, toggleContextSid
padding: 0.15rem 0.1rem;
font-family: inherit;
font-size: 0.65rem;
font-weight: 700;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-text-muted);
@@ -598,7 +598,7 @@ defineExpose({ focus, prefill, send, contextCount, sidebarOpen, toggleContextSid
.context-sidebar-header .section-count {
margin-left: auto;
color: var(--color-text-muted);
font-weight: 600;
font-weight: 500;
background: var(--color-bg);
padding: 0.05rem 0.3rem;
border-radius: 8px;
@@ -622,7 +622,7 @@ defineExpose({ focus, prefill, send, contextCount, sidebarOpen, toggleContextSid
}
.auto-pill {
font-size: 0.55rem;
font-weight: 700;
font-weight: 500;
letter-spacing: 0.05em;
padding: 0.05rem 0.3rem;
border-radius: 4px;
@@ -686,7 +686,7 @@ defineExpose({ focus, prefill, send, contextCount, sidebarOpen, toggleContextSid
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--color-text-muted);
font-weight: 700;
font-weight: 500;
margin-bottom: 0.2rem;
}
.queued-clear-row {
@@ -734,7 +734,7 @@ defineExpose({ focus, prefill, send, contextCount, sidebarOpen, toggleContextSid
.empty-section-label {
font-size: 0.7rem;
font-weight: 600;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-text-muted);
@@ -871,7 +871,7 @@ defineExpose({ focus, prefill, send, contextCount, sidebarOpen, toggleContextSid
.btn-open-chat.prominent {
font-size: 0.9rem;
color: var(--color-primary);
font-weight: 600;
font-weight: 500;
}
.btn-clear-response {
background: none;
@@ -918,7 +918,7 @@ defineExpose({ focus, prefill, send, contextCount, sidebarOpen, toggleContextSid
:deep(.thinking-summary) {
padding: 0.25rem 0.5rem;
font-size: 0.72rem;
font-weight: 600;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--color-text-muted);
+10 -7
View File
@@ -532,10 +532,13 @@ function closeEventSlideOver(changed = false) {
</template>
<style scoped>
/* Inline ToolCallCard renders inside an assistant bubble — the bubble
already contains it, so no outer border per the structural-not-decorative
rule. Background tint differentiates it; error state gets a left-edge
accent the way the assistant bubble itself uses one. */
.tool-call-card {
display: inline-block;
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: 10px;
font-size: 0.8rem;
margin-top: 0.4rem;
@@ -543,7 +546,7 @@ function closeEventSlideOver(changed = false) {
vertical-align: top;
}
.tool-call-card.error {
border-color: var(--color-danger, #e74c3c);
border-left: 3px solid var(--color-danger);
}
.tool-call-card.declined {
opacity: 0.55;
@@ -566,7 +569,7 @@ function closeEventSlideOver(changed = false) {
}
.tool-label {
font-weight: 600;
font-weight: 500;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.03em;
@@ -636,7 +639,7 @@ function closeEventSlideOver(changed = false) {
}
.web-search-results .tool-search-item::after { content: none; }
.tool-event-title { font-weight: 600; color: var(--color-text); }
.tool-event-title { font-weight: 500; color: var(--color-text); }
.tool-event-time { color: var(--color-text-muted); font-size: 0.75rem; }
.tool-event-list { display: flex; flex-direction: column; gap: 0.2rem; }
@@ -652,7 +655,7 @@ function closeEventSlideOver(changed = false) {
.tool-note-tag { font-size: 0.72rem; color: var(--color-text-muted); }
.tool-task-priority {
font-size: 0.7rem; font-weight: 600; text-transform: uppercase;
font-size: 0.7rem; font-weight: 500; text-transform: uppercase;
letter-spacing: 0.04em; padding: 0.1rem 0.3rem; border-radius: 3px;
}
.priority-high { color: var(--color-danger, #e74c3c); }
@@ -697,7 +700,7 @@ function closeEventSlideOver(changed = false) {
.confirm-created {
color: var(--color-success, #2ecc71);
font-size: 0.78rem;
font-weight: 600;
font-weight: 500;
}
.confirm-denied {
color: var(--color-text-muted);
@@ -792,7 +795,7 @@ function closeEventSlideOver(changed = false) {
}
.tool-event-card-title {
font-size: 0.8rem;
font-weight: 600;
font-weight: 500;
color: var(--color-text);
white-space: nowrap;
overflow: hidden;
+19 -11
View File
@@ -4,7 +4,7 @@ import { useRoute, useRouter } from "vue-router";
import { useChatStore } from "@/stores/chat";
import { apiGet } from "@/api/client";
import ChatPanel from "@/components/ChatPanel.vue";
import { MoreVertical, Menu, Paperclip } from "lucide-vue-next";
import { MoreVertical, Menu, Paperclip, Trash2 } from "lucide-vue-next";
const route = useRoute();
const router = useRouter();
@@ -338,6 +338,7 @@ onUnmounted(() => {
:disabled="bulkDeleting"
@click="bulkDelete"
>
<Trash2 :size="16" />
{{ bulkDeleting ? '...' : bulkConfirm ? `Delete ${selectedIds.size}?` : `Delete (${selectedIds.size})` }}
</button>
</div>
@@ -545,22 +546,29 @@ onUnmounted(() => {
.bulk-count { font-size: 0.75rem; color: var(--color-text-muted); flex-shrink: 0; }
.bulk-link {
background: none; border: none; color: var(--color-primary);
background: none; border: none; color: var(--color-text-secondary);
font-size: 0.75rem; cursor: pointer; padding: 0; text-decoration: underline;
}
.bulk-link:hover { color: var(--color-text); }
/* Destructive action — Oxblood per Hybrid rule, paired with Trash2 icon */
.bulk-delete-btn {
display: inline-flex;
align-items: center;
gap: 0.3rem;
margin-left: auto;
background: none;
border: 1px solid var(--color-danger, #e74c3c);
color: var(--color-danger, #e74c3c);
border-radius: 4px;
border: 1px solid var(--color-action-destructive);
color: var(--color-action-destructive);
border-radius: var(--radius-sm);
font-size: 0.75rem;
font-weight: 600;
font-weight: 500;
padding: 0.2rem 0.55rem;
cursor: pointer;
}
.bulk-delete-btn:hover:not(:disabled) { background: var(--color-action-destructive); color: #fff; }
.bulk-delete-btn:disabled { opacity: 0.5; cursor: default; }
.bulk-delete-btn.confirm { background: var(--color-danger, #e74c3c); color: #fff; }
.bulk-delete-btn.confirm { background: var(--color-action-destructive-hover); color: #fff; border-color: var(--color-action-destructive-hover); }
.conv-checkbox {
flex-shrink: 0;
@@ -580,7 +588,7 @@ onUnmounted(() => {
}
.conv-group-label {
font-size: 0.68rem;
font-weight: 700;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-muted);
@@ -614,7 +622,7 @@ onUnmounted(() => {
background: none; border: none; color: var(--color-text-muted);
cursor: pointer; font-size: 1.2rem; padding: 0 0.25rem; line-height: 1;
}
.btn-delete-conv:hover { color: var(--color-danger, #e74c3c); }
.btn-delete-conv:hover { color: var(--color-action-destructive); }
.chat-main {
flex: 1;
@@ -721,7 +729,7 @@ onUnmounted(() => {
cursor: pointer;
}
.scope-option:hover { background: var(--color-bg-secondary); }
.scope-option.active { color: var(--color-primary); font-weight: 600; }
.scope-option.active { color: var(--color-primary); font-weight: 500; }
/* Context toggle button (header) */
.btn-context-toggle {
@@ -748,7 +756,7 @@ onUnmounted(() => {
.context-icon { font-size: 0.85rem; line-height: 1; }
.context-count-badge {
font-size: 0.65rem;
font-weight: 700;
font-weight: 500;
background: var(--color-bg);
padding: 0.05rem 0.35rem;
border-radius: 8px;
-6
View File
@@ -389,12 +389,6 @@ onMounted(async () => {
.journal-chat-panel { flex: 1; min-height: 0; }
/* Long-form reading line-height for journal assistant bubbles
(the daily prep is prose, not chat snippets — wants the doc's 1.7) */
.journal-chat-panel :deep(.role-assistant .message-content) {
line-height: 1.7;
}
/* ── Right column ─────────────────────────────────────────────────────────── */
.journal-right {
grid-column: 2;