heyiam 0.2.29 → 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.
Files changed (177) hide show
  1. package/README.md +45 -0
  2. package/dist/config.js +10 -1
  3. package/dist/db.js +1 -2
  4. package/dist/export.js +40 -25
  5. package/dist/format-utils.js +5 -0
  6. package/dist/index.js +168 -0
  7. package/dist/mount.js +300 -102
  8. package/dist/parsers/claude.js +2 -28
  9. package/dist/parsers/codex.js +2 -26
  10. package/dist/parsers/cursor.js +2 -26
  11. package/dist/parsers/duration.js +35 -0
  12. package/dist/parsers/gemini.js +2 -20
  13. package/dist/parsers/types.js +0 -1
  14. package/dist/public/assets/index-BZ65TU_Y.js +40 -0
  15. package/dist/public/assets/index-CqCaW2cb.css +1 -0
  16. package/dist/public/index.html +2 -2
  17. package/dist/redact.js +4 -104
  18. package/dist/render/build-render-data.js +9 -2
  19. package/dist/render/index.js +32 -5
  20. package/dist/render/liquid.js +147 -7
  21. package/dist/render/mock-data.js +303 -0
  22. package/dist/render/templates/aurora/portfolio.liquid +204 -0
  23. package/dist/render/templates/aurora/project.liquid +260 -0
  24. package/dist/render/templates/aurora/session.liquid +223 -0
  25. package/dist/render/templates/aurora/styles.css +1178 -0
  26. package/dist/render/templates/bauhaus/portfolio.liquid +179 -0
  27. package/dist/render/templates/bauhaus/project.liquid +300 -0
  28. package/dist/render/templates/bauhaus/session.liquid +333 -0
  29. package/dist/render/templates/bauhaus/styles.css +1641 -0
  30. package/dist/render/templates/blueprint/portfolio.liquid +167 -0
  31. package/dist/render/templates/blueprint/project.liquid +286 -0
  32. package/dist/render/templates/blueprint/session.liquid +248 -0
  33. package/dist/render/templates/blueprint/styles.css +1285 -0
  34. package/dist/render/templates/canvas/portfolio.liquid +215 -0
  35. package/dist/render/templates/canvas/project.liquid +235 -0
  36. package/dist/render/templates/canvas/session.liquid +223 -0
  37. package/dist/render/templates/canvas/styles.css +1436 -0
  38. package/dist/render/templates/carbon/portfolio.liquid +170 -0
  39. package/dist/render/templates/carbon/project.liquid +249 -0
  40. package/dist/render/templates/carbon/session.liquid +190 -0
  41. package/dist/render/templates/carbon/styles.css +1091 -0
  42. package/dist/render/templates/chalk/portfolio.liquid +199 -0
  43. package/dist/render/templates/chalk/project.liquid +245 -0
  44. package/dist/render/templates/chalk/session.liquid +215 -0
  45. package/dist/render/templates/chalk/styles.css +1157 -0
  46. package/dist/render/templates/circuit/portfolio.liquid +162 -0
  47. package/dist/render/templates/circuit/project.liquid +247 -0
  48. package/dist/render/templates/circuit/session.liquid +205 -0
  49. package/dist/render/templates/circuit/styles.css +1403 -0
  50. package/dist/render/templates/cosmos/portfolio.liquid +232 -0
  51. package/dist/render/templates/cosmos/project.liquid +327 -0
  52. package/dist/render/templates/cosmos/session.liquid +239 -0
  53. package/dist/render/templates/cosmos/styles.css +1151 -0
  54. package/dist/render/templates/daylight/portfolio.liquid +217 -0
  55. package/dist/render/templates/daylight/project.liquid +229 -0
  56. package/dist/render/templates/daylight/session.liquid +219 -0
  57. package/dist/render/templates/daylight/styles.css +1311 -0
  58. package/dist/render/templates/editorial/portfolio.liquid +126 -0
  59. package/dist/render/templates/editorial/project.liquid +202 -0
  60. package/dist/render/templates/editorial/session.liquid +171 -0
  61. package/dist/render/templates/editorial/styles.css +822 -0
  62. package/dist/render/templates/ember/portfolio.liquid +318 -0
  63. package/dist/render/templates/ember/project.liquid +232 -0
  64. package/dist/render/templates/ember/session.liquid +202 -0
  65. package/dist/render/templates/ember/styles.css +1283 -0
  66. package/dist/render/templates/glacier/portfolio.liquid +271 -0
  67. package/dist/render/templates/glacier/project.liquid +288 -0
  68. package/dist/render/templates/glacier/session.liquid +217 -0
  69. package/dist/render/templates/glacier/styles.css +1200 -0
  70. package/dist/render/templates/grid/portfolio.liquid +265 -0
  71. package/dist/render/templates/grid/project.liquid +306 -0
  72. package/dist/render/templates/grid/session.liquid +260 -0
  73. package/dist/render/templates/grid/styles.css +1441 -0
  74. package/dist/render/templates/kinetic/portfolio.liquid +170 -0
  75. package/dist/render/templates/kinetic/project.liquid +242 -0
  76. package/dist/render/templates/kinetic/session.liquid +228 -0
  77. package/dist/render/templates/kinetic/styles.css +944 -0
  78. package/dist/render/templates/meridian/portfolio.liquid +255 -0
  79. package/dist/render/templates/meridian/project.liquid +376 -0
  80. package/dist/render/templates/meridian/session.liquid +298 -0
  81. package/dist/render/templates/meridian/styles.css +1369 -0
  82. package/dist/render/templates/minimal/portfolio.liquid +71 -0
  83. package/dist/render/templates/minimal/project.liquid +154 -0
  84. package/dist/render/templates/minimal/session.liquid +140 -0
  85. package/dist/render/templates/minimal/styles.css +525 -0
  86. package/dist/render/templates/mono/portfolio.liquid +291 -0
  87. package/dist/render/templates/mono/project.liquid +275 -0
  88. package/dist/render/templates/mono/session.liquid +276 -0
  89. package/dist/render/templates/mono/styles.css +1016 -0
  90. package/dist/render/templates/neon/portfolio.liquid +217 -0
  91. package/dist/render/templates/neon/project.liquid +225 -0
  92. package/dist/render/templates/neon/session.liquid +195 -0
  93. package/dist/render/templates/neon/styles.css +1265 -0
  94. package/dist/render/templates/noir/portfolio.liquid +137 -0
  95. package/dist/render/templates/noir/project.liquid +220 -0
  96. package/dist/render/templates/noir/session.liquid +241 -0
  97. package/dist/render/templates/noir/styles.css +1223 -0
  98. package/dist/render/templates/obsidian/portfolio.liquid +257 -0
  99. package/dist/render/templates/obsidian/project.liquid +280 -0
  100. package/dist/render/templates/obsidian/session.liquid +241 -0
  101. package/dist/render/templates/obsidian/styles.css +1401 -0
  102. package/dist/render/templates/paper/portfolio.liquid +267 -0
  103. package/dist/render/templates/paper/project.liquid +235 -0
  104. package/dist/render/templates/paper/session.liquid +271 -0
  105. package/dist/render/templates/paper/styles.css +1509 -0
  106. package/dist/render/templates/parallax/portfolio.liquid +305 -0
  107. package/dist/render/templates/parallax/project.liquid +275 -0
  108. package/dist/render/templates/parallax/session.liquid +295 -0
  109. package/dist/render/templates/parallax/styles.css +1874 -0
  110. package/dist/render/templates/parchment/portfolio.liquid +290 -0
  111. package/dist/render/templates/parchment/project.liquid +289 -0
  112. package/dist/render/templates/parchment/session.liquid +346 -0
  113. package/dist/render/templates/parchment/styles.css +1397 -0
  114. package/dist/render/templates/partials/_beats.liquid +16 -0
  115. package/dist/render/templates/partials/_breadcrumb.liquid +9 -0
  116. package/dist/render/templates/partials/_footer.liquid +7 -0
  117. package/dist/render/templates/partials/_growth-chart.liquid +7 -0
  118. package/dist/render/templates/partials/_key-decisions.liquid +20 -0
  119. package/dist/render/templates/partials/_links.liquid +16 -0
  120. package/dist/render/templates/partials/_narrative.liquid +8 -0
  121. package/dist/render/templates/partials/_phases.liquid +20 -0
  122. package/dist/render/templates/partials/_portfolio-header.liquid +20 -0
  123. package/dist/render/templates/partials/_portfolio-projects.liquid +16 -0
  124. package/dist/render/templates/partials/_portfolio-stats.liquid +19 -0
  125. package/dist/render/templates/partials/_qa.liquid +13 -0
  126. package/dist/render/templates/partials/_screenshot.liquid +15 -0
  127. package/dist/render/templates/partials/_session-cards.liquid +30 -0
  128. package/dist/render/templates/partials/_session-header.liquid +39 -0
  129. package/dist/render/templates/partials/_session-sidebar.liquid +30 -0
  130. package/dist/render/templates/partials/_skills.liquid +12 -0
  131. package/dist/render/templates/partials/_source-breakdown.liquid +22 -0
  132. package/dist/render/templates/partials/_stats.liquid +38 -0
  133. package/dist/render/templates/partials/_work-timeline.liquid +7 -0
  134. package/dist/render/templates/project.liquid +7 -4
  135. package/dist/render/templates/radar/portfolio.liquid +233 -0
  136. package/dist/render/templates/radar/project.liquid +278 -0
  137. package/dist/render/templates/radar/session.liquid +300 -0
  138. package/dist/render/templates/radar/styles.css +1049 -0
  139. package/dist/render/templates/showcase/portfolio.liquid +231 -0
  140. package/dist/render/templates/showcase/project.liquid +237 -0
  141. package/dist/render/templates/showcase/session.liquid +210 -0
  142. package/dist/render/templates/showcase/styles.css +1279 -0
  143. package/dist/render/templates/signal/portfolio.liquid +227 -0
  144. package/dist/render/templates/signal/project.liquid +278 -0
  145. package/dist/render/templates/signal/session.liquid +282 -0
  146. package/dist/render/templates/signal/styles.css +1395 -0
  147. package/dist/render/templates/strata/portfolio.liquid +192 -0
  148. package/dist/render/templates/strata/project.liquid +282 -0
  149. package/dist/render/templates/strata/session.liquid +261 -0
  150. package/dist/render/templates/strata/styles.css +1350 -0
  151. package/dist/render/templates/styles.css +1190 -0
  152. package/dist/render/templates/terminal/portfolio.liquid +118 -0
  153. package/dist/render/templates/terminal/project.liquid +161 -0
  154. package/dist/render/templates/terminal/session.liquid +145 -0
  155. package/dist/render/templates/terminal/styles.css +492 -0
  156. package/dist/render/templates/verdant/portfolio.liquid +333 -0
  157. package/dist/render/templates/verdant/project.liquid +309 -0
  158. package/dist/render/templates/verdant/session.liquid +237 -0
  159. package/dist/render/templates/verdant/styles.css +1257 -0
  160. package/dist/render/templates/zen/portfolio.liquid +136 -0
  161. package/dist/render/templates/zen/project.liquid +187 -0
  162. package/dist/render/templates/zen/session.liquid +203 -0
  163. package/dist/render/templates/zen/styles.css +1207 -0
  164. package/dist/render/templates.js +90 -0
  165. package/dist/routes/context.js +15 -10
  166. package/dist/routes/enhance.js +17 -40
  167. package/dist/routes/export.js +14 -4
  168. package/dist/routes/preview.js +480 -108
  169. package/dist/routes/projects.js +11 -19
  170. package/dist/routes/publish.js +15 -17
  171. package/dist/routes/settings.js +94 -1
  172. package/dist/routes/sse.js +9 -0
  173. package/dist/server.js +8 -2
  174. package/dist/settings.js +17 -9
  175. package/package.json +2 -4
  176. package/dist/public/assets/index-CC9G8EF1.js +0 -21
  177. package/dist/public/assets/index-Dalqz2mC.css +0 -1
@@ -0,0 +1,179 @@
1
+ <div class="heyiam-portfolio bauhaus" data-render-version="2" data-template="bauhaus" data-username="{{ user.username }}">
2
+
3
+ {%- comment -%} Hero {%- endcomment -%}
4
+ {% if hasProfile %}
5
+ <section class="hero" aria-label="Introduction">
6
+ <div class="hero-grid">
7
+ <div class="hero-content anim-slide-left">
8
+ {% if user.photoUrl %}
9
+ <img src="{{ user.photoUrl }}" alt="{{ user.displayName }}" class="hero-photo" width="120" height="120">
10
+ {% endif %}
11
+ {% if user.displayName != blank %}
12
+ <h1>{{ user.displayName }}<span class="accent-dot">.</span></h1>
13
+ {% endif %}
14
+ {% if user.bio %}
15
+ <p class="hero-bio">{{ user.bio }}</p>
16
+ {% endif %}
17
+ {% if user.location %}
18
+ <p class="hero-location">{{ user.location }}</p>
19
+ {% endif %}
20
+ <div class="profile-links">
21
+ {% if user.email %}
22
+ <a href="mailto:{{ user.email }}" aria-label="Email {{ user.email }}">
23
+ <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>
24
+ {{ user.email }}
25
+ </a>
26
+ {% endif %}
27
+ {% if user.linkedinUrl %}
28
+ <a href="{{ user.linkedinUrl }}" target="_blank" rel="noopener" aria-label="LinkedIn profile">
29
+ <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>
30
+ LinkedIn
31
+ </a>
32
+ {% endif %}
33
+ {% if user.githubUrl %}
34
+ <a href="{{ user.githubUrl }}" target="_blank" rel="noopener" aria-label="GitHub profile">
35
+ <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>
36
+ GitHub
37
+ </a>
38
+ {% endif %}
39
+ {% if user.twitterHandle %}
40
+ <a href="https://x.com/{{ user.twitterHandle }}" target="_blank" rel="noopener" aria-label="Twitter/X profile">
41
+ <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>
42
+ @{{ user.twitterHandle }}
43
+ </a>
44
+ {% endif %}
45
+ {% if user.websiteUrl %}
46
+ <a href="{{ user.websiteUrl }}" target="_blank" rel="noopener" aria-label="Personal website">
47
+ {{ user.websiteUrl | stripProtocol }}
48
+ </a>
49
+ {% endif %}
50
+ {% if user.resumeUrl %}
51
+ <a href="{{ user.resumeUrl }}" aria-label="Download resume as PDF">
52
+ <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>
53
+ Resume (PDF)
54
+ </a>
55
+ {% endif %}
56
+ </div>
57
+ </div>
58
+ <div class="hero-shapes anim-scale-in anim-delay-2" aria-hidden="true">
59
+ <div class="shape-circle"></div>
60
+ <div class="shape-rect-blue"></div>
61
+ <div class="shape-rect-yellow"></div>
62
+ <div class="shape-line"></div>
63
+ </div>
64
+ </div>
65
+ </section>
66
+ {% endif %}
67
+
68
+ {%- comment -%} Stats bar {%- endcomment -%}
69
+ <section class="stats-bar anim-fade-up anim-delay-2" aria-label="Aggregate statistics">
70
+ <div class="stat-item">
71
+ <div class="stat-number">{{ projects.size }}</div>
72
+ <div class="stat-label">Projects</div>
73
+ </div>
74
+ <div class="stat-item">
75
+ <div class="stat-number">{{ totalSessions }}</div>
76
+ <div class="stat-label">Sessions</div>
77
+ </div>
78
+ {% if efficiencyMultiplier %}
79
+ <div class="stat-item" style="background: var(--yellow); color: var(--black);">
80
+ <div class="stat-number">{{ efficiencyMultiplier }}</div>
81
+ <div class="stat-label">Multiplier</div>
82
+ </div>
83
+ <div class="stat-item" style="background: var(--red); color: #fff;">
84
+ <div class="stat-number">{{ totalDurationMinutes | formatDuration }}</div>
85
+ <div class="stat-label" style="color: rgba(255,255,255,0.8);">You</div>
86
+ </div>
87
+ <div class="stat-item" style="background: var(--blue); color: #fff;">
88
+ <div class="stat-number">{{ totalAgentDurationMinutes | formatDuration }}</div>
89
+ <div class="stat-label" style="color: rgba(255,255,255,0.8);">Agents</div>
90
+ </div>
91
+ {% else %}
92
+ <div class="stat-item">
93
+ <div class="stat-number">{{ totalDurationMinutes | formatDuration }}</div>
94
+ <div class="stat-label">{{ durationLabel }}</div>
95
+ </div>
96
+ {% endif %}
97
+ <div class="stat-item">
98
+ <div class="stat-number">{{ totalLoc | formatLoc }}</div>
99
+ <div class="stat-label">Lines Changed</div>
100
+ </div>
101
+ </section>
102
+
103
+ {%- comment -%} Projects {%- endcomment -%}
104
+ {% if projects.size > 0 %}
105
+ <section class="projects-section" aria-label="Projects">
106
+ <div class="section-heading anim-slide-right anim-delay-3">
107
+ <svg class="section-heading-shape" width="24" height="24" aria-hidden="true">
108
+ <circle cx="12" cy="12" r="12" fill="var(--red)" />
109
+ </svg>
110
+ <h2>Projects</h2>
111
+ <div class="heading-line" aria-hidden="true"></div>
112
+ </div>
113
+
114
+ <div class="projects-grid">
115
+ {% for p in projects %}
116
+ <a href="/{{ user.username }}/{{ p.slug }}" class="project-card anim-fade-up anim-delay-3">
117
+ <div class="project-card-inner">
118
+ <div class="project-accent" aria-hidden="true"></div>
119
+ <div class="project-body">
120
+ <div class="project-header">
121
+ <h3 class="project-title">{{ p.title }}</h3>
122
+ </div>
123
+ {% if p.narrative %}
124
+ <p class="project-narrative">{{ p.narrative }}</p>
125
+ {% endif %}
126
+ {% if p.skills.size > 0 %}
127
+ <div class="project-skills" aria-label="Skills used">
128
+ {% for skill in p.skills %}
129
+ <span class="skill-chip">{{ skill }}</span>
130
+ {% endfor %}
131
+ </div>
132
+ {% endif %}
133
+ <div class="project-stats">
134
+ <span><strong>{{ p.totalSessions }}</strong> sessions</span>
135
+ <span><strong>{{ p.totalDurationMinutes | formatDuration }}</strong></span>
136
+ <span><strong>{{ p.totalLoc | formatLoc }}</strong> LOC</span>
137
+ </div>
138
+ {% if p.sourceCounts.size > 0 %}
139
+ {% assign pTotalSrc = 0 %}
140
+ {% for src in p.sourceCounts %}
141
+ {% assign pTotalSrc = pTotalSrc | plus: src.count %}
142
+ {% endfor %}
143
+ {% if pTotalSrc > 0 %}
144
+ <div class="project-source-bar" aria-label="Source distribution">
145
+ {% for src in p.sourceCounts %}
146
+ {% assign pSrcPct = src.count | times: 100.0 | divided_by: pTotalSrc | round %}
147
+ <div class="source-segment-{{ src.tool | downcase | replace: ' code', '' | replace: ' ', '-' }}" style="width: {{ pSrcPct }}%"></div>
148
+ {% endfor %}
149
+ </div>
150
+ <div class="project-source-legend">
151
+ {% for src in p.sourceCounts %}
152
+ {% assign pSrcPct = src.count | times: 100.0 | divided_by: pTotalSrc | round %}
153
+ <span><span class="legend-dot {{ src.tool | downcase | replace: ' code', '' | replace: ' ', '-' }}" aria-hidden="true"></span>{{ src.tool }} {{ pSrcPct }}%</span>
154
+ {% endfor %}
155
+ </div>
156
+ {% endif %}
157
+ {% endif %}
158
+ </div>
159
+ </div>
160
+ </a>
161
+ {% endfor %}
162
+ </div>
163
+ </section>
164
+ {% endif %}
165
+
166
+ {%- comment -%} Footer {%- endcomment -%}
167
+ <footer class="bauhaus-footer">
168
+ <div class="footer-inner">
169
+ <span class="footer-text">Built with heyi.am</span>
170
+ <div class="footer-shapes" aria-hidden="true">
171
+ <span class="footer-shape footer-sq-red"></span>
172
+ <span class="footer-shape footer-circle-blue"></span>
173
+ <span class="footer-shape footer-sq-yellow"></span>
174
+ </div>
175
+ </div>
176
+ </footer>
177
+
178
+
179
+ </div>
@@ -0,0 +1,300 @@
1
+ <div class="heyiam-project bauhaus" data-render-version="2" data-template="bauhaus"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
+
3
+ {%- comment -%} Breadcrumb {%- endcomment -%}
4
+ <nav class="breadcrumb" aria-label="Breadcrumb">
5
+ <a href="/{{ user.username }}">{{ user.username }}</a>
6
+ <span aria-hidden="true">/</span>
7
+ <strong>{{ project.slug }}</strong>
8
+ </nav>
9
+
10
+ {%- comment -%} Project hero {%- endcomment -%}
11
+ <section class="project-hero" aria-label="Project overview">
12
+ <div class="project-hero-content anim-slide-left">
13
+ <h1 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
+ </div>
25
+ <div class="project-hero-shapes anim-scale-in anim-delay-2" aria-hidden="true">
26
+ <div class="hero-shape-circle"></div>
27
+ <div class="hero-shape-rect"></div>
28
+ <div class="hero-shape-line-h"></div>
29
+ <div class="hero-shape-small-sq"></div>
30
+ </div>
31
+ </section>
32
+
33
+ {%- comment -%} Stats grid {%- endcomment -%}
34
+ <section class="stats-grid anim-fade-up anim-delay-1" aria-label="Project statistics">
35
+ <div class="stat-cell">
36
+ <div class="stat-cell-number">{{ project.totalSessions }}</div>
37
+ <div class="stat-cell-label">Sessions</div>
38
+ </div>
39
+ {% if efficiencyMultiplier %}
40
+ <div class="stat-cell" style="background: var(--yellow); color: var(--black);">
41
+ <div class="stat-cell-number">{{ efficiencyMultiplier }}</div>
42
+ <div class="stat-cell-label">Multiplier</div>
43
+ </div>
44
+ <div class="stat-cell" style="background: var(--red); color: #fff;">
45
+ <div class="stat-cell-number">{{ project.totalDurationMinutes | formatDuration }}</div>
46
+ <div class="stat-cell-label">You</div>
47
+ </div>
48
+ <div class="stat-cell" style="background: var(--blue); color: #fff;">
49
+ <div class="stat-cell-number">{{ project.totalAgentDurationMinutes | formatDuration }}</div>
50
+ <div class="stat-cell-label">Agents</div>
51
+ </div>
52
+ {% else %}
53
+ <div class="stat-cell">
54
+ <div class="stat-cell-number">{{ project.totalDurationMinutes | formatDuration }}</div>
55
+ <div class="stat-cell-label">{{ durationLabel }}</div>
56
+ </div>
57
+ {% endif %}
58
+ <div class="stat-cell">
59
+ <div class="stat-cell-number">{{ project.totalLoc | formatLoc }}</div>
60
+ <div class="stat-cell-label">LOC</div>
61
+ </div>
62
+ <div class="stat-cell">
63
+ <div class="stat-cell-number">{{ project.totalFilesChanged | localeNumber }}</div>
64
+ <div class="stat-cell-label">Files</div>
65
+ </div>
66
+ {% if project.totalTokens %}
67
+ <div class="stat-cell">
68
+ <div class="stat-cell-number">{{ project.totalTokens | formatTokens }}</div>
69
+ <div class="stat-cell-label">Tokens</div>
70
+ </div>
71
+ {% endif %}
72
+ </section>
73
+
74
+ {%- comment -%} Screenshot {%- endcomment -%}
75
+ {% if project.screenshotUrl %}
76
+ <section class="screenshot-section anim-slide-right anim-delay-2" aria-label="Project screenshot">
77
+ <div class="screenshot-frame">
78
+ <div class="screenshot-chrome" aria-hidden="true">
79
+ <span class="chrome-dot"></span>
80
+ <span class="chrome-dot"></span>
81
+ <span class="chrome-dot"></span>
82
+ </div>
83
+ <div class="screenshot-body">
84
+ <img src="{{ project.screenshotUrl }}" alt="{{ project.title }} screenshot" style="width:100%; height:100%; object-fit:cover;">
85
+ </div>
86
+ </div>
87
+ </section>
88
+ {% endif %}
89
+
90
+ {%- comment -%} Narrative {%- endcomment -%}
91
+ {% if project.narrative %}
92
+ <section class="narrative-section anim-slide-left anim-delay-2" aria-label="Project narrative">
93
+ <div class="section-heading">
94
+ <svg width="20" height="20" aria-hidden="true">
95
+ <rect width="20" height="20" fill="var(--red)" />
96
+ </svg>
97
+ <h2>Narrative</h2>
98
+ <div class="heading-line" aria-hidden="true"></div>
99
+ </div>
100
+ <div class="narrative-block">
101
+ {{ project.narrative }}
102
+ </div>
103
+ </section>
104
+ {% endif %}
105
+
106
+ {%- comment -%} Key decisions {%- endcomment -%}
107
+ {% if arc.size > 0 %}
108
+ <section class="decisions-section anim-slide-left anim-delay-3" aria-label="Key decisions">
109
+ <div class="section-heading">
110
+ <svg width="20" height="20" aria-hidden="true">
111
+ <rect width="20" height="20" fill="var(--yellow)" />
112
+ </svg>
113
+ <h2>Key Decisions</h2>
114
+ <div class="heading-line" aria-hidden="true"></div>
115
+ </div>
116
+ <ol class="decisions-list">
117
+ {% for item in arc %}
118
+ <li class="decision-item">
119
+ <div class="decision-number" aria-hidden="true">{{ forloop.index }}</div>
120
+ <div class="decision-text">{{ item.description }}</div>
121
+ </li>
122
+ {% endfor %}
123
+ </ol>
124
+ </section>
125
+ {% endif %}
126
+
127
+ {%- comment -%} Phases {%- endcomment -%}
128
+ {% if arc.size > 0 %}
129
+ <section class="phases-section anim-slide-right anim-delay-3" aria-label="Project phases">
130
+ <div class="section-heading">
131
+ <svg width="20" height="20" aria-hidden="true">
132
+ <circle cx="10" cy="10" r="10" fill="var(--blue)" />
133
+ </svg>
134
+ <h2>Phases</h2>
135
+ <div class="heading-line" aria-hidden="true"></div>
136
+ </div>
137
+ <div class="phases-grid">
138
+ {% for item in arc %}
139
+ <div class="phase-row">
140
+ <div class="phase-number">{{ item.phase }}</div>
141
+ <div class="phase-content">
142
+ <div class="phase-title">{{ item.title }}</div>
143
+ {% if item.dates %}
144
+ <div class="phase-dates">{{ item.dates }}</div>
145
+ {% endif %}
146
+ <div class="phase-desc">{{ item.description }}</div>
147
+ </div>
148
+ </div>
149
+ {% endfor %}
150
+ </div>
151
+ </section>
152
+ {% endif %}
153
+
154
+ {%- comment -%} Skills {%- endcomment -%}
155
+ {% if project.skills.size > 0 %}
156
+ <section class="skills-section anim-slide-right anim-delay-3" aria-label="Skills used">
157
+ <div class="section-heading">
158
+ <svg width="20" height="20" aria-hidden="true">
159
+ <circle cx="10" cy="10" r="10" fill="var(--red)" />
160
+ </svg>
161
+ <h2>Skills</h2>
162
+ <div class="heading-line" aria-hidden="true"></div>
163
+ </div>
164
+ <div class="skills-grid">
165
+ {% for skill in project.skills %}
166
+ <span class="skill-tag">{{ skill }}</span>
167
+ {% endfor %}
168
+ </div>
169
+ </section>
170
+ {% endif %}
171
+
172
+ {%- comment -%} Source breakdown {%- endcomment -%}
173
+ {% if sourceCounts.size > 0 %}
174
+ <section class="source-section anim-slide-left anim-delay-4" aria-label="Source breakdown">
175
+ <div class="section-heading">
176
+ <svg width="20" height="20" aria-hidden="true">
177
+ <rect width="20" height="20" fill="var(--blue)" />
178
+ </svg>
179
+ <h2>Source Breakdown</h2>
180
+ <div class="heading-line" aria-hidden="true"></div>
181
+ </div>
182
+ {% assign totalSourceSessions = 0 %}
183
+ {% for src in sourceCounts %}
184
+ {% assign totalSourceSessions = totalSourceSessions | plus: src.count %}
185
+ {% endfor %}
186
+ {% if totalSourceSessions > 0 %}
187
+ <div class="source-bar-wrapper">
188
+ <div class="source-bar">
189
+ {% for src in sourceCounts %}
190
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
191
+ <div class="source-segment" style="width: {{ srcPct }}%; background: var({% if forloop.first %}--blue{% else %}--yellow{% endif %});"></div>
192
+ {% endfor %}
193
+ </div>
194
+ </div>
195
+ <div class="source-legend">
196
+ {% for src in sourceCounts %}
197
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
198
+ <span><span class="legend-swatch" style="background: var({% if forloop.first %}--blue{% else %}--yellow{% endif %});" aria-hidden="true"></span>{{ src.tool }} {{ srcPct }}% ({{ src.count }} session{% if src.count != 1 %}s{% endif %})</span>
199
+ {% endfor %}
200
+ </div>
201
+ {% endif %}
202
+ </section>
203
+ {% endif %}
204
+
205
+ {%- comment -%} Work timeline (sessions table) {%- endcomment -%}
206
+ {% if featuredSessions.size > 0 %}
207
+ <section class="sessions-section anim-slide-right anim-delay-4" aria-label="Work timeline">
208
+ <div class="section-heading">
209
+ <svg width="20" height="20" aria-hidden="true">
210
+ <rect width="20" height="20" fill="var(--yellow)" />
211
+ </svg>
212
+ <h2>Work Timeline</h2>
213
+ <div class="heading-line" aria-hidden="true"></div>
214
+ </div>
215
+ <div class="sessions-table-wrapper">
216
+ <table class="sessions-table">
217
+ <thead>
218
+ <tr>
219
+ <th scope="col">#</th>
220
+ <th scope="col">Session</th>
221
+ <th scope="col">Date</th>
222
+ <th scope="col">Duration</th>
223
+ <th scope="col">LOC</th>
224
+ <th scope="col">Source</th>
225
+ <th scope="col">Agents</th>
226
+ </tr>
227
+ </thead>
228
+ <tbody>
229
+ {% for s in featuredSessions %}
230
+ <tr>
231
+ <td><span class="session-num">{{ forloop.index }}</span></td>
232
+ <td><a href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}" class="session-title-link">{{ s.title }}</a></td>
233
+ <td>{{ s.recordedAt | formatDateShort }}</td>
234
+ <td>{{ s.durationMinutes | formatDuration }}</td>
235
+ <td>{{ s.locChanged | localeNumber }}</td>
236
+ <td>{% if s.sourceTool %}<span class="session-source-badge badge-{{ s.sourceTool | downcase | replace: ' ', '-' }}">{{ s.sourceTool }}</span>{% endif %}</td>
237
+ <td>
238
+ {% if s.agentCount and s.agentCount > 0 %}
239
+ <div class="agent-dots" aria-label="{{ s.agentCount }} agents">
240
+ {% for agent in s.agents %}
241
+ <span class="agent-dot" style="background:{{ agent.color }}" title="{{ agent.role }}"></span>
242
+ {% endfor %}
243
+ </div>
244
+ {% else %}
245
+ <span style="font-family:var(--font-mono);font-size:12px;color:var(--text-secondary)">0</span>
246
+ {% endif %}
247
+ </td>
248
+ </tr>
249
+ {% endfor %}
250
+ </tbody>
251
+ </table>
252
+ </div>
253
+ </section>
254
+ {% endif %}
255
+
256
+ {%- comment -%} Featured sessions {%- endcomment -%}
257
+ {% if featuredSessions.size > 0 %}
258
+ <section class="featured-section anim-fade-up anim-delay-4" aria-label="Featured sessions">
259
+ <div class="section-heading">
260
+ <svg width="20" height="20" aria-hidden="true">
261
+ <circle cx="10" cy="10" r="10" fill="var(--red)" />
262
+ </svg>
263
+ <h2>Featured Sessions</h2>
264
+ <div class="heading-line" aria-hidden="true"></div>
265
+ </div>
266
+ <div class="featured-grid">
267
+ {% for s in featuredSessions %}
268
+ <a href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}" class="featured-card">
269
+ <div class="featured-card-accent" aria-hidden="true"></div>
270
+ <div class="featured-card-body">
271
+ {% if s.skills.size > 0 %}
272
+ <div class="featured-card-tag">{{ s.skills | first }}</div>
273
+ {% endif %}
274
+ <h3 class="featured-card-title">{{ s.title }}</h3>
275
+ <div class="featured-card-stats">
276
+ <span><strong>{{ s.durationMinutes | formatDuration }}</strong></span>
277
+ <span><strong>{{ s.locChanged | localeNumber }}</strong> LOC</span>
278
+ <span><strong>{{ s.filesChanged }}</strong> files</span>
279
+ </div>
280
+ </div>
281
+ </a>
282
+ {% endfor %}
283
+ </div>
284
+ </section>
285
+ {% endif %}
286
+
287
+ {%- comment -%} Footer {%- endcomment -%}
288
+ <footer class="bauhaus-footer">
289
+ <div class="footer-inner">
290
+ <span class="footer-text">Built with heyi.am</span>
291
+ <div class="footer-shapes" aria-hidden="true">
292
+ <span class="footer-shape footer-sq-red"></span>
293
+ <span class="footer-shape footer-circle-blue"></span>
294
+ <span class="footer-shape footer-sq-yellow"></span>
295
+ </div>
296
+ </div>
297
+ </footer>
298
+
299
+
300
+ </div>