feat(xp): add interactive code-editor ui for experiences#31
feat(xp): add interactive code-editor ui for experiences#31
Conversation
Replace the vertical timeline with a file-explorer and code-editor styled view for career experiences to provide a more interactive, developer-oriented presentation. This change adds a sidebar file-tree, editor tabs, breadcrumb, line-numbered code block rendering of each experience (including JSDoc-style comments, export object, tags and stack), a logo watermark, and an editor status bar. It also introduces state for active tabs, tab closing, and adjustments to utility functions and styles (spacing, responsive tweaks, syntax classes) to support the new UI layout.
WalkthroughThe ExperiencePage component's timeline-based UI is replaced with a code editor interface featuring tabs for experiences, a file explorer sidebar, dynamic TypeScript-like code rendering with inline syntax highlighting, and revised logo handling. Multiple helper utilities for date formatting and via-logo mapping are removed. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/pages/ExperiencePage.vue (4)
302-330: RepeatedparseTechnologiescalls for same data.
parseTechnologies(visibleExperiences[activeTab].technologies)is called multiple times in the template for line number calculations and iteration. Consider computing it once:+ const activeTechnologies = computed(() => { + const active = visibleExperiences.value[activeTab.value]; + return active ? parseTechnologies(active.technologies) : []; + });Then use
activeTechnologiesin the template instead of repeatedparseTechnologies(...)calls.
136-149: Complex inline line number calculations are fragile and duplicated.Line numbers are computed inline with expressions like
(via ? X : Y) + highlights.length + techIndex. This pattern is repeated throughout the template, making it error-prone when adding/removing fields.Consider a computed property that pre-calculates line offsets:
const lineOffsets = computed(() => { const exp = visibleExperiences.value[activeTab.value]; if (!exp) return { export: 6, company: 7, /* ... */ }; const base = 6 + exp.highlights.length; const viaOffset = exp.via ? 1 : 0; return { export: base, company: base + 1, position: base + 2, via: exp.via ? base + 3 : null, periodStart: base + 3 + viaOffset, // ... etc }; });
65-72: Breadcrumb handles undefined safely but may display ".ts" alone.The optional chaining
visibleExperiences[activeTab]?.slugprevents crashes, but ifactiveTabis out of bounds, the breadcrumb would render as ".ts". Consider wrapping the breadcrumb in av-ifconsistent with the editor content guard at line 75.
388-389: Unnecessary interpolation for static values.
Ln {{ 1 }}, Col {{ 1 }}uses Vue interpolation for constant values. Simplify to plain text:- <span class="status-item" - >Ln {{ 1 }}, Col {{ 1 }}</span - > + <span class="status-item">Ln 1, Col 1</span>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/pages/ExperiencePage.vue(8 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.vue
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.vue: Indent content inside<script>and<style>tags in Vue files (configured viavueIndentScriptAndStyle: truein.prettierrc)
Use Vue 3 Composition API with<script setup>syntax for Vue components
D3 integration should use imperative DOM manipulation within Vue lifecycle hooks, kept separate from Vue reactivity
Files:
src/pages/ExperiencePage.vue
**/*.{ts,tsx,vue}
📄 CodeRabbit inference engine (CLAUDE.md)
Use constants:
IDEAL_BLIP_WIDTH = 22px,NEW_GROUP_BLIP_WIDTH = 88px,EXISTING_GROUP_BLIP_WIDTH = 124px
Files:
src/pages/ExperiencePage.vue
🧠 Learnings (1)
📚 Learning: 2025-12-14T01:59:00.938Z
Learnt from: CR
Repo: caiokf/homepage PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-14T01:59:00.938Z
Learning: Applies to **/*.{ts,tsx,vue} : Use constants: `IDEAL_BLIP_WIDTH = 22px`, `NEW_GROUP_BLIP_WIDTH = 88px`, `EXISTING_GROUP_BLIP_WIDTH = 124px`
Applied to files:
src/pages/ExperiencePage.vue
🔇 Additional comments (3)
src/pages/ExperiencePage.vue (3)
18-27: File tree and tabs are properly synchronized.Both the file explorer buttons and editor tabs correctly bind to
activeTabfor selection state and click handlers. This keeps the two UI elements in sync.Also applies to: 50-61
400-403: Vue 3 Composition API usage is correct.The component properly uses
<script setup>withrefandcomputedfor reactive state management, following Vue 3 best practices and the coding guidelines.
500-931: Styles are well-organized with proper scoping.The scoped CSS uses consistent design tokens, handles responsive breakpoints appropriately, and provides clear syntax highlighting classes for the code-editor aesthetic.
| function closeTab(index: number) { | ||
| if (visibleExperiences.value.length <= 1) return; | ||
| if (activeTab.value >= index && activeTab.value > 0) { | ||
| activeTab.value--; | ||
| } | ||
| } |
There was a problem hiding this comment.
closeTab adjusts selection but doesn't actually close the tab.
The function decrements activeTab when appropriate but never removes the tab from visibleExperiences. The closed tab remains visible in the UI. If tab closing is intended, you need to track closed tabs separately (since visibleExperiences is derived from config). If closing isn't meant to be functional, consider removing the close button to avoid user confusion.
+ const closedTabs = ref<Set<number>>(new Set());
+
function closeTab(index: number) {
if (visibleExperiences.value.length <= 1) return;
+ closedTabs.value.add(index);
if (activeTab.value >= index && activeTab.value > 0) {
activeTab.value--;
}
}Then filter visibleExperiences or the rendered tabs based on closedTabs.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/pages/ExperiencePage.vue around lines 442-447, closeTab currently only
adjusts activeTab but never removes the closed tab from the UI; either remove
the close button from the template if closing is not intended, or implement real
closing by tracking closed tabs (e.g., a reactive Set or array of closed
experience ids): update closeTab to add the target experience id to closedTabs,
adjust activeTab to a valid index if needed, and change the computed/templated
visibleExperiences rendering to filter out any experiences whose id is in
closedTabs so the tab actually disappears from the UI.
Replace the vertical timeline with a file-explorer and code-editor styled view for career experiences to provide a more interactive, developer-oriented presentation. This change adds a sidebar file-tree, editor tabs, breadcrumb, line-numbered code block rendering of each experience (including JSDoc-style comments, export object, tags and stack), a logo watermark, and an editor status bar. It also introduces state for active tabs, tab closing, and adjustments to utility functions and styles (spacing, responsive tweaks, syntax classes) to support the new UI layout.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.