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,257 @@
1
+ <div class="heyiam-portfolio obsidian" data-render-version="2" data-template="obsidian" data-username="{{ user.username }}">
2
+
3
+ <a href="#main" class="skip-link">Skip to content</a>
4
+
5
+ <main id="main">
6
+ {% if hasProfile %}
7
+ <section class="hero hero--portfolio container fade-up" aria-label="Profile">
8
+ <div class="hero-row">
9
+ {% if user.photoUrl %}
10
+ <img src="{{ user.photoUrl }}" alt="{{ user.displayName }}" class="hero-photo portfolio-photo">
11
+ {% endif %}
12
+ <div class="hero-row-text">
13
+ {% if user.displayName != blank %}
14
+ <h1>{{ user.displayName }}</h1>
15
+ {% endif %}
16
+ {% if user.bio != blank %}
17
+ <p class="hero-bio portfolio-bio">{{ user.bio }}</p>
18
+ {% endif %}
19
+ {% if user.location != blank %}
20
+ <p class="hero-location portfolio-location">
21
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
22
+ {{ user.location }}
23
+ </p>
24
+ {% endif %}
25
+ {% if user.email or user.linkedinUrl or user.githubUrl or user.twitterHandle or user.websiteUrl or user.resumeUrl %}
26
+ <ul class="hero-links" aria-label="Contact and social links">
27
+ {% if user.email %}
28
+ <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>
29
+ {% endif %}
30
+ {% if user.linkedinUrl %}
31
+ <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>
32
+ {% endif %}
33
+ {% if user.githubUrl %}
34
+ <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>
35
+ {% endif %}
36
+ {% if user.twitterHandle %}
37
+ <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>
38
+ {% endif %}
39
+ {% if user.websiteUrl %}
40
+ <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>
41
+ {% endif %}
42
+ {% if user.resumeUrl %}
43
+ <li><a href="{{ user.resumeUrl }}" download><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>
44
+ {% endif %}
45
+ </ul>
46
+ {% endif %}
47
+ </div>
48
+ </div>
49
+ </section>
50
+ {% endif %}
51
+
52
+ {%- comment -%} Stats {%- endcomment -%}
53
+ <section class="container fade-up" aria-label="Statistics">
54
+ <div class="stats-grid">
55
+ <div class="stat-card">
56
+ <div class="stat-value">{{ projects.size }}</div>
57
+ <div class="stat-label">Projects</div>
58
+ </div>
59
+ <div class="stat-card">
60
+ <div class="stat-value">{{ totalSessions }}</div>
61
+ <div class="stat-label">Sessions</div>
62
+ </div>
63
+ {% if efficiencyMultiplier %}
64
+ <div class="stat-card stat-card--leverage">
65
+ <div class="leverage-row">
66
+ <div class="leverage-cell">
67
+ <div class="leverage-cell__value">{{ totalDurationMinutes | formatDuration }}</div>
68
+ <div class="leverage-cell__label">You</div>
69
+ </div>
70
+ <div class="leverage-cell">
71
+ <div class="leverage-cell__value leverage-cell__value--accent">{{ totalAgentDurationMinutes | formatDuration }}</div>
72
+ <div class="leverage-cell__label">Agents</div>
73
+ </div>
74
+ <div class="leverage-cell">
75
+ <div class="leverage-cell__value leverage-cell__value--accent">{{ efficiencyMultiplier }}</div>
76
+ <div class="leverage-cell__label">Multiplier</div>
77
+ </div>
78
+ </div>
79
+ <div class="leverage-rule"></div>
80
+ </div>
81
+ {% else %}
82
+ <div class="stat-card">
83
+ <div class="stat-value">{{ totalDurationMinutes | formatDuration }}</div>
84
+ <div class="stat-label">Total Time</div>
85
+ </div>
86
+ {% endif %}
87
+ <div class="stat-card">
88
+ <div class="stat-value">{{ totalLoc | formatLoc }}</div>
89
+ <div class="stat-label">Lines Changed</div>
90
+ </div>
91
+ </div>
92
+ </section>
93
+
94
+ {%- comment -%} Projects {%- endcomment -%}
95
+ {% if projects.size > 0 %}
96
+ <section class="container" aria-label="Projects">
97
+ <h2 class="section-heading fade-up">Projects</h2>
98
+ <div class="projects-list">
99
+ {% for p in projects %}
100
+ <a href="/{{ user.username }}/{{ p.slug }}" class="project-card shimmer-transition fade-up">
101
+ <div class="project-header">
102
+ <span class="project-name">{{ p.title }}</span>
103
+ <div class="project-meta">
104
+ <span>{{ p.totalSessions }} session{% if p.totalSessions != 1 %}s{% endif %}</span>
105
+ <span>{{ p.totalDurationMinutes | formatDuration }}</span>
106
+ <span>{{ p.totalLoc | formatLoc }} LOC</span>
107
+ </div>
108
+ </div>
109
+ {% if p.narrative != blank %}
110
+ <p class="project-narrative">{{ p.narrative | truncate: 120 }}</p>
111
+ {% endif %}
112
+ {% if p.skills.size > 0 %}
113
+ <div class="project-skills">
114
+ {% for skill in p.skills %}
115
+ <span class="skill-tag chip">{{ skill }}</span>
116
+ {% endfor %}
117
+ </div>
118
+ {% endif %}
119
+ <div class="project-footer">
120
+ {% if p.sourceCounts.size > 0 %}
121
+ <div class="source-bar">
122
+ {% assign pTotal = 0 %}
123
+ {% for src in p.sourceCounts %}
124
+ {% assign pTotal = pTotal | plus: src.count %}
125
+ {% endfor %}
126
+ {% for src in p.sourceCounts %}
127
+ {% if pTotal > 0 %}
128
+ {% assign sPct = src.count | times: 100.0 | divided_by: pTotal | round %}
129
+ {% else %}
130
+ {% assign sPct = 0 %}
131
+ {% endif %}
132
+ <span>{{ src.tool }} {{ sPct }}%</span>
133
+ {% if forloop.first and pTotal > 0 %}
134
+ <div class="source-track"><div class="source-fill" style="width: {{ sPct }}%"></div></div>
135
+ {% endif %}
136
+ {% endfor %}
137
+ </div>
138
+ {% else %}
139
+ <div class="project-meta">
140
+ <span>{{ p.totalFilesChanged }} files</span>
141
+ {% if p.publishedCount > 0 %}
142
+ <span>{{ p.publishedCount }} published</span>
143
+ {% endif %}
144
+ </div>
145
+ {% endif %}
146
+ <span class="project-arrow" aria-hidden="true">&rarr;</span>
147
+ </div>
148
+ </a>
149
+ {% endfor %}
150
+ </div>
151
+ </section>
152
+ {% endif %}
153
+
154
+ {%- comment -%} Skills Overview {%- endcomment -%}
155
+ {% if allSkills.size > 0 %}
156
+ <section class="container" aria-label="Skills overview">
157
+ <h2 class="section-heading fade-up">Skills</h2>
158
+ <div class="skills-overview fade-up">
159
+ {% if topSkills.size > 0 %}
160
+ <div class="skills-chart">
161
+ <div class="skills-chart-title">Most used technologies across all projects</div>
162
+ <div class="skills-bar-list">
163
+ {% assign maxSkillCount = topSkills[0].count %}
164
+ {% for sk in topSkills %}
165
+ {% if maxSkillCount > 0 %}
166
+ {% assign skPct = sk.count | times: 100 | divided_by: maxSkillCount %}
167
+ {% else %}
168
+ {% assign skPct = 0 %}
169
+ {% endif %}
170
+ <div class="skills-bar-row">
171
+ <span class="skills-bar-label">{{ sk.name }}</span>
172
+ <div class="skills-bar-track">
173
+ <div class="skills-bar-fill" style="width: {{ skPct }}%"></div>
174
+ </div>
175
+ <span class="skills-bar-value">{{ sk.count }} project{% if sk.count != 1 %}s{% endif %}</span>
176
+ </div>
177
+ {% endfor %}
178
+ </div>
179
+ </div>
180
+ {% endif %}
181
+ <div class="skills-tags">
182
+ {% for skill in allSkills %}
183
+ <span class="skill-tag chip">{{ skill }}</span>
184
+ {% endfor %}
185
+ </div>
186
+ </div>
187
+ </section>
188
+ {% endif %}
189
+
190
+ {%- comment -%} Activity {%- endcomment -%}
191
+ <section class="container activity-section" aria-label="Activity">
192
+ <h2 class="section-heading fade-up">Activity</h2>
193
+ <div class="activity-grid fade-up">
194
+ {% if sourceCounts.size > 0 %}
195
+ <div class="activity-card">
196
+ <div class="activity-chart" aria-label="Source breakdown chart">
197
+ <div class="activity-chart-title">Source Breakdown</div>
198
+ {% assign totalSrc = 0 %}
199
+ {% for src in sourceCounts %}
200
+ {% assign totalSrc = totalSrc | plus: src.count %}
201
+ {% endfor %}
202
+ {% if totalSrc > 0 %}
203
+ {% assign primaryPct = sourceCounts[0].count | times: 100.0 | divided_by: totalSrc | round %}
204
+ {% assign dashLen = primaryPct | times: 314 | divided_by: 100 %}
205
+ {% assign dashGap = 314 | minus: dashLen %}
206
+ <div class="source-ring" role="img" aria-label="{{ sourceCounts[0].tool }} {{ primaryPct }}%">
207
+ <svg viewBox="0 0 120 120" class="ring-svg">
208
+ <circle cx="60" cy="60" r="50" fill="none" stroke="var(--card)" stroke-width="10"/>
209
+ <circle cx="60" cy="60" r="50" fill="none" stroke="var(--accent)" stroke-width="10"
210
+ stroke-dasharray="{{ dashLen }} {{ dashGap }}" stroke-dashoffset="0" stroke-linecap="round"
211
+ transform="rotate(-90 60 60)"/>
212
+ </svg>
213
+ <div class="ring-label">
214
+ <span class="ring-value">{{ primaryPct }}%</span>
215
+ <span class="ring-sub">{{ sourceCounts[0].tool }}</span>
216
+ </div>
217
+ </div>
218
+ <div class="source-legend">
219
+ {% for src in sourceCounts %}
220
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSrc | round %}
221
+ <div class="legend-item">
222
+ <span class="legend-dot" style="background: {% if forloop.first %}var(--accent){% else %}var(--fg-dim){% endif %}"></span>
223
+ <span class="legend-label">{{ src.tool }}</span>
224
+ <span class="legend-value">{{ srcPct }}%</span>
225
+ </div>
226
+ {% endfor %}
227
+ </div>
228
+ {% endif %}
229
+ </div>
230
+ </div>
231
+ {% endif %}
232
+ {% if recentSessions.size > 0 %}
233
+ <div class="activity-card">
234
+ <div class="activity-chart-title">Recent Sessions</div>
235
+ <div class="recent-list">
236
+ {% for rs in recentSessions %}
237
+ <div class="recent-item">
238
+ <span class="recent-dot" aria-hidden="true"></span>
239
+ <span class="recent-title">{{ rs.title }}</span>
240
+ {% if rs.recordedAt %}
241
+ <span class="recent-date">{{ rs.recordedAt | formatDateShort }}</span>
242
+ {% endif %}
243
+ </div>
244
+ {% endfor %}
245
+ </div>
246
+ </div>
247
+ {% endif %}
248
+ </div>
249
+ </section>
250
+ </main>
251
+
252
+ <footer class="site-footer">
253
+ <div class="container">Built with heyi.am</div>
254
+ </footer>
255
+
256
+
257
+ </div>
@@ -0,0 +1,280 @@
1
+ <div class="heyiam-project obsidian" data-render-version="2" data-template="obsidian"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
+
3
+ <a href="#main" class="skip-link">Skip to content</a>
4
+
5
+ <main id="main">
6
+ <div class="container">
7
+
8
+ {%- comment -%} Breadcrumb {%- endcomment -%}
9
+ <div class="breadcrumb fade-up" aria-label="Breadcrumb">
10
+ <a href="/{{ user.username }}">{{ user.username }}</a>
11
+ <span class="breadcrumb-sep">/</span>
12
+ <span aria-current="page">{{ project.slug }}</span>
13
+ </div>
14
+
15
+ {%- comment -%} Hero {%- endcomment -%}
16
+ <section class="hero hero--project fade-up" aria-label="Project header">
17
+ <h1 data-editable="title">{{ project.title }}</h1>
18
+ {% if project.repoUrl or project.projectUrl %}
19
+ <div class="hero-links">
20
+ {% if project.repoUrl %}
21
+ <a href="{{ project.repoUrl }}" target="_blank" rel="noopener" aria-label="GitHub repository">
22
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
23
+ {{ project.repoUrl | stripProtocol }}
24
+ </a>
25
+ {% endif %}
26
+ {% if project.projectUrl %}
27
+ <a href="{{ project.projectUrl }}" target="_blank" rel="noopener" aria-label="Live site">
28
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
29
+ {{ project.projectUrl | stripProtocol }}
30
+ </a>
31
+ {% endif %}
32
+ </div>
33
+ {% endif %}
34
+ </section>
35
+
36
+ {%- comment -%} Screenshot {%- endcomment -%}
37
+ {% if project.screenshotUrl %}
38
+ <div class="screenshot fade-up browser-chrome">
39
+ <div class="screenshot-chrome">
40
+ <span class="chrome-dot" aria-hidden="true"></span>
41
+ <span class="chrome-dot" aria-hidden="true"></span>
42
+ <span class="chrome-dot" aria-hidden="true"></span>
43
+ {% if project.projectUrl %}
44
+ <span class="chrome-bar">{{ project.projectUrl | stripProtocol }}</span>
45
+ {% endif %}
46
+ </div>
47
+ <img src="{{ project.screenshotUrl }}" alt="{{ project.title }} screenshot">
48
+ </div>
49
+ {% endif %}
50
+
51
+ {%- comment -%} Narrative {%- endcomment -%}
52
+ {% if project.narrative != blank %}
53
+ <section class="narrative fade-up" aria-label="Project narrative">
54
+ <h2 class="section-heading">Narrative</h2>
55
+ <p>{{ project.narrative }}</p>
56
+ </section>
57
+ {% endif %}
58
+
59
+ {%- comment -%} Stats {%- endcomment -%}
60
+ <section class="fade-up" aria-label="Project statistics">
61
+ <div class="stats-grid stats-grid--project">
62
+ <div class="stat-card">
63
+ <div class="stat-value">{{ project.totalSessions }}</div>
64
+ <div class="stat-label">Sessions</div>
65
+ </div>
66
+ {% if efficiencyMultiplier %}
67
+ <div class="stat-card stat-card--leverage">
68
+ <div class="leverage-row">
69
+ <div class="leverage-cell">
70
+ <div class="leverage-cell__value">{{ project.totalDurationMinutes | formatDuration }}</div>
71
+ <div class="leverage-cell__label">You</div>
72
+ </div>
73
+ <div class="leverage-cell">
74
+ <div class="leverage-cell__value leverage-cell__value--accent">{{ project.totalAgentDurationMinutes | formatDuration }}</div>
75
+ <div class="leverage-cell__label">Agents</div>
76
+ </div>
77
+ <div class="leverage-cell">
78
+ <div class="leverage-cell__value leverage-cell__value--accent">{{ efficiencyMultiplier }}</div>
79
+ <div class="leverage-cell__label">Multiplier</div>
80
+ </div>
81
+ </div>
82
+ <div class="leverage-rule"></div>
83
+ </div>
84
+ {% else %}
85
+ <div class="stat-card">
86
+ <div class="stat-value">{{ project.totalDurationMinutes | formatDuration }}</div>
87
+ <div class="stat-label">Time</div>
88
+ </div>
89
+ {% endif %}
90
+ <div class="stat-card">
91
+ <div class="stat-value">{{ project.totalLoc | formatLoc }}</div>
92
+ <div class="stat-label">LOC</div>
93
+ </div>
94
+ <div class="stat-card">
95
+ <div class="stat-value">{{ project.totalFilesChanged }}</div>
96
+ <div class="stat-label">Files</div>
97
+ </div>
98
+ {% if project.totalTokens %}
99
+ <div class="stat-card">
100
+ <div class="stat-value">{{ project.totalTokens | formatTokens }}</div>
101
+ <div class="stat-label">Tokens</div>
102
+ </div>
103
+ {% endif %}
104
+ </div>
105
+ </section>
106
+
107
+ {%- comment -%} Work Timeline (CSS-only SVG chart) {%- endcomment -%}
108
+ {% if featuredSessions.size > 0 %}
109
+ <section class="timeline-chart fade-up" aria-label="Work timeline">
110
+ <div class="chart-title">Lines of code per session</div>
111
+ {% assign maxLoc = 1 %}
112
+ {% for s in featuredSessions %}
113
+ {% if s.locChanged > maxLoc %}{% assign maxLoc = s.locChanged %}{% endif %}
114
+ {% endfor %}
115
+ {% assign sessionCount = featuredSessions.size %}
116
+ {% assign chartW = 700 %}
117
+ {% assign chartH = 200 %}
118
+ <div class="chart-area">
119
+ <svg class="chart-svg" viewBox="0 0 {{ chartW }} {{ chartH }}" preserveAspectRatio="none" aria-label="Chart showing lines of code written across {{ sessionCount }} sessions">
120
+ <defs>
121
+ <linearGradient id="purpleGradient" x1="0" y1="0" x2="0" y2="1">
122
+ <stop offset="0%" stop-color="var(--accent)" stop-opacity="0.25"/>
123
+ <stop offset="100%" stop-color="var(--accent)" stop-opacity="0"/>
124
+ </linearGradient>
125
+ </defs>
126
+ <line class="chart-grid-line" x1="0" y1="50" x2="{{ chartW }}" y2="50"/>
127
+ <line class="chart-grid-line" x1="0" y1="100" x2="{{ chartW }}" y2="100"/>
128
+ <line class="chart-grid-line" x1="0" y1="150" x2="{{ chartW }}" y2="150"/>
129
+ {% assign points = "" %}
130
+ {% assign areaPoints = "" %}
131
+ {% for s in featuredSessions %}
132
+ {% assign idx = forloop.index0 %}
133
+ {% if sessionCount > 1 %}
134
+ {% assign x = idx | times: chartW | minus: 100 | divided_by: sessionCount | plus: 50 %}
135
+ {% else %}
136
+ {% assign x = 350 %}
137
+ {% endif %}
138
+ {% assign yRaw = s.locChanged | times: 180 | divided_by: maxLoc %}
139
+ {% assign y = chartH | minus: yRaw | minus: 10 %}
140
+ {% if forloop.first %}
141
+ {% assign points = x | append: "," | append: y %}
142
+ {% assign areaPoints = x | append: "," | append: y %}
143
+ {% else %}
144
+ {% assign points = points | append: " " | append: x | append: "," | append: y %}
145
+ {% assign areaPoints = areaPoints | append: " " | append: x | append: "," | append: y %}
146
+ {% endif %}
147
+ {% if forloop.last %}
148
+ {% assign areaPoints = areaPoints | append: " " | append: x | append: "," | append: chartH | append: " " | append: 50 | append: "," | append: chartH %}
149
+ {% endif %}
150
+ {% endfor %}
151
+ <polygon class="chart-area-fill" points="{{ areaPoints }}"/>
152
+ <polyline class="chart-line" points="{{ points }}"/>
153
+ {% for s in featuredSessions %}
154
+ {% assign idx = forloop.index0 %}
155
+ {% if sessionCount > 1 %}
156
+ {% assign cx = idx | times: chartW | minus: 100 | divided_by: sessionCount | plus: 50 %}
157
+ {% else %}
158
+ {% assign cx = 350 %}
159
+ {% endif %}
160
+ {% assign cyRaw = s.locChanged | times: 180 | divided_by: maxLoc %}
161
+ {% assign cy = chartH | minus: cyRaw | minus: 10 %}
162
+ <circle class="chart-dot" cx="{{ cx }}" cy="{{ cy }}" r="5"/>
163
+ {% endfor %}
164
+ </svg>
165
+ </div>
166
+ <div class="chart-labels">
167
+ {% for s in featuredSessions %}
168
+ <span>{{ s.recordedAt | formatDateShort }}</span>
169
+ {% endfor %}
170
+ </div>
171
+ </section>
172
+ {% endif %}
173
+
174
+ {%- comment -%} Phases {%- endcomment -%}
175
+ {% if arc.size > 0 %}
176
+ <section class="phases fade-up" aria-label="Project phases">
177
+ <h2 class="section-heading">Phases</h2>
178
+ <ol class="phase-list">
179
+ {% for item in arc %}
180
+ <li class="phase-item">
181
+ <span class="phase-dot" aria-hidden="true"></span>
182
+ <div class="phase-title">{{ item.title }}</div>
183
+ {% if item.dates %}
184
+ <div class="phase-dates">{{ item.dates }}</div>
185
+ {% endif %}
186
+ <div class="phase-desc">{{ item.description }}</div>
187
+ </li>
188
+ {% endfor %}
189
+ </ol>
190
+ </section>
191
+ {% endif %}
192
+
193
+ {%- comment -%} Key Decisions {%- endcomment -%}
194
+ {% if arc.size > 0 %}
195
+ <section class="decisions fade-up" aria-label="Key decisions">
196
+ <h2 class="section-heading">Key Decisions</h2>
197
+ <ol class="decision-list">
198
+ {% for item in arc %}
199
+ <li class="decision-item">
200
+ <span class="decision-num">{{ forloop.index }}</span>
201
+ <span class="decision-text">{{ item.description }}</span>
202
+ </li>
203
+ {% endfor %}
204
+ </ol>
205
+ </section>
206
+ {% endif %}
207
+
208
+ {%- comment -%} Skills {%- endcomment -%}
209
+ {% if project.skills.size > 0 %}
210
+ <section class="skills-section fade-up" aria-label="Technologies used">
211
+ <h2 class="section-heading">Skills</h2>
212
+ <div class="skills-grid">
213
+ {% for skill in project.skills %}
214
+ <span class="skill-tag chip">{{ skill }}</span>
215
+ {% endfor %}
216
+ </div>
217
+ </section>
218
+ {% endif %}
219
+
220
+ {%- comment -%} Source Breakdown {%- endcomment -%}
221
+ {% if sourceCounts.size > 0 %}
222
+ <section class="source-section fade-up" aria-label="Source breakdown">
223
+ <h2 class="section-heading">Source Breakdown</h2>
224
+ <div class="source-breakdown">
225
+ {% assign totalSourceSessions = 0 %}
226
+ {% for src in sourceCounts %}
227
+ {% assign totalSourceSessions = totalSourceSessions | plus: src.count %}
228
+ {% endfor %}
229
+ {% if totalSourceSessions > 0 %}
230
+ <div class="source-header">
231
+ {% for src in sourceCounts %}
232
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
233
+ <span>{{ src.tool }} &mdash; {{ srcPct }}% ({{ src.count }} session{% if src.count != 1 %}s{% endif %})</span>
234
+ {% endfor %}
235
+ </div>
236
+ {% assign firstPct = sourceCounts[0].count | times: 100.0 | divided_by: totalSourceSessions | round %}
237
+ <div class="source-track-lg">
238
+ <div class="source-fill-lg" style="width: {{ firstPct }}%"></div>
239
+ </div>
240
+ {% endif %}
241
+ </div>
242
+ </section>
243
+ {% endif %}
244
+
245
+ {%- comment -%} Featured Sessions {%- endcomment -%}
246
+ {% if featuredSessions.size > 0 %}
247
+ <section class="sessions-section fade-up" aria-label="Featured sessions">
248
+ <h2 class="section-heading">Sessions</h2>
249
+ <div class="session-list">
250
+ {% for s in featuredSessions %}
251
+ <a href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}" class="session-card shimmer-transition">
252
+ <span class="session-num">{{ forloop.index }}</span>
253
+ <div class="session-info">
254
+ <div class="session-title">{{ s.title }}</div>
255
+ <div class="session-meta">
256
+ <span>{{ s.durationMinutes | formatDuration }}</span>
257
+ <span>{{ s.locChanged | localeNumber }} LOC</span>
258
+ {% if s.agentSummary %}
259
+ <span>{{ s.agentSummary.agents.size }} agents</span>
260
+ {% endif %}
261
+ </div>
262
+ </div>
263
+ {% if s.skills.size > 0 %}
264
+ <span class="session-tag">{{ s.skills | first }}</span>
265
+ {% endif %}
266
+ </a>
267
+ {% endfor %}
268
+ </div>
269
+ </section>
270
+ {% endif %}
271
+
272
+ </div>
273
+ </main>
274
+
275
+ <footer class="site-footer">
276
+ <div class="container">Built with heyi.am</div>
277
+ </footer>
278
+
279
+
280
+ </div>