Skip to content

feat(xp): render experience timeline as a git-style terminal log#30

Open
caiokf wants to merge 1 commit intomainfrom
render-experience-as-git-terminal-log
Open

feat(xp): render experience timeline as a git-style terminal log#30
caiokf wants to merge 1 commit intomainfrom
render-experience-as-git-terminal-log

Conversation

@caiokf
Copy link
Owner

@caiokf caiokf commented Dec 14, 2025

git-log

Replace the old sidebar badges and linear timeline with a rich, git-inspired terminal view to present experiences as commits. This refactor adds a terminal header, commit list navigation, a styled git log window with graph visualization, commit metadata (hashes, author, date), tags as refs, tech badges as diff stats, and interactive behaviors (hover highlighting, smooth scroll to commit, show-more). It also removes the old BadgeGroup import/usages and via-logo helper, consolidates logo handling, updates date/formatting and duration formatting, and introduces many CSS rules to style the new terminal/log UI for multiple responsive breakpoints.

Summary by CodeRabbit

  • New Features
    • Redesigned experience page with a terminal-style git log visualization
    • Added interactive commit-style entries for navigating career history
    • Implemented graph-based timeline visualization with color-coded branches
    • Added expandable view to display full history of experiences
    • Enhanced technology badges and tag display within each entry
    • Improved date and duration formatting for better readability

✏️ Tip: You can customize this high-level summary in your review settings.

Replace the old sidebar badges and linear timeline with a rich, git-inspired terminal view to present experiences as commits. This refactor adds a terminal header, commit list navigation, a styled git log window with graph visualization, commit metadata (hashes, author, date), tags as refs, tech badges as diff stats, and interactive behaviors (hover highlighting, smooth scroll to commit, show-more). It also removes the old BadgeGroup import/usages and via-logo helper, consolidates logo handling, updates date/formatting and duration formatting, and introduces many CSS rules to style the new terminal/log UI for multiple responsive breakpoints.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 14, 2025

Walkthrough

ExperiencePage.vue has been redesigned from a traditional technologies sidebar to a terminal-style git log interface. The update introduces commit-oriented navigation, synthetic commit hashing, graph visualization, color-coded branches and tags, and interactive scrolling with expanded history toggling.

Changes

Cohort / File(s) Summary
Terminal-style Experience UI Redesign
src/pages/ExperiencePage.vue
Replaced sidebar UI with git log-themed terminal window. Added interactive state (hoveredIndex, showAll, commitRefs) and helper functions (getFullHash, getShortHash, getBranchColor, getTagColor, formatGitDate, calculateDuration, scrollToCommit). Introduced computed aggregations (totalCompanies, uniqueTags, visibleExperiences). Updated styling with terminal/log aesthetic classes and reworked logo handling and technology badge rendering.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Interactive state management: Verify hoveredIndex, showAll, and commitRefs logic is correct and handles edge cases
  • Synthetic hash generation: Review getFullHash and getShortHash implementations for consistency and collision avoidance
  • Color and formatting functions: Ensure getBranchColor, getTagColor, formatGitDate, and calculateDuration produce expected outputs across edge cases
  • Computed properties: Check totalCompanies, uniqueTags, and visibleExperiences calculations for accuracy
  • Styling and layout: Verify CSS classes (terminal-window, git-log, commit-entry, etc.) align with visual design intent and responsive behavior

Poem

🐰 From sidebar to git log we hop,
Where commits bloom and branches don't stop,
Terminal vibes and hashes so neat,
Technologies dance in their badge retreat,
The experience flows with a colorful beat! 🌲✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: replacing the experience sidebar UI with a git-style terminal log visualization, which is the primary focus of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch render-experience-as-git-terminal-log

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/ExperiencePage.vue (1)

356-384: Clarify the "ago" suffix for current positions.

Lines 379-381 append "ago" to the duration when !experience.endDate (current position). This is semantically incorrect—"2y 3m ago" implies the experience ended 2y 3m in the past, but for current positions, the duration should represent time elapsed since starting, not time since ending.

Consider either:

  1. Removing the "ago" suffix for current positions, or
  2. Changing the logic to show elapsed time since start more clearly

