feat: Animated pixel art ocean floor background for FloatingTranscriptDebug

Replace galaxy background with animated underwater scene featuring water
depth gradient, pixel art sea floor (sand, seaweed, coral, starfish),
rising bubbles with water sway, and swimming pixel art fish. Also update
transcript-debug tool cards styling and add .claude-*/tasks/ to gitignore.
This commit is contained in:
2026-02-19 14:44:25 -06:00
parent 04f3fe053d
commit c8e8e50fd6
12 changed files with 379 additions and 304 deletions

View File

@@ -4,6 +4,12 @@ import type { ParsedUserMessage } from '@/types/transcript-debug'
const props = defineProps<{
message: ParsedUserMessage
collapsed?: boolean
sectionCount?: number
}>()
const emit = defineEmits<{
toggleCollapse: []
}>()
const isOptimistic = computed(() => props.message.uuid.startsWith('optimistic-'))
@@ -15,57 +21,151 @@ function formatTime(ts: string): string {
</script>
<template>
<div :class="['user-bubble', { meta: message.isMeta, optimistic: isOptimistic }]">
<div class="bubble-header">
<span class="role-badge">User</span>
<span v-if="message.isMeta" class="meta-badge">meta</span>
<span v-if="isOptimistic" class="sending-badge">
<span class="sending-dot"></span>
<span class="sending-dot"></span>
<span class="sending-dot"></span>
Sending
</span>
<span class="timestamp">{{ formatTime(message.timestamp) }}</span>
<div :class="['user-divider', { meta: message.isMeta, optimistic: isOptimistic }]">
<div class="divider-line" />
<div class="divider-content">
<div class="divider-header">
<span class="role-badge">User</span>
<span v-if="message.isMeta" class="meta-badge">meta</span>
<span v-if="isOptimistic" class="sending-badge">
<span class="sending-dot"></span>
<span class="sending-dot"></span>
<span class="sending-dot"></span>
Sending
</span>
<button
v-if="sectionCount && sectionCount > 0"
:class="['collapse-btn', { collapsed }]"
@click.stop="emit('toggleCollapse')"
:title="collapsed ? 'Expand section' : 'Collapse section'"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<polyline :points="collapsed ? '9 18 15 12 9 6' : '6 9 12 15 18 9'" />
</svg>
<span v-if="collapsed" class="collapse-count">{{ sectionCount }}</span>
</button>
<span class="timestamp">{{ formatTime(message.timestamp) }}</span>
</div>
<div class="divider-text">{{ message.content }}</div>
</div>
<div class="bubble-content">{{ message.content }}</div>
</div>
</template>
<style scoped>
.user-bubble {
.user-divider {
width: 100%;
margin: 0.75rem 0 0.25rem;
}
.divider-line {
height: 1px;
background: linear-gradient(90deg, transparent, rgba(129, 140, 248, 0.2) 10%, rgba(129, 140, 248, 0.2) 90%, transparent);
margin-bottom: 0.5rem;
}
.divider-content {
padding: 0 0.25rem;
}
.divider-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.25rem;
}
.role-badge {
font-size: 10px;
font-weight: 700;
color: #818cf8;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.meta-badge {
font-size: 9px;
padding: 0.05rem 0.3rem;
border-radius: 3px;
background: transparent;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
color: rgba(251, 191, 36, 0.7);
font-weight: 600;
}
.timestamp {
font-size: 10px;
color: var(--text-muted);
margin-left: auto;
font-family: 'SF Mono', 'Fira Code', monospace;
}
.divider-text {
font-size: 13px;
line-height: 1.5;
color: var(--text-primary);
font-weight: 500;
white-space: pre-wrap;
word-break: break-word;
}
/* Meta messages: dimmed */
.user-divider.meta {
opacity: 0.45;
}
.user-divider.meta .divider-line {
background: linear-gradient(90deg, transparent, rgba(251, 191, 36, 0.15) 10%, rgba(251, 191, 36, 0.15) 90%, transparent);
}
/* Collapse button */
.collapse-btn {
display: inline-flex;
align-items: center;
gap: 2px;
padding: 1px 4px;
border: none;
border-radius: 12px;
padding: 0.75rem 1rem;
margin-left: 2rem;
transition: opacity 0.3s, border-color 0.3s, background 0.3s;
}
.user-bubble.meta {
opacity: 0.5;
border-style: dashed;
}
/* Optimistic / sending state */
.user-bubble.optimistic {
opacity: 0.7;
border-color: rgba(99, 102, 241, 0.08);
border-style: dashed;
border-radius: 3px;
background: transparent;
cursor: pointer;
color: var(--text-muted);
transition: all 0.15s;
}
.collapse-btn:hover {
background: rgba(129, 140, 248, 0.1);
color: #818cf8;
}
.collapse-btn svg {
transition: transform 0.2s ease;
}
.collapse-count {
font-size: 9px;
font-weight: 600;
color: var(--text-muted);
font-family: 'SF Mono', 'Fira Code', monospace;
}
.collapse-btn:hover .collapse-count {
color: #818cf8;
}
/* Optimistic / sending */
.user-divider.optimistic {
opacity: 0.6;
}
.user-divider.optimistic .divider-line {
background: linear-gradient(90deg, transparent, rgba(99, 102, 241, 0.1) 10%, rgba(99, 102, 241, 0.1) 90%, transparent);
}
.sending-badge {
display: inline-flex;
align-items: center;
gap: 3px;
font-size: 10px;
font-size: 9px;
color: var(--accent, #6366f1);
font-weight: 500;
padding: 0.1rem 0.4rem;
border-radius: 4px;
background: rgba(99, 102, 241, 0.08);
}
.sending-dot {
@@ -83,42 +183,4 @@ function formatTime(ts: string): string {
0%, 80%, 100% { opacity: 0.2; }
40% { opacity: 1; }
}
.bubble-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.role-badge {
font-size: 11px;
font-weight: 600;
color: #818cf8;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.meta-badge {
font-size: 10px;
padding: 0.1rem 0.4rem;
border-radius: 4px;
background: rgba(251, 191, 36, 0.15);
color: #fbbf24;
}
.timestamp {
font-size: 11px;
color: var(--text-muted);
margin-left: auto;
font-family: 'SF Mono', 'Fira Code', monospace;
}
.bubble-content {
font-size: 13px;
line-height: 1.6;
color: var(--text-primary);
white-space: pre-wrap;
word-break: break-word;
}
</style>