heyiam 0.2.28 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/config.js +10 -1
- package/dist/db.js +1 -2
- package/dist/export.js +40 -25
- package/dist/format-utils.js +5 -0
- package/dist/index.js +168 -0
- package/dist/mount.js +300 -102
- package/dist/parsers/claude.js +2 -28
- package/dist/parsers/codex.js +2 -26
- package/dist/parsers/cursor.js +2 -26
- package/dist/parsers/duration.js +35 -0
- package/dist/parsers/gemini.js +2 -20
- package/dist/parsers/types.js +0 -1
- package/dist/public/assets/index-BZ65TU_Y.js +40 -0
- package/dist/public/assets/index-CqCaW2cb.css +1 -0
- package/dist/public/index.html +2 -2
- package/dist/redact.js +4 -104
- package/dist/render/build-render-data.js +9 -2
- package/dist/render/index.js +32 -5
- package/dist/render/liquid.js +147 -7
- package/dist/render/mock-data.js +303 -0
- package/dist/render/templates/aurora/portfolio.liquid +204 -0
- package/dist/render/templates/aurora/project.liquid +260 -0
- package/dist/render/templates/aurora/session.liquid +223 -0
- package/dist/render/templates/aurora/styles.css +1178 -0
- package/dist/render/templates/bauhaus/portfolio.liquid +179 -0
- package/dist/render/templates/bauhaus/project.liquid +300 -0
- package/dist/render/templates/bauhaus/session.liquid +333 -0
- package/dist/render/templates/bauhaus/styles.css +1641 -0
- package/dist/render/templates/blueprint/portfolio.liquid +167 -0
- package/dist/render/templates/blueprint/project.liquid +286 -0
- package/dist/render/templates/blueprint/session.liquid +248 -0
- package/dist/render/templates/blueprint/styles.css +1285 -0
- package/dist/render/templates/canvas/portfolio.liquid +215 -0
- package/dist/render/templates/canvas/project.liquid +235 -0
- package/dist/render/templates/canvas/session.liquid +223 -0
- package/dist/render/templates/canvas/styles.css +1436 -0
- package/dist/render/templates/carbon/portfolio.liquid +170 -0
- package/dist/render/templates/carbon/project.liquid +249 -0
- package/dist/render/templates/carbon/session.liquid +190 -0
- package/dist/render/templates/carbon/styles.css +1091 -0
- package/dist/render/templates/chalk/portfolio.liquid +199 -0
- package/dist/render/templates/chalk/project.liquid +245 -0
- package/dist/render/templates/chalk/session.liquid +215 -0
- package/dist/render/templates/chalk/styles.css +1157 -0
- package/dist/render/templates/circuit/portfolio.liquid +162 -0
- package/dist/render/templates/circuit/project.liquid +247 -0
- package/dist/render/templates/circuit/session.liquid +205 -0
- package/dist/render/templates/circuit/styles.css +1403 -0
- package/dist/render/templates/cosmos/portfolio.liquid +232 -0
- package/dist/render/templates/cosmos/project.liquid +327 -0
- package/dist/render/templates/cosmos/session.liquid +239 -0
- package/dist/render/templates/cosmos/styles.css +1151 -0
- package/dist/render/templates/daylight/portfolio.liquid +217 -0
- package/dist/render/templates/daylight/project.liquid +229 -0
- package/dist/render/templates/daylight/session.liquid +219 -0
- package/dist/render/templates/daylight/styles.css +1311 -0
- package/dist/render/templates/editorial/portfolio.liquid +126 -0
- package/dist/render/templates/editorial/project.liquid +202 -0
- package/dist/render/templates/editorial/session.liquid +171 -0
- package/dist/render/templates/editorial/styles.css +822 -0
- package/dist/render/templates/ember/portfolio.liquid +318 -0
- package/dist/render/templates/ember/project.liquid +232 -0
- package/dist/render/templates/ember/session.liquid +202 -0
- package/dist/render/templates/ember/styles.css +1283 -0
- package/dist/render/templates/glacier/portfolio.liquid +271 -0
- package/dist/render/templates/glacier/project.liquid +288 -0
- package/dist/render/templates/glacier/session.liquid +217 -0
- package/dist/render/templates/glacier/styles.css +1200 -0
- package/dist/render/templates/grid/portfolio.liquid +265 -0
- package/dist/render/templates/grid/project.liquid +306 -0
- package/dist/render/templates/grid/session.liquid +260 -0
- package/dist/render/templates/grid/styles.css +1441 -0
- package/dist/render/templates/kinetic/portfolio.liquid +170 -0
- package/dist/render/templates/kinetic/project.liquid +242 -0
- package/dist/render/templates/kinetic/session.liquid +228 -0
- package/dist/render/templates/kinetic/styles.css +944 -0
- package/dist/render/templates/meridian/portfolio.liquid +255 -0
- package/dist/render/templates/meridian/project.liquid +376 -0
- package/dist/render/templates/meridian/session.liquid +298 -0
- package/dist/render/templates/meridian/styles.css +1369 -0
- package/dist/render/templates/minimal/portfolio.liquid +71 -0
- package/dist/render/templates/minimal/project.liquid +154 -0
- package/dist/render/templates/minimal/session.liquid +140 -0
- package/dist/render/templates/minimal/styles.css +525 -0
- package/dist/render/templates/mono/portfolio.liquid +291 -0
- package/dist/render/templates/mono/project.liquid +275 -0
- package/dist/render/templates/mono/session.liquid +276 -0
- package/dist/render/templates/mono/styles.css +1016 -0
- package/dist/render/templates/neon/portfolio.liquid +217 -0
- package/dist/render/templates/neon/project.liquid +225 -0
- package/dist/render/templates/neon/session.liquid +195 -0
- package/dist/render/templates/neon/styles.css +1265 -0
- package/dist/render/templates/noir/portfolio.liquid +137 -0
- package/dist/render/templates/noir/project.liquid +220 -0
- package/dist/render/templates/noir/session.liquid +241 -0
- package/dist/render/templates/noir/styles.css +1223 -0
- package/dist/render/templates/obsidian/portfolio.liquid +257 -0
- package/dist/render/templates/obsidian/project.liquid +280 -0
- package/dist/render/templates/obsidian/session.liquid +241 -0
- package/dist/render/templates/obsidian/styles.css +1401 -0
- package/dist/render/templates/paper/portfolio.liquid +267 -0
- package/dist/render/templates/paper/project.liquid +235 -0
- package/dist/render/templates/paper/session.liquid +271 -0
- package/dist/render/templates/paper/styles.css +1509 -0
- package/dist/render/templates/parallax/portfolio.liquid +305 -0
- package/dist/render/templates/parallax/project.liquid +275 -0
- package/dist/render/templates/parallax/session.liquid +295 -0
- package/dist/render/templates/parallax/styles.css +1874 -0
- package/dist/render/templates/parchment/portfolio.liquid +290 -0
- package/dist/render/templates/parchment/project.liquid +289 -0
- package/dist/render/templates/parchment/session.liquid +346 -0
- package/dist/render/templates/parchment/styles.css +1397 -0
- package/dist/render/templates/partials/_beats.liquid +16 -0
- package/dist/render/templates/partials/_breadcrumb.liquid +9 -0
- package/dist/render/templates/partials/_footer.liquid +7 -0
- package/dist/render/templates/partials/_growth-chart.liquid +7 -0
- package/dist/render/templates/partials/_key-decisions.liquid +20 -0
- package/dist/render/templates/partials/_links.liquid +16 -0
- package/dist/render/templates/partials/_narrative.liquid +8 -0
- package/dist/render/templates/partials/_phases.liquid +20 -0
- package/dist/render/templates/partials/_portfolio-header.liquid +20 -0
- package/dist/render/templates/partials/_portfolio-projects.liquid +16 -0
- package/dist/render/templates/partials/_portfolio-stats.liquid +19 -0
- package/dist/render/templates/partials/_qa.liquid +13 -0
- package/dist/render/templates/partials/_screenshot.liquid +15 -0
- package/dist/render/templates/partials/_session-cards.liquid +30 -0
- package/dist/render/templates/partials/_session-header.liquid +39 -0
- package/dist/render/templates/partials/_session-sidebar.liquid +30 -0
- package/dist/render/templates/partials/_skills.liquid +12 -0
- package/dist/render/templates/partials/_source-breakdown.liquid +22 -0
- package/dist/render/templates/partials/_stats.liquid +38 -0
- package/dist/render/templates/partials/_work-timeline.liquid +7 -0
- package/dist/render/templates/project.liquid +7 -4
- package/dist/render/templates/radar/portfolio.liquid +233 -0
- package/dist/render/templates/radar/project.liquid +278 -0
- package/dist/render/templates/radar/session.liquid +300 -0
- package/dist/render/templates/radar/styles.css +1049 -0
- package/dist/render/templates/showcase/portfolio.liquid +231 -0
- package/dist/render/templates/showcase/project.liquid +237 -0
- package/dist/render/templates/showcase/session.liquid +210 -0
- package/dist/render/templates/showcase/styles.css +1279 -0
- package/dist/render/templates/signal/portfolio.liquid +227 -0
- package/dist/render/templates/signal/project.liquid +278 -0
- package/dist/render/templates/signal/session.liquid +282 -0
- package/dist/render/templates/signal/styles.css +1395 -0
- package/dist/render/templates/strata/portfolio.liquid +192 -0
- package/dist/render/templates/strata/project.liquid +282 -0
- package/dist/render/templates/strata/session.liquid +261 -0
- package/dist/render/templates/strata/styles.css +1350 -0
- package/dist/render/templates/styles.css +1190 -0
- package/dist/render/templates/terminal/portfolio.liquid +118 -0
- package/dist/render/templates/terminal/project.liquid +161 -0
- package/dist/render/templates/terminal/session.liquid +145 -0
- package/dist/render/templates/terminal/styles.css +492 -0
- package/dist/render/templates/verdant/portfolio.liquid +333 -0
- package/dist/render/templates/verdant/project.liquid +309 -0
- package/dist/render/templates/verdant/session.liquid +237 -0
- package/dist/render/templates/verdant/styles.css +1257 -0
- package/dist/render/templates/zen/portfolio.liquid +136 -0
- package/dist/render/templates/zen/project.liquid +187 -0
- package/dist/render/templates/zen/session.liquid +203 -0
- package/dist/render/templates/zen/styles.css +1207 -0
- package/dist/render/templates.js +90 -0
- package/dist/routes/context.js +15 -10
- package/dist/routes/enhance.js +17 -40
- package/dist/routes/export.js +14 -4
- package/dist/routes/preview.js +480 -108
- package/dist/routes/projects.js +11 -19
- package/dist/routes/publish.js +15 -17
- package/dist/routes/settings.js +94 -1
- package/dist/routes/sse.js +9 -0
- package/dist/server.js +8 -2
- package/dist/settings.js +17 -9
- package/package.json +2 -4
- package/dist/public/assets/index-B_d6DlEI.js +0 -21
- package/dist/public/assets/index-Dalqz2mC.css +0 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
<div class="heyiam-portfolio glacier" data-render-version="2" data-template="glacier" data-username="{{ user.username }}">
|
|
2
|
+
|
|
3
|
+
{%- comment -%} Header {%- endcomment -%}
|
|
4
|
+
{% if hasProfile %}
|
|
5
|
+
<header class="header container">
|
|
6
|
+
{% if user.photoUrl %}
|
|
7
|
+
<img src="{{ user.photoUrl }}" alt="{{ user.displayName }}" class="header-photo">
|
|
8
|
+
{% elsif user.displayName != blank %}
|
|
9
|
+
<div class="header-avatar" aria-hidden="true">{{ user.displayName | slice: 0 }}</div>
|
|
10
|
+
{% endif %}
|
|
11
|
+
{% if user.displayName != blank %}
|
|
12
|
+
<h1>{{ user.displayName }}</h1>
|
|
13
|
+
{% endif %}
|
|
14
|
+
{% if user.bio %}
|
|
15
|
+
<p class="header-bio">{{ user.bio }}</p>
|
|
16
|
+
{% endif %}
|
|
17
|
+
{% if user.location %}
|
|
18
|
+
<p class="header-location">{{ user.location }}</p>
|
|
19
|
+
{% endif %}
|
|
20
|
+
|
|
21
|
+
{% if user.email or user.linkedinUrl or user.githubUrl or user.twitterHandle or user.websiteUrl or user.resumeUrl %}
|
|
22
|
+
<ul class="header-links" aria-label="Contact and social links">
|
|
23
|
+
{% if user.email %}
|
|
24
|
+
<li><a href="mailto:{{ user.email }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 4 10 8 10-8"/></svg>{{ user.email }}</a></li>
|
|
25
|
+
{% endif %}
|
|
26
|
+
{% if user.linkedinUrl %}
|
|
27
|
+
<li><a href="{{ user.linkedinUrl }}" target="_blank" rel="noopener"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M20.5 2h-17A1.5 1.5 0 002 3.5v17A1.5 1.5 0 003.5 22h17a1.5 1.5 0 001.5-1.5v-17A1.5 1.5 0 0020.5 2zM8 19H5v-9h3zM6.5 8.25A1.75 1.75 0 118.3 6.5a1.78 1.78 0 01-1.8 1.75zM19 19h-3v-4.74c0-1.42-.6-1.93-1.38-1.93A1.74 1.74 0 0013 14.19V19h-3v-9h2.9v1.3a3.11 3.11 0 012.7-1.4c1.55 0 3.36.86 3.36 3.66z"/></svg>LinkedIn</a></li>
|
|
28
|
+
{% endif %}
|
|
29
|
+
{% if user.githubUrl %}
|
|
30
|
+
<li><a href="{{ user.githubUrl }}" target="_blank" rel="noopener"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>GitHub</a></li>
|
|
31
|
+
{% endif %}
|
|
32
|
+
{% if user.twitterHandle %}
|
|
33
|
+
<li><a href="https://x.com/{{ user.twitterHandle }}" target="_blank" rel="noopener"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>@{{ user.twitterHandle }}</a></li>
|
|
34
|
+
{% endif %}
|
|
35
|
+
{% if user.websiteUrl %}
|
|
36
|
+
<li><a href="{{ user.websiteUrl }}" target="_blank" rel="noopener"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>{{ user.websiteUrl | stripProtocol }}</a></li>
|
|
37
|
+
{% endif %}
|
|
38
|
+
{% if user.resumeUrl %}
|
|
39
|
+
<li><a href="{{ user.resumeUrl }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><polyline points="9 15 12 18 15 15"/></svg>Resume (PDF)</a></li>
|
|
40
|
+
{% endif %}
|
|
41
|
+
</ul>
|
|
42
|
+
{% endif %}
|
|
43
|
+
</header>
|
|
44
|
+
{% endif %}
|
|
45
|
+
|
|
46
|
+
{%- comment -%} Stats {%- endcomment -%}
|
|
47
|
+
<section class="container" aria-label="Aggregate statistics">
|
|
48
|
+
<div class="stats">
|
|
49
|
+
<div class="stat-card frost fade-up">
|
|
50
|
+
<span class="stat-value">{{ projects.size }}</span>
|
|
51
|
+
<span class="stat-label">Projects</span>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="stat-card frost fade-up">
|
|
54
|
+
<span class="stat-value">{{ totalSessions }}</span>
|
|
55
|
+
<span class="stat-label">Sessions</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="stat-card frost fade-up">
|
|
58
|
+
<span class="stat-value">{{ totalLoc | localeNumber }}</span>
|
|
59
|
+
<span class="stat-label">Lines Changed</span>
|
|
60
|
+
</div>
|
|
61
|
+
{% if efficiencyMultiplier %}
|
|
62
|
+
<div class="stat-card frost fade-up">
|
|
63
|
+
<span class="stat-value">{{ totalDurationMinutes | formatDuration }}</span>
|
|
64
|
+
<span class="stat-label">{{ durationLabel }}</span>
|
|
65
|
+
</div>
|
|
66
|
+
{% else %}
|
|
67
|
+
<div class="stat-card frost fade-up">
|
|
68
|
+
<span class="stat-value">{{ totalDurationMinutes | formatDuration }}</span>
|
|
69
|
+
<span class="stat-label">Time</span>
|
|
70
|
+
</div>
|
|
71
|
+
{% endif %}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{% if efficiencyMultiplier %}
|
|
75
|
+
{% assign totalCombined = totalDurationMinutes | plus: totalAgentDurationMinutes %}
|
|
76
|
+
{% if totalCombined > 0 %}
|
|
77
|
+
{% assign humanPct = totalDurationMinutes | times: 100 | divided_by: totalCombined %}
|
|
78
|
+
{% assign agentPct = 100 | minus: humanPct %}
|
|
79
|
+
<div class="gl-leverage frost fade-up" role="figure" aria-label="Leverage: {{ efficiencyMultiplier }} multiplier">
|
|
80
|
+
<svg width="64" height="64" viewBox="0 0 64 64">
|
|
81
|
+
<circle cx="32" cy="32" r="26" fill="none" stroke="rgba(56,189,248,0.12)" stroke-width="5"/>
|
|
82
|
+
<circle cx="32" cy="32" r="26" fill="none" stroke="var(--accent)" stroke-width="5" stroke-dasharray="{{ humanPct | times: 163 | divided_by: 100 }} 163" stroke-dashoffset="-33" stroke-linecap="round"/>
|
|
83
|
+
<text x="32" y="36" text-anchor="middle" font-family="var(--font-display)" font-size="14" font-weight="700" fill="var(--accent-dark)">{{ efficiencyMultiplier }}</text>
|
|
84
|
+
</svg>
|
|
85
|
+
<div class="gl-leverage__values">
|
|
86
|
+
<span class="gl-leverage__human">{{ totalDurationMinutes | formatDuration }} <small>you</small></span>
|
|
87
|
+
<span class="gl-leverage__agent">{{ totalAgentDurationMinutes | formatDuration }} <small>agents</small></span>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
{% endif %}
|
|
91
|
+
{% endif %}
|
|
92
|
+
</section>
|
|
93
|
+
|
|
94
|
+
{%- comment -%} Projects {%- endcomment -%}
|
|
95
|
+
{% if projects.size > 0 %}
|
|
96
|
+
<section class="projects container" aria-label="Projects">
|
|
97
|
+
<h2 class="section-heading">Projects</h2>
|
|
98
|
+
<div class="project-list">
|
|
99
|
+
{% for p in projects %}
|
|
100
|
+
<article class="project-card frost fade-up">
|
|
101
|
+
<div class="project-card-header">
|
|
102
|
+
<a href="/{{ user.username }}/{{ p.slug }}" class="project-name">{{ p.title }}</a>
|
|
103
|
+
</div>
|
|
104
|
+
{% if p.narrative %}
|
|
105
|
+
<p class="project-narrative">{{ p.narrative }}</p>
|
|
106
|
+
{% endif %}
|
|
107
|
+
{% if p.skills.size > 0 %}
|
|
108
|
+
<div class="project-skills">
|
|
109
|
+
{% for skill in p.skills %}
|
|
110
|
+
<span class="skill-chip">{{ skill }}</span>
|
|
111
|
+
{% endfor %}
|
|
112
|
+
</div>
|
|
113
|
+
{% endif %}
|
|
114
|
+
<div class="project-stats">
|
|
115
|
+
<span>{{ p.totalSessions }} session{% if p.totalSessions != 1 %}s{% endif %}</span>
|
|
116
|
+
<span>{{ p.totalDurationMinutes | formatDuration }}</span>
|
|
117
|
+
<span>{{ p.totalLoc | localeNumber }} LOC</span>
|
|
118
|
+
</div>
|
|
119
|
+
{% if p.sourceCounts.size > 0 %}
|
|
120
|
+
{% assign pTotalSrc = 0 %}
|
|
121
|
+
{% for src in p.sourceCounts %}{% assign pTotalSrc = pTotalSrc | plus: src.count %}{% endfor %}
|
|
122
|
+
{% if pTotalSrc > 0 %}
|
|
123
|
+
{% assign pFirstPct = p.sourceCounts[0].count | times: 100.0 | divided_by: pTotalSrc | round %}
|
|
124
|
+
<div class="source-bar" role="img" aria-label="Source mix">
|
|
125
|
+
<div class="source-bar-fill" style="width: {{ pFirstPct }}%"></div>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="source-legend">
|
|
128
|
+
{% for src in p.sourceCounts %}
|
|
129
|
+
{% assign pSrcPct = src.count | times: 100.0 | divided_by: pTotalSrc | round %}
|
|
130
|
+
<span><span class="source-legend-dot" style="background: {% if forloop.first %}var(--accent){% else %}#e2e8f0{% endif %}"></span>{{ src.tool }} {{ pSrcPct }}%</span>
|
|
131
|
+
{% endfor %}
|
|
132
|
+
</div>
|
|
133
|
+
{% endif %}
|
|
134
|
+
{% endif %}
|
|
135
|
+
</article>
|
|
136
|
+
{% endfor %}
|
|
137
|
+
</div>
|
|
138
|
+
</section>
|
|
139
|
+
{% endif %}
|
|
140
|
+
|
|
141
|
+
{%- comment -%} Activity Chart {%- endcomment -%}
|
|
142
|
+
{% if activityByMonth.size > 0 %}
|
|
143
|
+
{% assign maxLoc = 0 %}
|
|
144
|
+
{% for m in activityByMonth %}
|
|
145
|
+
{% for proj in m.projects %}
|
|
146
|
+
{% if proj.loc > maxLoc %}{% assign maxLoc = proj.loc %}{% endif %}
|
|
147
|
+
{% endfor %}
|
|
148
|
+
{% endfor %}
|
|
149
|
+
<section class="container fade-up" aria-label="Activity overview" style="margin-bottom: 48px;">
|
|
150
|
+
<h2 class="section-heading">Activity</h2>
|
|
151
|
+
<div class="frost" style="padding: 24px;">
|
|
152
|
+
<svg viewBox="0 0 900 220" role="img" aria-label="Monthly activity chart showing lines of code per project" style="width: 100%; height: auto;">
|
|
153
|
+
<line x1="60" y1="20" x2="60" y2="170" stroke="rgba(56,189,248,0.1)" stroke-width="1"/>
|
|
154
|
+
<line x1="60" y1="170" x2="880" y2="170" stroke="rgba(56,189,248,0.1)" stroke-width="1"/>
|
|
155
|
+
<line x1="60" y1="110" x2="880" y2="110" stroke="rgba(56,189,248,0.06)" stroke-width="1"/>
|
|
156
|
+
<line x1="60" y1="50" x2="880" y2="50" stroke="rgba(56,189,248,0.06)" stroke-width="1"/>
|
|
157
|
+
<text x="50" y="174" fill="#64748b" font-family="IBM Plex Mono, monospace" font-size="11" text-anchor="end">0</text>
|
|
158
|
+
{% if maxLoc > 0 %}
|
|
159
|
+
{% assign halfMax = maxLoc | divided_by: 2 %}
|
|
160
|
+
<text x="50" y="114" fill="#64748b" font-family="IBM Plex Mono, monospace" font-size="11" text-anchor="end">{{ halfMax | formatLoc }}</text>
|
|
161
|
+
<text x="50" y="54" fill="#64748b" font-family="IBM Plex Mono, monospace" font-size="11" text-anchor="end">{{ maxLoc | formatLoc }}</text>
|
|
162
|
+
{% endif %}
|
|
163
|
+
{% assign monthSpacing = 820 | divided_by: activityByMonth.size %}
|
|
164
|
+
{% for m in activityByMonth %}
|
|
165
|
+
{% assign outerIdx = forloop.index0 %}
|
|
166
|
+
{% assign groupX = outerIdx | times: monthSpacing | plus: 80 %}
|
|
167
|
+
{% for proj in m.projects %}
|
|
168
|
+
{% if maxLoc > 0 %}
|
|
169
|
+
{% assign barH = proj.loc | times: 150 | divided_by: maxLoc %}
|
|
170
|
+
{% else %}
|
|
171
|
+
{% assign barH = 0 %}
|
|
172
|
+
{% endif %}
|
|
173
|
+
{% assign barY = 170 | minus: barH %}
|
|
174
|
+
{% assign barX = forloop.index0 | times: 70 | plus: groupX %}
|
|
175
|
+
{% if forloop.index0 == 0 %}{% assign barFill = "#38bdf8" %}{% assign barOp = "0.8" %}
|
|
176
|
+
{% elsif forloop.index0 == 1 %}{% assign barFill = "#7dd3fc" %}{% assign barOp = "0.7" %}
|
|
177
|
+
{% else %}{% assign barFill = "#bae6fd" %}{% assign barOp = "0.8" %}
|
|
178
|
+
{% endif %}
|
|
179
|
+
<rect x="{{ barX }}" y="{{ barY }}" width="60" height="{{ barH }}" rx="6" fill="{{ barFill }}" opacity="{{ barOp }}" aria-label="{{ proj.name }} {{ m.month }}: {{ proj.loc }} LOC"/>
|
|
180
|
+
{% endfor %}
|
|
181
|
+
{% assign labelX = groupX | plus: 35 %}
|
|
182
|
+
<text x="{{ labelX }}" y="192" fill="#64748b" font-family="IBM Plex Mono, monospace" font-size="11" text-anchor="middle">{{ m.month }}</text>
|
|
183
|
+
{% endfor %}
|
|
184
|
+
{% assign legendX = 560 %}
|
|
185
|
+
{% assign legendY = 20 %}
|
|
186
|
+
{% for p in projects %}
|
|
187
|
+
{% if forloop.index0 == 0 %}{% assign dotFill = "#38bdf8" %}{% assign dotOp = "0.8" %}
|
|
188
|
+
{% elsif forloop.index0 == 1 %}{% assign dotFill = "#7dd3fc" %}{% assign dotOp = "0.7" %}
|
|
189
|
+
{% else %}{% assign dotFill = "#bae6fd" %}{% assign dotOp = "0.8" %}
|
|
190
|
+
{% endif %}
|
|
191
|
+
{% assign dotY = forloop.index0 | times: 20 | plus: legendY %}
|
|
192
|
+
<rect x="{{ legendX }}" y="{{ dotY }}" width="12" height="12" rx="3" fill="{{ dotFill }}" opacity="{{ dotOp }}"/>
|
|
193
|
+
{% assign textX = legendX | plus: 20 %}
|
|
194
|
+
{% assign textY = dotY | plus: 11 %}
|
|
195
|
+
<text x="{{ textX }}" y="{{ textY }}" fill="#64748b" font-family="IBM Plex Mono, monospace" font-size="11">{{ p.title }}</text>
|
|
196
|
+
{% if forloop.index0 >= 2 %}{% break %}{% endif %}
|
|
197
|
+
{% endfor %}
|
|
198
|
+
</svg>
|
|
199
|
+
</div>
|
|
200
|
+
</section>
|
|
201
|
+
{% endif %}
|
|
202
|
+
|
|
203
|
+
{%- comment -%} Skills Overview {%- endcomment -%}
|
|
204
|
+
{% if allSkills.size > 0 %}
|
|
205
|
+
<section class="container fade-up" aria-label="All skills">
|
|
206
|
+
<h2 class="section-heading">Skills</h2>
|
|
207
|
+
<div class="frost" style="padding: 28px;">
|
|
208
|
+
<div style="display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 24px;">
|
|
209
|
+
{% for skill in allSkills %}
|
|
210
|
+
<span class="skill-chip">{{ skill.name }}</span>
|
|
211
|
+
{% endfor %}
|
|
212
|
+
</div>
|
|
213
|
+
{% if topSkills.size > 0 %}
|
|
214
|
+
<div style="display: flex; flex-direction: column; gap: 12px;">
|
|
215
|
+
{% assign maxSkillCount = topSkills[0].projectCount %}
|
|
216
|
+
{% for skill in topSkills %}
|
|
217
|
+
<div>
|
|
218
|
+
<div style="display: flex; justify-content: space-between; margin-bottom: 4px;">
|
|
219
|
+
<span style="font-family: var(--font-mono); font-size: 13px; color: var(--fg);">{{ skill.name }}</span>
|
|
220
|
+
<span style="font-family: var(--font-mono); font-size: 12px; color: var(--fg-muted);">{{ skill.projectCount }} project{% if skill.projectCount != 1 %}s{% endif %}</span>
|
|
221
|
+
</div>
|
|
222
|
+
<div style="height: 6px; border-radius: 3px; background: rgba(56,189,248,0.1); overflow: hidden;">
|
|
223
|
+
{% if maxSkillCount > 0 %}
|
|
224
|
+
{% assign skillPct = skill.projectCount | times: 100 | divided_by: maxSkillCount %}
|
|
225
|
+
{% else %}
|
|
226
|
+
{% assign skillPct = 0 %}
|
|
227
|
+
{% endif %}
|
|
228
|
+
<div style="height: 100%; width: {{ skillPct }}%; border-radius: 3px; background: linear-gradient(90deg, var(--accent), #7dd3fc);"></div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
{% endfor %}
|
|
232
|
+
</div>
|
|
233
|
+
{% endif %}
|
|
234
|
+
</div>
|
|
235
|
+
</section>
|
|
236
|
+
{% endif %}
|
|
237
|
+
|
|
238
|
+
{%- comment -%} Source Overview {%- endcomment -%}
|
|
239
|
+
{% if sourceCounts.size > 0 %}
|
|
240
|
+
<section class="container fade-up" aria-label="Overall source breakdown">
|
|
241
|
+
<h2 class="section-heading">Source Breakdown</h2>
|
|
242
|
+
<div class="frost" style="padding: 28px;">
|
|
243
|
+
<div style="display: grid; grid-template-columns: repeat({{ sourceCounts.size }}, 1fr); gap: 24px; margin-bottom: 20px;">
|
|
244
|
+
{% for src in sourceCounts %}
|
|
245
|
+
{% if totalSourceSessions > 0 %}
|
|
246
|
+
{% assign srcPct = src.count | times: 100 | divided_by: totalSourceSessions %}
|
|
247
|
+
{% else %}
|
|
248
|
+
{% assign srcPct = 0 %}
|
|
249
|
+
{% endif %}
|
|
250
|
+
<div style="text-align: center;">
|
|
251
|
+
<div style="font-family: var(--font-display); font-size: 1.5rem; font-weight: 700; color: {% if forloop.first %}var(--accent){% else %}var(--fg-muted){% endif %};">{{ srcPct }}%</div>
|
|
252
|
+
<div style="font-family: var(--font-mono); font-size: 12px; color: var(--fg-muted); margin-top: 4px;">{{ src.tool }}</div>
|
|
253
|
+
<div style="font-family: var(--font-mono); font-size: 11px; color: var(--fg-muted); margin-top: 2px;">{{ src.count }} sessions</div>
|
|
254
|
+
</div>
|
|
255
|
+
{% endfor %}
|
|
256
|
+
</div>
|
|
257
|
+
{% if totalSourceSessions > 0 %}
|
|
258
|
+
<div style="height: 8px; border-radius: 4px; overflow: hidden; display: flex; background: rgba(56,189,248,0.1);">
|
|
259
|
+
{% for src in sourceCounts %}
|
|
260
|
+
{% assign srcPct = src.count | times: 100 | divided_by: totalSourceSessions %}
|
|
261
|
+
<div style="width: {{ srcPct }}%; height: 100%; background: {% if forloop.first %}var(--accent){% else %}#e2e8f0{% endif %};"></div>
|
|
262
|
+
{% endfor %}
|
|
263
|
+
</div>
|
|
264
|
+
{% endif %}
|
|
265
|
+
</div>
|
|
266
|
+
</section>
|
|
267
|
+
{% endif %}
|
|
268
|
+
|
|
269
|
+
{%- comment -%} Fade-up animation {%- endcomment -%}
|
|
270
|
+
|
|
271
|
+
</div>
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
<div class="heyiam-project glacier" data-render-version="2" data-template="glacier"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
|
|
2
|
+
|
|
3
|
+
{%- comment -%} Breadcrumb {%- endcomment -%}
|
|
4
|
+
<div class="container">
|
|
5
|
+
<nav class="breadcrumb" aria-label="Breadcrumb">
|
|
6
|
+
<a href="/{{ user.username }}">{{ user.username }}</a>
|
|
7
|
+
<span class="breadcrumb-sep">/</span>
|
|
8
|
+
<span aria-current="page">{{ project.slug }}</span>
|
|
9
|
+
</nav>
|
|
10
|
+
|
|
11
|
+
{%- comment -%} Header {%- endcomment -%}
|
|
12
|
+
<header class="project-header">
|
|
13
|
+
<h1 class="project-title" data-editable="title">{{ project.title }}</h1>
|
|
14
|
+
{% if project.repoUrl or project.projectUrl %}
|
|
15
|
+
<div class="project-links">
|
|
16
|
+
{% if project.repoUrl %}
|
|
17
|
+
<a href="{{ project.repoUrl }}" target="_blank" rel="noopener">{{ project.repoUrl | stripProtocol }}</a>
|
|
18
|
+
{% endif %}
|
|
19
|
+
{% if project.projectUrl %}
|
|
20
|
+
<a href="{{ project.projectUrl }}" target="_blank" rel="noopener">{{ project.projectUrl | stripProtocol }}</a>
|
|
21
|
+
{% endif %}
|
|
22
|
+
</div>
|
|
23
|
+
{% endif %}
|
|
24
|
+
</header>
|
|
25
|
+
|
|
26
|
+
{%- comment -%} Screenshot {%- endcomment -%}
|
|
27
|
+
{% if project.screenshotUrl %}
|
|
28
|
+
<div class="screenshot fade-up" role="img" aria-label="{{ project.title }} screenshot" data-editable="screenshot">
|
|
29
|
+
<div class="screenshot-chrome">
|
|
30
|
+
<span class="screenshot-dot"></span>
|
|
31
|
+
<span class="screenshot-dot"></span>
|
|
32
|
+
<span class="screenshot-dot"></span>
|
|
33
|
+
<span class="screenshot-url">Preview</span>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="screenshot-body">
|
|
36
|
+
<img src="{{ project.screenshotUrl }}" alt="{{ project.title }} screenshot" style="width: 100%; display: block;">
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
{% endif %}
|
|
40
|
+
|
|
41
|
+
{%- comment -%} Narrative {%- endcomment -%}
|
|
42
|
+
{% if project.narrative %}
|
|
43
|
+
<section class="narrative fade-up" aria-label="Project narrative">
|
|
44
|
+
<h2 class="section-heading">Narrative</h2>
|
|
45
|
+
<p>{{ project.narrative }}</p>
|
|
46
|
+
</section>
|
|
47
|
+
{% endif %}
|
|
48
|
+
|
|
49
|
+
{%- comment -%} Stats {%- endcomment -%}
|
|
50
|
+
<section class="fade-up" aria-label="Project statistics">
|
|
51
|
+
<div class="stats">
|
|
52
|
+
<div class="stat-card frost">
|
|
53
|
+
<span class="stat-value">{{ project.totalSessions }}</span>
|
|
54
|
+
<span class="stat-label">Sessions</span>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="stat-card frost">
|
|
57
|
+
<span class="stat-value">{{ project.totalLoc | localeNumber }}</span>
|
|
58
|
+
<span class="stat-label">LOC</span>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="stat-card frost">
|
|
61
|
+
<span class="stat-value">{{ project.totalFilesChanged }}</span>
|
|
62
|
+
<span class="stat-label">Files</span>
|
|
63
|
+
</div>
|
|
64
|
+
{% if project.totalTokens %}
|
|
65
|
+
<div class="stat-card frost">
|
|
66
|
+
<span class="stat-value">{{ project.totalTokens | formatTokens }}</span>
|
|
67
|
+
<span class="stat-label">Tokens</span>
|
|
68
|
+
</div>
|
|
69
|
+
{% endif %}
|
|
70
|
+
{% if efficiencyMultiplier %}
|
|
71
|
+
<div class="stat-card frost">
|
|
72
|
+
<span class="stat-value">{{ project.totalDurationMinutes | formatDuration }}</span>
|
|
73
|
+
<span class="stat-label">{{ durationLabel }}</span>
|
|
74
|
+
</div>
|
|
75
|
+
{% else %}
|
|
76
|
+
<div class="stat-card frost">
|
|
77
|
+
<span class="stat-value">{{ project.totalDurationMinutes | formatDuration }}</span>
|
|
78
|
+
<span class="stat-label">Time</span>
|
|
79
|
+
</div>
|
|
80
|
+
{% endif %}
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
{% if efficiencyMultiplier %}
|
|
84
|
+
{% assign totalCombined = project.totalDurationMinutes | plus: project.totalAgentDurationMinutes %}
|
|
85
|
+
{% if totalCombined > 0 %}
|
|
86
|
+
{% assign humanPct = project.totalDurationMinutes | times: 100 | divided_by: totalCombined %}
|
|
87
|
+
{% assign agentPct = 100 | minus: humanPct %}
|
|
88
|
+
<div class="gl-leverage frost" role="figure" aria-label="Leverage: {{ efficiencyMultiplier }} multiplier">
|
|
89
|
+
<svg width="64" height="64" viewBox="0 0 64 64">
|
|
90
|
+
<circle cx="32" cy="32" r="26" fill="none" stroke="rgba(56,189,248,0.12)" stroke-width="5"/>
|
|
91
|
+
<circle cx="32" cy="32" r="26" fill="none" stroke="var(--accent)" stroke-width="5" stroke-dasharray="{{ humanPct | times: 163 | divided_by: 100 }} 163" stroke-dashoffset="-33" stroke-linecap="round"/>
|
|
92
|
+
<text x="32" y="36" text-anchor="middle" font-family="var(--font-display)" font-size="14" font-weight="700" fill="var(--accent-dark)">{{ efficiencyMultiplier }}</text>
|
|
93
|
+
</svg>
|
|
94
|
+
<div class="gl-leverage__values">
|
|
95
|
+
<span class="gl-leverage__human">{{ project.totalDurationMinutes | formatDuration }} <small>you</small></span>
|
|
96
|
+
<span class="gl-leverage__agent">{{ project.totalAgentDurationMinutes | formatDuration }} <small>agents</small></span>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
{% endif %}
|
|
100
|
+
{% endif %}
|
|
101
|
+
</section>
|
|
102
|
+
|
|
103
|
+
{%- comment -%} Work timeline chart (CSS-only SVG bar chart) {%- endcomment -%}
|
|
104
|
+
{% if featuredSessions.size > 0 %}
|
|
105
|
+
<section class="chart-section fade-up" aria-label="Work timeline">
|
|
106
|
+
<h2 class="section-heading">Work Timeline</h2>
|
|
107
|
+
<div class="chart-container frost">
|
|
108
|
+
{% assign maxLoc = 1 %}
|
|
109
|
+
{% for s in featuredSessions %}
|
|
110
|
+
{% if s.locChanged > maxLoc %}{% assign maxLoc = s.locChanged %}{% endif %}
|
|
111
|
+
{% endfor %}
|
|
112
|
+
{% assign barCount = featuredSessions.size %}
|
|
113
|
+
{% assign barWidth = 80 %}
|
|
114
|
+
{% assign barGap = 100 %}
|
|
115
|
+
{% assign chartW = barCount | times: barGap | plus: 60 %}
|
|
116
|
+
{% assign chartH = 200 %}
|
|
117
|
+
<svg class="chart-svg" viewBox="0 0 {{ chartW }} {{ chartH }}" role="img" aria-label="Bar chart of sessions by lines of code">
|
|
118
|
+
<line x1="60" y1="20" x2="60" y2="160" class="chart-grid-line"/>
|
|
119
|
+
<line x1="60" y1="160" x2="{{ chartW | minus: 20 }}" y2="160" class="chart-grid-line"/>
|
|
120
|
+
<line x1="60" y1="100" x2="{{ chartW | minus: 20 }}" y2="100" class="chart-grid-line"/>
|
|
121
|
+
<line x1="60" y1="40" x2="{{ chartW | minus: 20 }}" y2="40" class="chart-grid-line"/>
|
|
122
|
+
<text x="50" y="164" class="chart-label" text-anchor="end">0</text>
|
|
123
|
+
{% assign halfMax = maxLoc | divided_by: 2 %}
|
|
124
|
+
<text x="50" y="104" class="chart-label" text-anchor="end">{{ halfMax }}</text>
|
|
125
|
+
<text x="50" y="44" class="chart-label" text-anchor="end">{{ maxLoc }}</text>
|
|
126
|
+
{% for s in featuredSessions %}
|
|
127
|
+
{% assign barX = forloop.index0 | times: barGap | plus: 80 %}
|
|
128
|
+
{% assign barH = s.locChanged | times: 140 | divided_by: maxLoc %}
|
|
129
|
+
{% assign barY = 160 | minus: barH %}
|
|
130
|
+
{% assign labelX = barX | plus: 40 %}
|
|
131
|
+
{% assign labelY = barY | minus: 7 %}
|
|
132
|
+
<rect x="{{ barX }}" y="{{ barY }}" width="{{ barWidth }}" height="{{ barH }}" class="chart-bar" aria-label="{{ s.title }}: {{ s.locChanged }} LOC"/>
|
|
133
|
+
<text x="{{ labelX }}" y="{{ labelY }}" class="chart-value" text-anchor="middle">{{ s.locChanged }}</text>
|
|
134
|
+
<text x="{{ labelX }}" y="180" class="chart-label" text-anchor="middle">{{ s.recordedAt | formatDateShort }}</text>
|
|
135
|
+
{% endfor %}
|
|
136
|
+
</svg>
|
|
137
|
+
</div>
|
|
138
|
+
</section>
|
|
139
|
+
{% endif %}
|
|
140
|
+
|
|
141
|
+
{%- comment -%} Phases {%- endcomment -%}
|
|
142
|
+
{% if arc.size > 0 %}
|
|
143
|
+
<section class="phases fade-up" aria-label="Project phases">
|
|
144
|
+
<h2 class="section-heading">Phases</h2>
|
|
145
|
+
<div class="phase-list">
|
|
146
|
+
{% for item in arc %}
|
|
147
|
+
<div class="phase-item frost">
|
|
148
|
+
<span class="phase-number">{{ item.phase }}</span>
|
|
149
|
+
<div class="phase-content">
|
|
150
|
+
<div class="phase-name">{{ item.title }}</div>
|
|
151
|
+
{% if item.dates %}
|
|
152
|
+
<div class="phase-dates">{{ item.dates }}</div>
|
|
153
|
+
{% endif %}
|
|
154
|
+
{% if item.description %}
|
|
155
|
+
<div class="phase-desc">{{ item.description }}</div>
|
|
156
|
+
{% endif %}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
{% endfor %}
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
{% endif %}
|
|
163
|
+
|
|
164
|
+
{%- comment -%} Skills {%- endcomment -%}
|
|
165
|
+
{% if project.skills.size > 0 %}
|
|
166
|
+
<section class="skills-section fade-up" aria-label="Technologies used">
|
|
167
|
+
<h2 class="section-heading">Skills</h2>
|
|
168
|
+
<div class="skills-grid">
|
|
169
|
+
{% for skill in project.skills %}
|
|
170
|
+
<span class="skill-chip">{{ skill }}</span>
|
|
171
|
+
{% endfor %}
|
|
172
|
+
</div>
|
|
173
|
+
</section>
|
|
174
|
+
{% endif %}
|
|
175
|
+
|
|
176
|
+
{%- comment -%} Key decisions {%- endcomment -%}
|
|
177
|
+
{% if arc.size > 0 %}
|
|
178
|
+
<section class="decisions fade-up" aria-label="Key decisions">
|
|
179
|
+
<h2 class="section-heading">Key Decisions</h2>
|
|
180
|
+
<div class="decision-list">
|
|
181
|
+
{% for item in arc %}
|
|
182
|
+
<div class="decision-item frost">
|
|
183
|
+
<span class="decision-number">{{ item.phase }}</span>
|
|
184
|
+
<p class="decision-text">{{ item.description }}</p>
|
|
185
|
+
</div>
|
|
186
|
+
{% endfor %}
|
|
187
|
+
</div>
|
|
188
|
+
</section>
|
|
189
|
+
{% endif %}
|
|
190
|
+
|
|
191
|
+
{%- comment -%} Source breakdown {%- endcomment -%}
|
|
192
|
+
{% if sourceCounts.size > 0 %}
|
|
193
|
+
<section class="source-section fade-up" aria-label="Source breakdown">
|
|
194
|
+
<h2 class="section-heading">Source Breakdown</h2>
|
|
195
|
+
<div class="source-card frost">
|
|
196
|
+
{% assign totalSourceSessions = 0 %}
|
|
197
|
+
{% for src in sourceCounts %}
|
|
198
|
+
{% assign totalSourceSessions = totalSourceSessions | plus: src.count %}
|
|
199
|
+
{% endfor %}
|
|
200
|
+
{% if totalSourceSessions > 0 %}
|
|
201
|
+
{% assign firstPct = sourceCounts[0].count | times: 100.0 | divided_by: totalSourceSessions | round %}
|
|
202
|
+
<div class="source-bar" role="img" aria-label="Source mix">
|
|
203
|
+
<div class="source-bar-fill" style="width: {{ firstPct }}%"></div>
|
|
204
|
+
</div>
|
|
205
|
+
<div class="source-legend">
|
|
206
|
+
{% for src in sourceCounts %}
|
|
207
|
+
{% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
|
|
208
|
+
<span><span class="source-legend-dot" style="background: {% if forloop.first %}var(--accent){% else %}#e2e8f0{% endif %}"></span>{{ src.tool }} {{ srcPct }}% ({{ src.count }} session{% if src.count != 1 %}s{% endif %})</span>
|
|
209
|
+
{% endfor %}
|
|
210
|
+
</div>
|
|
211
|
+
{% endif %}
|
|
212
|
+
</div>
|
|
213
|
+
</section>
|
|
214
|
+
{% endif %}
|
|
215
|
+
|
|
216
|
+
{%- comment -%} Sessions table {%- endcomment -%}
|
|
217
|
+
{% if featuredSessions.size > 0 %}
|
|
218
|
+
<section class="sessions-section fade-up" aria-label="All sessions">
|
|
219
|
+
<h2 class="section-heading">Sessions</h2>
|
|
220
|
+
<div class="sessions-table-wrap frost">
|
|
221
|
+
<table class="sessions-table">
|
|
222
|
+
<thead>
|
|
223
|
+
<tr>
|
|
224
|
+
<th scope="col">#</th>
|
|
225
|
+
<th scope="col">Title</th>
|
|
226
|
+
<th scope="col">Date</th>
|
|
227
|
+
<th scope="col">Duration</th>
|
|
228
|
+
<th scope="col">LOC</th>
|
|
229
|
+
<th scope="col">Source</th>
|
|
230
|
+
<th scope="col">Agents</th>
|
|
231
|
+
</tr>
|
|
232
|
+
</thead>
|
|
233
|
+
<tbody>
|
|
234
|
+
{% for s in featuredSessions %}
|
|
235
|
+
<tr>
|
|
236
|
+
<td>{{ forloop.index }}</td>
|
|
237
|
+
<td><a href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}" class="session-title-link">{{ s.title }}</a></td>
|
|
238
|
+
<td>{{ s.recordedAt | formatDateShort }}</td>
|
|
239
|
+
<td>{{ s.durationMinutes | formatDuration }}</td>
|
|
240
|
+
<td>{{ s.locChanged | localeNumber }}</td>
|
|
241
|
+
<td>{% if s.sourceTool %}<span class="session-source-badge">{{ s.sourceTool }}</span>{% endif %}</td>
|
|
242
|
+
<td>
|
|
243
|
+
{% if s.agentSummary %}
|
|
244
|
+
<div class="agent-dots" aria-label="{{ s.agentSummary.agents.size }} agents">
|
|
245
|
+
{% for agent in s.agentSummary.agents %}
|
|
246
|
+
<span class="agent-dot" style="background:{{ agent.color }}" title="{{ agent.role }}"></span>
|
|
247
|
+
{% endfor %}
|
|
248
|
+
</div>
|
|
249
|
+
{% else %}
|
|
250
|
+
<span style="font-family:var(--font-mono);font-size:12px;color:var(--fg-muted)">--</span>
|
|
251
|
+
{% endif %}
|
|
252
|
+
</td>
|
|
253
|
+
</tr>
|
|
254
|
+
{% endfor %}
|
|
255
|
+
</tbody>
|
|
256
|
+
</table>
|
|
257
|
+
</div>
|
|
258
|
+
</section>
|
|
259
|
+
{% endif %}
|
|
260
|
+
|
|
261
|
+
{%- comment -%} Featured sessions {%- endcomment -%}
|
|
262
|
+
{% if featuredSessions.size > 0 %}
|
|
263
|
+
<section class="featured-section fade-up" aria-label="Featured sessions">
|
|
264
|
+
<h2 class="section-heading">Featured Sessions</h2>
|
|
265
|
+
<div class="featured-grid">
|
|
266
|
+
{% for s in featuredSessions %}
|
|
267
|
+
<a href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}" class="featured-card frost">
|
|
268
|
+
{% if s.skills.size > 0 %}
|
|
269
|
+
<span class="featured-tag">{{ s.skills | first }}</span>
|
|
270
|
+
{% endif %}
|
|
271
|
+
<span class="featured-name">{{ s.title }}</span>
|
|
272
|
+
<div class="featured-stats">
|
|
273
|
+
<span>{{ s.durationMinutes | formatDuration }}</span>
|
|
274
|
+
<span>{{ s.locChanged | localeNumber }} LOC</span>
|
|
275
|
+
{% if s.agentSummary %}
|
|
276
|
+
<span>{{ s.agentSummary.agents.size }} agents</span>
|
|
277
|
+
{% endif %}
|
|
278
|
+
</div>
|
|
279
|
+
</a>
|
|
280
|
+
{% endfor %}
|
|
281
|
+
</div>
|
|
282
|
+
</section>
|
|
283
|
+
{% endif %}
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
{%- comment -%} Fade-up animation {%- endcomment -%}
|
|
287
|
+
|
|
288
|
+
</div>
|