Apply this diff to remove the misleading "ago" suffix:

  function calculateDuration(experience: Experience): string {
    const startDate = new Date(experience.startDate);
    const endDate = experience.endDate ? new Date(experience.endDate) : new Date();

    const months =
      (endDate.getFullYear() - startDate.getFullYear()) * 12 +
      (endDate.getMonth() - startDate.getMonth());

    const years = Math.floor(months / 12);
    const remainingMonths = months % 12;

    let duration = "";
    if (years > 0) {
      duration += `${years}y`;
    }
    if (remainingMonths > 0) {
      if (duration) duration += " ";
      duration += `${remainingMonths}m`;
    }
    if (!duration) {
      duration = "<1m";
    }

-   if (!experience.endDate) {
-     duration += " ago";
-   }

    return duration;
  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9570d4e and da3ddbc.

📒 Files selected for processing (1)
  • src/pages/ExperiencePage.vue (7 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 via vueIndentScriptAndStyle: true in .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 (2)
📚 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 **/*.vue : Use Vue 3 Composition API with `<script setup>` syntax for Vue components

Applied to files:

  • src/pages/ExperiencePage.vue
📚 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 (14)
src/pages/ExperiencePage.vue (14)

7-26: LGTM!

The terminal header and commit navigation are well-implemented with proper interactive states (hover, active) and semantic button elements for accessibility.


28-42: LGTM!

Career stats display properly uses computed properties and presents the information clearly.


46-54: LGTM!

Terminal window chrome provides good visual fidelity to the git terminal aesthetic.


70-80: LGTM!

The git graph visualization correctly handles visual states and boundary conditions for the graph lines and nodes.


85-118: LGTM!

Commit metadata is well-structured with proper conditional rendering for optional fields like website links.


157-166: LGTM!

Technology badges are rendered correctly using the parsed technologies list.


180-195: LGTM!

The show-more functionality provides good progressive disclosure with clear messaging about the hidden content.


238-254: LGTM!

The ref handling and scroll navigation are properly implemented using Vue 3's Composition API with appropriate type checking.


256-281: LGTM!

All computed properties are correctly implemented with proper use of Sets for uniqueness and efficient calculations.


284-295: LGTM!

The hash generation is deterministic and correctly uses modulo to safely index the base string. While not cryptographically secure, it's appropriate for generating stable UI identifiers.


298-328: LGTM!

Color mapping functions properly handle tag-to-CSS-class mapping with appropriate fallbacks.


330-354: LGTM!

Date formatting correctly mimics git's date output format with proper day/month name lookups.


386-388: LGTM!

Technology parsing correctly splits and trims the comma-separated technology list.


391-1007: LGTM!

The styling comprehensively implements the terminal/git log aesthetic with proper use of CSS custom properties, flexible layouts, and responsive design patterns. The code follows Vue file indentation guidelines.

Based on learnings, the style content inside the <style> tag is properly indented as per the vueIndentScriptAndStyle: true configuration.

Comment on lines +144 to +156
<div class="commit-diff">
<div class="diff-header">
<span class="diff-files"
>{{ parseTechnologies(experience.technologies).length }} technologies</span
>
<span class="diff-separator">|</span>
<span class="diff-insertions"
>+{{ Math.floor(Math.random() * 50000) + 10000 }}</span
>
<span class="diff-deletions"
>-{{ Math.floor(Math.random() * 20000) + 5000 }}</span
>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make diff stats deterministic.

The diff insertions and deletions use Math.random() on lines 150-154, causing the values to change on every render. This breaks the git metaphor where diff stats are stable and consistent.

Apply this diff to generate deterministic values based on the experience data:

                  <div class="diff-header">
                    <span class="diff-files"
                      >{{ parseTechnologies(experience.technologies).length }} technologies</span
                    >
                    <span class="diff-separator">|</span>
                    <span class="diff-insertions"
-                     >+{{ Math.floor(Math.random() * 50000) + 10000 }}</span
+                     >+{{ Math.floor((experience.slug.length * 3000 + index * 2000) % 50000) + 10000 }}</span
                    >
                    <span class="diff-deletions"
-                     >-{{ Math.floor(Math.random() * 20000) + 5000 }}</span
+                     >-{{ Math.floor((experience.slug.length * 1000 + index * 500) % 20000) + 5000 }}</span
                    >
                  </div>

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/pages/ExperiencePage.vue around lines 144-156, the diff
insertions/deletions use Math.random() which changes on every render; replace
them with deterministic values derived from the experience data: compute a small
deterministic numeric hash (e.g., sum/rolling hash of experience.id or
JSON.stringify(experience) or experience.title) and map it into the original
ranges (insertions = (hash % 50000) + 10000, deletions = (hash2 % 20000) +
5000). Move this logic into a computed property or a helper function that
returns insertions/deletions for the given experience so the values remain
stable across renders.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant