heyiam 0.2.29 → 0.3.1

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 (186) hide show
  1. package/README.md +45 -0
  2. package/dist/auth.js +29 -3
  3. package/dist/config.js +10 -1
  4. package/dist/db.js +0 -1
  5. package/dist/export.js +124 -27
  6. package/dist/format-utils.js +5 -0
  7. package/dist/github.js +381 -0
  8. package/dist/index.js +168 -0
  9. package/dist/mount.js +300 -102
  10. package/dist/parsers/claude.js +2 -28
  11. package/dist/parsers/codex.js +2 -26
  12. package/dist/parsers/cursor.js +2 -26
  13. package/dist/parsers/duration.js +35 -0
  14. package/dist/parsers/gemini.js +2 -20
  15. package/dist/parsers/index.js +22 -3
  16. package/dist/parsers/types.js +0 -1
  17. package/dist/public/assets/index-Coilyhtr.css +1 -0
  18. package/dist/public/assets/index-D0noVMFu.js +44 -0
  19. package/dist/public/index.html +2 -2
  20. package/dist/redact.js +4 -104
  21. package/dist/render/build-render-data.js +9 -2
  22. package/dist/render/index.js +32 -5
  23. package/dist/render/liquid.js +147 -7
  24. package/dist/render/mock-data.js +303 -0
  25. package/dist/render/templates/aurora/portfolio.liquid +192 -0
  26. package/dist/render/templates/aurora/project.liquid +260 -0
  27. package/dist/render/templates/aurora/session.liquid +223 -0
  28. package/dist/render/templates/aurora/styles.css +1184 -0
  29. package/dist/render/templates/bauhaus/portfolio.liquid +169 -0
  30. package/dist/render/templates/bauhaus/project.liquid +300 -0
  31. package/dist/render/templates/bauhaus/session.liquid +333 -0
  32. package/dist/render/templates/bauhaus/styles.css +1645 -0
  33. package/dist/render/templates/blueprint/portfolio.liquid +153 -0
  34. package/dist/render/templates/blueprint/project.liquid +286 -0
  35. package/dist/render/templates/blueprint/session.liquid +248 -0
  36. package/dist/render/templates/blueprint/styles.css +1289 -0
  37. package/dist/render/templates/canvas/portfolio.liquid +203 -0
  38. package/dist/render/templates/canvas/project.liquid +235 -0
  39. package/dist/render/templates/canvas/session.liquid +223 -0
  40. package/dist/render/templates/canvas/styles.css +1440 -0
  41. package/dist/render/templates/carbon/portfolio.liquid +160 -0
  42. package/dist/render/templates/carbon/project.liquid +249 -0
  43. package/dist/render/templates/carbon/session.liquid +190 -0
  44. package/dist/render/templates/carbon/styles.css +1097 -0
  45. package/dist/render/templates/chalk/portfolio.liquid +189 -0
  46. package/dist/render/templates/chalk/project.liquid +245 -0
  47. package/dist/render/templates/chalk/session.liquid +215 -0
  48. package/dist/render/templates/chalk/styles.css +1161 -0
  49. package/dist/render/templates/circuit/portfolio.liquid +152 -0
  50. package/dist/render/templates/circuit/project.liquid +247 -0
  51. package/dist/render/templates/circuit/session.liquid +205 -0
  52. package/dist/render/templates/circuit/styles.css +1409 -0
  53. package/dist/render/templates/cosmos/portfolio.liquid +222 -0
  54. package/dist/render/templates/cosmos/project.liquid +327 -0
  55. package/dist/render/templates/cosmos/session.liquid +239 -0
  56. package/dist/render/templates/cosmos/styles.css +1157 -0
  57. package/dist/render/templates/daylight/portfolio.liquid +207 -0
  58. package/dist/render/templates/daylight/project.liquid +229 -0
  59. package/dist/render/templates/daylight/session.liquid +219 -0
  60. package/dist/render/templates/daylight/styles.css +1315 -0
  61. package/dist/render/templates/editorial/portfolio.liquid +110 -0
  62. package/dist/render/templates/editorial/project.liquid +202 -0
  63. package/dist/render/templates/editorial/session.liquid +171 -0
  64. package/dist/render/templates/editorial/styles.css +826 -0
  65. package/dist/render/templates/ember/portfolio.liquid +306 -0
  66. package/dist/render/templates/ember/project.liquid +232 -0
  67. package/dist/render/templates/ember/session.liquid +202 -0
  68. package/dist/render/templates/ember/styles.css +1289 -0
  69. package/dist/render/templates/glacier/portfolio.liquid +261 -0
  70. package/dist/render/templates/glacier/project.liquid +288 -0
  71. package/dist/render/templates/glacier/session.liquid +217 -0
  72. package/dist/render/templates/glacier/styles.css +1204 -0
  73. package/dist/render/templates/grid/portfolio.liquid +255 -0
  74. package/dist/render/templates/grid/project.liquid +306 -0
  75. package/dist/render/templates/grid/session.liquid +260 -0
  76. package/dist/render/templates/grid/styles.css +1445 -0
  77. package/dist/render/templates/kinetic/portfolio.liquid +158 -0
  78. package/dist/render/templates/kinetic/project.liquid +242 -0
  79. package/dist/render/templates/kinetic/session.liquid +228 -0
  80. package/dist/render/templates/kinetic/styles.css +948 -0
  81. package/dist/render/templates/meridian/portfolio.liquid +243 -0
  82. package/dist/render/templates/meridian/project.liquid +376 -0
  83. package/dist/render/templates/meridian/session.liquid +298 -0
  84. package/dist/render/templates/meridian/styles.css +1375 -0
  85. package/dist/render/templates/minimal/portfolio.liquid +71 -0
  86. package/dist/render/templates/minimal/project.liquid +154 -0
  87. package/dist/render/templates/minimal/session.liquid +140 -0
  88. package/dist/render/templates/minimal/styles.css +529 -0
  89. package/dist/render/templates/mono/portfolio.liquid +281 -0
  90. package/dist/render/templates/mono/project.liquid +275 -0
  91. package/dist/render/templates/mono/session.liquid +276 -0
  92. package/dist/render/templates/mono/styles.css +1022 -0
  93. package/dist/render/templates/neon/portfolio.liquid +207 -0
  94. package/dist/render/templates/neon/project.liquid +225 -0
  95. package/dist/render/templates/neon/session.liquid +195 -0
  96. package/dist/render/templates/neon/styles.css +1271 -0
  97. package/dist/render/templates/noir/portfolio.liquid +137 -0
  98. package/dist/render/templates/noir/project.liquid +220 -0
  99. package/dist/render/templates/noir/session.liquid +241 -0
  100. package/dist/render/templates/noir/styles.css +1229 -0
  101. package/dist/render/templates/obsidian/portfolio.liquid +247 -0
  102. package/dist/render/templates/obsidian/project.liquid +280 -0
  103. package/dist/render/templates/obsidian/session.liquid +241 -0
  104. package/dist/render/templates/obsidian/styles.css +1407 -0
  105. package/dist/render/templates/paper/portfolio.liquid +257 -0
  106. package/dist/render/templates/paper/project.liquid +235 -0
  107. package/dist/render/templates/paper/session.liquid +271 -0
  108. package/dist/render/templates/paper/styles.css +1513 -0
  109. package/dist/render/templates/parallax/portfolio.liquid +295 -0
  110. package/dist/render/templates/parallax/project.liquid +275 -0
  111. package/dist/render/templates/parallax/session.liquid +295 -0
  112. package/dist/render/templates/parallax/styles.css +1880 -0
  113. package/dist/render/templates/parchment/portfolio.liquid +280 -0
  114. package/dist/render/templates/parchment/project.liquid +289 -0
  115. package/dist/render/templates/parchment/session.liquid +346 -0
  116. package/dist/render/templates/parchment/styles.css +1401 -0
  117. package/dist/render/templates/partials/_beats.liquid +16 -0
  118. package/dist/render/templates/partials/_breadcrumb.liquid +9 -0
  119. package/dist/render/templates/partials/_footer.liquid +7 -0
  120. package/dist/render/templates/partials/_growth-chart.liquid +7 -0
  121. package/dist/render/templates/partials/_key-decisions.liquid +20 -0
  122. package/dist/render/templates/partials/_links.liquid +16 -0
  123. package/dist/render/templates/partials/_narrative.liquid +8 -0
  124. package/dist/render/templates/partials/_phases.liquid +20 -0
  125. package/dist/render/templates/partials/_portfolio-header.liquid +20 -0
  126. package/dist/render/templates/partials/_portfolio-projects.liquid +16 -0
  127. package/dist/render/templates/partials/_portfolio-stats.liquid +19 -0
  128. package/dist/render/templates/partials/_qa.liquid +13 -0
  129. package/dist/render/templates/partials/_screenshot.liquid +15 -0
  130. package/dist/render/templates/partials/_session-cards.liquid +30 -0
  131. package/dist/render/templates/partials/_session-header.liquid +39 -0
  132. package/dist/render/templates/partials/_session-sidebar.liquid +30 -0
  133. package/dist/render/templates/partials/_skills.liquid +12 -0
  134. package/dist/render/templates/partials/_source-breakdown.liquid +22 -0
  135. package/dist/render/templates/partials/_stats.liquid +38 -0
  136. package/dist/render/templates/partials/_work-timeline.liquid +7 -0
  137. package/dist/render/templates/project.liquid +7 -4
  138. package/dist/render/templates/radar/portfolio.liquid +223 -0
  139. package/dist/render/templates/radar/project.liquid +278 -0
  140. package/dist/render/templates/radar/session.liquid +300 -0
  141. package/dist/render/templates/radar/styles.css +1055 -0
  142. package/dist/render/templates/showcase/portfolio.liquid +221 -0
  143. package/dist/render/templates/showcase/project.liquid +237 -0
  144. package/dist/render/templates/showcase/session.liquid +210 -0
  145. package/dist/render/templates/showcase/styles.css +1284 -0
  146. package/dist/render/templates/signal/portfolio.liquid +217 -0
  147. package/dist/render/templates/signal/project.liquid +278 -0
  148. package/dist/render/templates/signal/session.liquid +282 -0
  149. package/dist/render/templates/signal/styles.css +1401 -0
  150. package/dist/render/templates/strata/portfolio.liquid +180 -0
  151. package/dist/render/templates/strata/project.liquid +282 -0
  152. package/dist/render/templates/strata/session.liquid +261 -0
  153. package/dist/render/templates/strata/styles.css +1354 -0
  154. package/dist/render/templates/styles.css +1190 -0
  155. package/dist/render/templates/terminal/portfolio.liquid +102 -0
  156. package/dist/render/templates/terminal/project.liquid +161 -0
  157. package/dist/render/templates/terminal/session.liquid +145 -0
  158. package/dist/render/templates/terminal/styles.css +497 -0
  159. package/dist/render/templates/verdant/portfolio.liquid +321 -0
  160. package/dist/render/templates/verdant/project.liquid +309 -0
  161. package/dist/render/templates/verdant/session.liquid +237 -0
  162. package/dist/render/templates/verdant/styles.css +1261 -0
  163. package/dist/render/templates/zen/portfolio.liquid +124 -0
  164. package/dist/render/templates/zen/project.liquid +187 -0
  165. package/dist/render/templates/zen/session.liquid +203 -0
  166. package/dist/render/templates/zen/styles.css +1211 -0
  167. package/dist/render/templates.js +90 -0
  168. package/dist/routes/auth.js +7 -3
  169. package/dist/routes/context.js +17 -10
  170. package/dist/routes/delete.js +195 -0
  171. package/dist/routes/enhance.js +57 -40
  172. package/dist/routes/export.js +14 -4
  173. package/dist/routes/github.js +254 -0
  174. package/dist/routes/index.js +2 -0
  175. package/dist/routes/portfolio-render-data.js +160 -0
  176. package/dist/routes/preview.js +555 -108
  177. package/dist/routes/projects.js +61 -24
  178. package/dist/routes/publish.js +320 -31
  179. package/dist/routes/settings.js +194 -1
  180. package/dist/routes/sse.js +9 -0
  181. package/dist/search.js +6 -0
  182. package/dist/server.js +11 -3
  183. package/dist/settings.js +112 -9
  184. package/package.json +3 -4
  185. package/dist/public/assets/index-CC9G8EF1.js +0 -21
  186. package/dist/public/assets/index-Dalqz2mC.css +0 -1
@@ -0,0 +1,203 @@
1
+ <div class="heyiam-portfolio canvas" data-render-version="2" data-template="canvas" data-username="{{ user.username }}">
2
+
3
+ {%- comment -%} Hero Split: Magazine Spread {%- endcomment -%}
4
+ {% if hasProfile %}
5
+ <section class="hero-split fade-in" aria-label="Introduction">
6
+ {% if user.photoUrl %}
7
+ <div class="hero-photo-container">
8
+ <img
9
+ src="{{ user.photoUrl }}"
10
+ alt="{{ user.displayName }}"
11
+ class="hero-photo"
12
+ loading="eager"
13
+ data-portfolio-field="photoBase64"{% unless user.photoUrl %} data-portfolio-empty="true"{% endunless %}>
14
+ </div>
15
+ {% endif %}
16
+
17
+ <div class="hero-content">
18
+ {% if user.displayName != blank %}
19
+ <h1 class="hero__name" data-portfolio-field="displayName">{{ user.displayName }}</h1>
20
+ {% endif %}
21
+ {% if user.location %}
22
+ <p class="hero__location" data-portfolio-field="location">{{ user.location }}</p>
23
+ {% endif %}
24
+
25
+ {% if user.bio %}
26
+ <p class="hero__bio" data-portfolio-field="bio">{{ user.bio }}</p>
27
+ {% endif %}
28
+
29
+ {%- comment -%} Contact Links {%- endcomment -%}
30
+ <ul class="hero__links" aria-label="Contact and social links">
31
+ <li data-portfolio-field="email"{% unless user.email %} data-portfolio-empty="true"{% endunless %}>
32
+ <a href="{% if user.email %}mailto:{{ user.email }}{% endif %}">
33
+ <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>
34
+ <span data-portfolio-text>{{ user.email }}</span>
35
+ </a>
36
+ </li>
37
+ <li data-portfolio-field="linkedinUrl"{% unless user.linkedinUrl %} data-portfolio-empty="true"{% endunless %}>
38
+ <a href="{% if user.linkedinUrl %}{{ user.linkedinUrl }}{% endif %}" target="_blank" rel="noopener">
39
+ <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>
40
+ LinkedIn
41
+ </a>
42
+ </li>
43
+ <li data-portfolio-field="githubUrl"{% unless user.githubUrl %} data-portfolio-empty="true"{% endunless %}>
44
+ <a href="{% if user.githubUrl %}{{ user.githubUrl }}{% endif %}" target="_blank" rel="noopener">
45
+ <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>
46
+ GitHub
47
+ </a>
48
+ </li>
49
+ <li data-portfolio-field="twitterHandle"{% unless user.twitterHandle %} data-portfolio-empty="true"{% endunless %}>
50
+ <a href="{% if user.twitterHandle %}https://x.com/{{ user.twitterHandle }}{% endif %}" target="_blank" rel="noopener">
51
+ <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>
52
+ <span data-portfolio-text>{% if user.twitterHandle %}@{{ user.twitterHandle }}{% endif %}</span>
53
+ </a>
54
+ </li>
55
+ <li data-portfolio-field="websiteUrl"{% unless user.websiteUrl %} data-portfolio-empty="true"{% endunless %}>
56
+ <a href="{% if user.websiteUrl %}{{ user.websiteUrl }}{% endif %}" target="_blank" rel="noopener">
57
+ <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>
58
+ <span data-portfolio-text>{{ user.websiteUrl | stripProtocol }}</span>
59
+ </a>
60
+ </li>
61
+ {% if user.resumeUrl %}
62
+ <li>
63
+ <a href="{{ user.resumeUrl }}">
64
+ <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>
65
+ Resume (PDF)
66
+ </a>
67
+ </li>
68
+ {% endif %}
69
+ </ul>
70
+
71
+ <hr class="hero-divider" aria-hidden="true">
72
+
73
+ {%- comment -%} Hero Stats {%- endcomment -%}
74
+ <div class="hero-stats" aria-label="Portfolio statistics">
75
+ <div class="hero-stat">
76
+ <div class="hero-stat__number">{{ projects.size }}</div>
77
+ <div class="hero-stat__label">Projects</div>
78
+ </div>
79
+ <div class="hero-stat">
80
+ <div class="hero-stat__number">{{ totalSessions }}</div>
81
+ <div class="hero-stat__label">Sessions</div>
82
+ </div>
83
+ <div class="hero-stat">
84
+ <div class="hero-stat__number">{{ totalLoc | formatLoc }}</div>
85
+ <div class="hero-stat__label">Lines Changed</div>
86
+ </div>
87
+ {% if efficiencyMultiplier %}
88
+ <div class="hero-stat">
89
+ <div class="hero-stat__number">{{ totalDurationMinutes | formatDuration }}</div>
90
+ <div class="hero-stat__label">{{ durationLabel }}</div>
91
+ </div>
92
+ {% else %}
93
+ <div class="hero-stat">
94
+ <div class="hero-stat__number">{{ totalDurationMinutes | formatDuration }}</div>
95
+ <div class="hero-stat__label">Time</div>
96
+ </div>
97
+ {% endif %}
98
+ </div>
99
+ </div>
100
+ </section>
101
+ {% endif %}
102
+
103
+ {%- comment -%} Below the fold: gallery content {%- endcomment -%}
104
+ <div class="page-content">
105
+
106
+ {%- comment -%} Leverage {%- endcomment -%}
107
+ {% if efficiencyMultiplier %}
108
+ <section class="canvas-leverage" aria-label="AI leverage" role="figure">
109
+ <div class="canvas-leverage__multi">{{ efficiencyMultiplier }}&times;</div>
110
+ <div class="canvas-leverage__context">{{ totalDurationMinutes | formatDuration }} you &middot; <span style="color:var(--canvas-accent)">{{ totalAgentDurationMinutes | formatDuration }} agents</span></div>
111
+ <div class="canvas-leverage__underline"></div>
112
+ </section>
113
+ {% endif %}
114
+
115
+ {%- comment -%} Projects {%- endcomment -%}
116
+ {% if projects.size > 0 %}
117
+ <section class="projects-section" aria-label="Projects">
118
+ <h2 class="section-title fade-in fade-in-d2">Projects</h2>
119
+
120
+ <div class="projects-grid">
121
+ {% for p in projects %}
122
+ <a href="/{{ user.username }}/{{ p.slug }}" class="project-card fade-in fade-in-d3">
123
+ <div class="project-card__header">
124
+ <h3 class="project-card__title">{{ p.title }}</h3>
125
+ <span class="project-card__stats">{{ p.totalSessions }} sessions &middot; {{ p.totalDurationMinutes | formatDuration }} &middot; {{ p.totalLoc | formatLoc }} LOC</span>
126
+ </div>
127
+ {% if p.narrative %}
128
+ <p class="project-card__narrative">{{ p.narrative }}</p>
129
+ {% endif %}
130
+ <div class="project-card__footer">
131
+ {% if p.skills.size > 0 %}
132
+ <div class="project-card__skills">
133
+ {% for skill in p.skills %}
134
+ <span class="skill-pill">{{ skill }}</span>
135
+ {% endfor %}
136
+ </div>
137
+ {% endif %}
138
+ {% if p.sourceCounts.size > 0 %}
139
+ {% assign pTotalSrc = 0 %}
140
+ {% for src in p.sourceCounts %}{% assign pTotalSrc = pTotalSrc | plus: src.count %}{% endfor %}
141
+ {% if pTotalSrc > 0 %}
142
+ <div class="source-bar">
143
+ <div class="source-bar__track">
144
+ {% assign pFirstPct = p.sourceCounts[0].count | times: 100.0 | divided_by: pTotalSrc | round %}
145
+ <div class="source-bar__fill" style="width: {{ pFirstPct }}%"></div>
146
+ </div>
147
+ <span>{{ p.sourceCounts[0].tool }} {{ pFirstPct }}%</span>
148
+ </div>
149
+ {% endif %}
150
+ {% endif %}
151
+ </div>
152
+ </a>
153
+ {% endfor %}
154
+ </div>
155
+ </section>
156
+ {% endif %}
157
+
158
+ {%- comment -%} Skills Overview {%- endcomment -%}
159
+ {% if allSkills.size > 0 %}
160
+ <section class="skills-overview fade-in fade-in-d3" aria-label="All skills">
161
+ <h2 class="section-title" style="text-align: center;">Skills</h2>
162
+ <div class="skills-cloud">
163
+ {% for skill in allSkills %}
164
+ {% if skill.projectCount > 1 %}
165
+ <span class="skill-pill skill-pill--lg">{{ skill.name }}</span>
166
+ {% else %}
167
+ <span class="skill-pill skill-pill--lg skill-pill--secondary">{{ skill.name }}</span>
168
+ {% endif %}
169
+ {% endfor %}
170
+ </div>
171
+ </section>
172
+ {% endif %}
173
+
174
+ {%- comment -%} Source Breakdown {%- endcomment -%}
175
+ {% if sourceCounts.size > 0 %}
176
+ <section class="source-overview fade-in fade-in-d4" aria-label="Source tool breakdown">
177
+ <h2 class="section-title" style="text-align: center;">Source Breakdown</h2>
178
+ <div class="source-overview__visual">
179
+ <div class="source-bar" style="height: 20px; border-radius: 10px; overflow: hidden; display: flex;">
180
+ {% for src in sourceCounts %}
181
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
182
+ <div class="source-bar__fill" style="width: {{ srcPct }}%; {% if forloop.index > 1 %}background: var(--canvas-gray, #d4d4d8);{% endif %}"></div>
183
+ {% endfor %}
184
+ </div>
185
+ <div class="source-legend">
186
+ {% for src in sourceCounts %}
187
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
188
+ <div class="source-legend__item">
189
+ <span class="source-legend__dot" style="background: {% if forloop.first %}var(--canvas-accent){% else %}var(--canvas-gray, #d4d4d8){% endif %}"></span>
190
+ <div>
191
+ <div class="source-legend__value">{{ srcPct }}%</div>
192
+ <div class="source-legend__label">{{ src.tool }}</div>
193
+ </div>
194
+ </div>
195
+ {% endfor %}
196
+ </div>
197
+ </div>
198
+ </section>
199
+ {% endif %}
200
+
201
+ </div>
202
+
203
+ </div>
@@ -0,0 +1,235 @@
1
+ <div class="heyiam-project canvas" data-render-version="2" data-template="canvas"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
+
3
+ <div class="page-wrapper">
4
+
5
+ {%- comment -%} Breadcrumb {%- endcomment -%}
6
+ <nav class="breadcrumb fade-in" aria-label="Breadcrumb">
7
+ <a href="/{{ user.username }}">{{ user.username }}</a>
8
+ <span class="breadcrumb__sep">/</span>
9
+ <span>{{ project.slug }}</span>
10
+ </nav>
11
+
12
+ {%- comment -%} Project Hero {%- endcomment -%}
13
+ <section class="project-hero fade-in fade-in-d1" aria-label="Project overview">
14
+ <h1 class="project-hero__title" data-editable="title">{{ project.title }}</h1>
15
+ {% if project.repoUrl or project.projectUrl %}
16
+ <div class="project-hero__links">
17
+ {% if project.repoUrl %}
18
+ <a href="{{ project.repoUrl }}" target="_blank" rel="noopener">{{ project.repoUrl | stripProtocol }}</a>
19
+ {% endif %}
20
+ {% if project.projectUrl %}
21
+ <a href="{{ project.projectUrl }}" target="_blank" rel="noopener">{{ project.projectUrl | stripProtocol }}</a>
22
+ {% endif %}
23
+ </div>
24
+ {% endif %}
25
+ </section>
26
+
27
+ {%- comment -%} Screenshot {%- endcomment -%}
28
+ {% if project.screenshotUrl %}
29
+ <div class="screenshot fade-in fade-in-d2" data-editable="screenshot">
30
+ <div class="screenshot__bar" aria-hidden="true">
31
+ <div class="screenshot__dot"></div>
32
+ <div class="screenshot__dot"></div>
33
+ <div class="screenshot__dot"></div>
34
+ <div class="screenshot__url">Preview</div>
35
+ </div>
36
+ <div class="screenshot__viewport">
37
+ <img src="{{ project.screenshotUrl }}" alt="{{ project.title }} screenshot" class="screenshot__image">
38
+ </div>
39
+ </div>
40
+ {% endif %}
41
+
42
+ {%- comment -%} Narrative {%- endcomment -%}
43
+ {% if project.narrative %}
44
+ <section class="narrative fade-in fade-in-d3" aria-label="Project narrative">
45
+ <h2 class="section-title">The Story</h2>
46
+ <p>{{ project.narrative }}</p>
47
+ </section>
48
+ {% endif %}
49
+
50
+ {%- comment -%} Stats {%- endcomment -%}
51
+ <section class="project-stats fade-in fade-in-d4" aria-label="Project statistics">
52
+ <div class="stat">
53
+ <div class="stat__number">{{ project.totalSessions }}</div>
54
+ <div class="stat__label">Sessions</div>
55
+ </div>
56
+ <div class="stat">
57
+ <div class="stat__number">{{ project.totalLoc | localeNumber }}</div>
58
+ <div class="stat__label">Lines Changed</div>
59
+ </div>
60
+ <div class="stat">
61
+ <div class="stat__number">{{ project.totalFilesChanged }}</div>
62
+ <div class="stat__label">Files</div>
63
+ </div>
64
+ {% if project.totalTokens %}
65
+ <div class="stat">
66
+ <div class="stat__number">{{ project.totalTokens | formatTokens }}</div>
67
+ <div class="stat__label">Tokens</div>
68
+ </div>
69
+ {% endif %}
70
+ {% if efficiencyMultiplier %}
71
+ <div class="stat">
72
+ <div class="stat__number">{{ project.totalDurationMinutes | formatDuration }}</div>
73
+ <div class="stat__label">{{ durationLabel }}</div>
74
+ </div>
75
+ {% else %}
76
+ <div class="stat">
77
+ <div class="stat__number">{{ project.totalDurationMinutes | formatDuration }}</div>
78
+ <div class="stat__label">Time</div>
79
+ </div>
80
+ {% endif %}
81
+ </section>
82
+
83
+ {%- comment -%} Leverage {%- endcomment -%}
84
+ {% if efficiencyMultiplier %}
85
+ <section class="canvas-leverage" aria-label="AI leverage" role="figure">
86
+ <div class="canvas-leverage__multi">{{ efficiencyMultiplier }}&times;</div>
87
+ <div class="canvas-leverage__context">{{ project.totalDurationMinutes | formatDuration }} you &middot; <span style="color:var(--canvas-accent)">{{ project.totalAgentDurationMinutes | formatDuration }} agents</span></div>
88
+ <div class="canvas-leverage__underline"></div>
89
+ </section>
90
+ {% endif %}
91
+
92
+ {%- comment -%} Phase Timeline {%- endcomment -%}
93
+ {% if arc.size > 0 %}
94
+ <section class="phases-section fade-in fade-in-d5" aria-label="Project phases">
95
+ <h2 class="section-title">Phases</h2>
96
+ <div class="phases-scroll" role="list">
97
+ {% for item in arc %}
98
+ <div class="phase-card" role="listitem">
99
+ <div class="phase-card__number">{% if item.phase < 10 %}0{% endif %}{{ item.phase }}</div>
100
+ <div class="phase-card__name">{{ item.title }}</div>
101
+ {% if item.dates %}
102
+ <div class="phase-card__dates">{{ item.dates }}</div>
103
+ {% endif %}
104
+ <div class="phase-card__desc">{{ item.description }}</div>
105
+ </div>
106
+ {% endfor %}
107
+ </div>
108
+ </section>
109
+ {% endif %}
110
+
111
+ {%- comment -%} Work Timeline Chart (CSS-only SVG) {%- endcomment -%}
112
+ {% if featuredSessions.size > 0 %}
113
+ <section class="timeline-section fade-in fade-in-d5" aria-label="Work timeline">
114
+ <h2 class="section-title">Work Timeline</h2>
115
+ <div class="chart-container">
116
+ {% assign maxLoc = 0 %}
117
+ {% for s in featuredSessions %}
118
+ {% if s.locChanged > maxLoc %}{% assign maxLoc = s.locChanged %}{% endif %}
119
+ {% endfor %}
120
+ {% assign barCount = featuredSessions.size %}
121
+ {% assign chartWidth = barCount | times: 80 | plus: 60 %}
122
+ {% assign chartHeight = 260 %}
123
+ {% assign barMaxHeight = 200 %}
124
+ <svg viewBox="0 0 {{ chartWidth }} {{ chartHeight }}" aria-label="Bar chart showing lines of code per session">
125
+ <title>Work timeline chart showing LOC per session</title>
126
+ {%- comment -%} Grid lines {%- endcomment -%}
127
+ <line x1="40" y1="25" x2="{{ chartWidth | minus: 20 }}" y2="25" stroke="#f4f4f5" stroke-width="1"/>
128
+ <line x1="40" y1="75" x2="{{ chartWidth | minus: 20 }}" y2="75" stroke="#f4f4f5" stroke-width="1"/>
129
+ <line x1="40" y1="125" x2="{{ chartWidth | minus: 20 }}" y2="125" stroke="#f4f4f5" stroke-width="1"/>
130
+ <line x1="40" y1="175" x2="{{ chartWidth | minus: 20 }}" y2="175" stroke="#f4f4f5" stroke-width="1"/>
131
+ <line x1="40" y1="225" x2="{{ chartWidth | minus: 20 }}" y2="225" stroke="#e4e4e7" stroke-width="1"/>
132
+ {%- comment -%} Bars {%- endcomment -%}
133
+ {% for s in featuredSessions %}
134
+ {% if maxLoc > 0 %}
135
+ {% assign barHeight = s.locChanged | times: barMaxHeight | divided_by: maxLoc %}
136
+ {% else %}
137
+ {% assign barHeight = 0 %}
138
+ {% endif %}
139
+ {% assign barY = 225 | minus: barHeight %}
140
+ {% assign barX = forloop.index0 | times: 80 | plus: 55 %}
141
+ {% assign labelX = barX | plus: 28 %}
142
+ <rect x="{{ barX }}" y="{{ barY }}" width="56" height="{{ barHeight }}" rx="8" fill="var(--canvas-accent)"/>
143
+ <text x="{{ labelX }}" y="245" font-family="IBM Plex Mono, monospace" font-size="9" fill="#a1a1aa" text-anchor="middle">S{{ forloop.index }}</text>
144
+ {% endfor %}
145
+ </svg>
146
+ </div>
147
+ </section>
148
+ {% endif %}
149
+
150
+ {%- comment -%} Skills {%- endcomment -%}
151
+ {% if project.skills.size > 0 %}
152
+ <section class="skills-section fade-in fade-in-d6" aria-label="Technologies used">
153
+ <h2 class="section-title">Skills</h2>
154
+ <div class="skills-list">
155
+ {% for skill in project.skills %}
156
+ <span class="skill-pill skill-pill--lg">{{ skill }}</span>
157
+ {% endfor %}
158
+ </div>
159
+ </section>
160
+ {% endif %}
161
+
162
+ {%- comment -%} Key Decisions {%- endcomment -%}
163
+ {% if arc.size > 0 %}
164
+ <section class="decisions-section fade-in fade-in-d6" aria-label="Key decisions">
165
+ <h2 class="section-title">Key Decisions</h2>
166
+ <ol class="decisions-list">
167
+ {% for item in arc %}
168
+ <li>
169
+ <span class="decision-num">{{ item.phase }}</span>
170
+ <span class="decision-text">{{ item.description }}</span>
171
+ </li>
172
+ {% endfor %}
173
+ </ol>
174
+ </section>
175
+ {% endif %}
176
+
177
+ {%- comment -%} Source Breakdown {%- endcomment -%}
178
+ {% if sourceCounts.size > 0 %}
179
+ <section class="source-section fade-in fade-in-d7" aria-label="Source breakdown">
180
+ <h2 class="section-title">Source Breakdown</h2>
181
+ <div class="source-visual">
182
+ {% if sourceCounts.size > 0 %}
183
+ <div class="source-bar-large">
184
+ {% assign firstSrc = sourceCounts | first %}
185
+ {% assign firstPct = firstSrc.count | times: 100.0 | divided_by: project.totalSessions | round %}
186
+ <div class="source-bar-large__fill" style="width: {{ firstPct }}%"></div>
187
+ </div>
188
+ {% endif %}
189
+ <div class="source-legend">
190
+ {% for src in sourceCounts %}
191
+ <div class="source-legend__item">
192
+ {% if forloop.first %}
193
+ <span class="source-legend__dot" style="background: var(--canvas-accent)"></span>
194
+ {% else %}
195
+ <span class="source-legend__dot" style="background: var(--canvas-gray)"></span>
196
+ {% endif %}
197
+ {% assign srcPct = src.count | times: 100.0 | divided_by: project.totalSessions | round %}
198
+ {{ src.tool }} {{ srcPct }}%
199
+ </div>
200
+ {% endfor %}
201
+ </div>
202
+ </div>
203
+ </section>
204
+ {% endif %}
205
+
206
+ {%- comment -%} Featured Sessions {%- endcomment -%}
207
+ {% if featuredSessions.size > 0 %}
208
+ <section class="sessions-section fade-in fade-in-d7" aria-label="Featured sessions">
209
+ <h2 class="section-title">Sessions</h2>
210
+ <div class="sessions-grid">
211
+ {% for s in featuredSessions %}
212
+ <a href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}" class="session-card">
213
+ {% if s.skills.size > 0 %}
214
+ <span class="session-card__tag">{{ s.skills | first }}</span>
215
+ {% endif %}
216
+ <h3 class="session-card__title">{{ s.title }}</h3>
217
+ <div class="session-card__meta">
218
+ <span>{{ s.durationMinutes | formatDuration }}</span>
219
+ <span>{{ s.locChanged | localeNumber }} LOC</span>
220
+ {% if s.agentSummary %}
221
+ <span>{{ s.agentSummary.agents.size }} agents</span>
222
+ {% endif %}
223
+ </div>
224
+ <div class="session-card__preview">
225
+ <div class="session-card__preview-fill" style="width: {{ s.durationMinutes | times: 100 | divided_by: 120 | at_most: 100 }}%; background: var(--canvas-accent)"></div>
226
+ </div>
227
+ </a>
228
+ {% endfor %}
229
+ </div>
230
+ </section>
231
+ {% endif %}
232
+
233
+ </div>
234
+
235
+ </div>
@@ -0,0 +1,223 @@
1
+ <div class="heyiam-session canvas" data-render-version="2" data-template="canvas">
2
+
3
+ <div class="page-wrapper">
4
+
5
+ {%- comment -%} Breadcrumb {%- endcomment -%}
6
+ <nav class="breadcrumb fade-in" aria-label="Breadcrumb">
7
+ <a href="/{{ user.username }}">{{ user.username }}</a>
8
+ {% if projectSlug %}
9
+ <span class="breadcrumb__sep">/</span>
10
+ <a href="/{{ user.username }}/{{ projectSlug }}">{{ projectSlug }}</a>
11
+ {% endif %}
12
+ <span class="breadcrumb__sep">/</span>
13
+ <span>{{ session.title }}</span>
14
+ </nav>
15
+
16
+ {%- comment -%} Session Header {%- endcomment -%}
17
+ <header class="session-header fade-in fade-in-d1">
18
+ <h1 class="session-header__title">{{ session.title }}</h1>
19
+ <div class="session-header__meta">
20
+ {% if session.recordedAt %}
21
+ <span class="session-header__meta-item">
22
+ <span class="session-header__meta-dot"></span>
23
+ {{ session.recordedAt | formatDate }}
24
+ </span>
25
+ {% endif %}
26
+ {% if session.sourceTool %}
27
+ <span class="session-header__meta-item">{{ session.sourceTool }}</span>
28
+ {% endif %}
29
+ <span class="session-header__meta-item">{{ session.durationMinutes | formatDuration }}</span>
30
+ <span class="session-header__meta-item">{{ session.turns }} turns</span>
31
+ <span class="session-header__meta-item">{{ session.locChanged | localeNumber }} LOC</span>
32
+ <span class="session-header__meta-item">{{ session.filesChanged }} files</span>
33
+ </div>
34
+ </header>
35
+
36
+ {%- comment -%} Dev Take {%- endcomment -%}
37
+ {% if session.devTake %}
38
+ <section class="dev-take fade-in fade-in-d2" aria-label="Developer reflection">
39
+ <div class="dev-take__label">Dev Take</div>
40
+ <p class="dev-take__text">{{ session.devTake }}</p>
41
+ </section>
42
+ {% endif %}
43
+
44
+ {%- comment -%} Two-column layout: main + sidebar {%- endcomment -%}
45
+ <div class="content-layout">
46
+ {%- comment -%} Main content column {%- endcomment -%}
47
+ <div class="main-content">
48
+
49
+ {%- comment -%} Session Stats {%- endcomment -%}
50
+ <section class="session-stats fade-in fade-in-d3" aria-label="Session statistics">
51
+ <div class="stat">
52
+ <div class="stat__number">{{ session.durationMinutes }}</div>
53
+ <div class="stat__label">Minutes</div>
54
+ </div>
55
+ {% if session.wallClockMinutes %}
56
+ <div class="stat">
57
+ <div class="stat__number">{{ session.wallClockMinutes }}</div>
58
+ <div class="stat__label">Wall clock</div>
59
+ </div>
60
+ {% endif %}
61
+ <div class="stat">
62
+ <div class="stat__number">{{ session.locChanged | localeNumber }}</div>
63
+ <div class="stat__label">Lines of code</div>
64
+ </div>
65
+ <div class="stat">
66
+ <div class="stat__number">{{ session.turns }}</div>
67
+ <div class="stat__label">Turns</div>
68
+ </div>
69
+ <div class="stat">
70
+ <div class="stat__number">{{ session.filesChanged }}</div>
71
+ <div class="stat__label">Files</div>
72
+ </div>
73
+ </section>
74
+
75
+ {%- comment -%} Narrative {%- endcomment -%}
76
+ {% if session.narrative %}
77
+ <section class="narrative-section fade-in fade-in-d3" aria-label="Session narrative">
78
+ <h2 class="section-title">Narrative</h2>
79
+ <p class="narrative-text">{{ session.narrative }}</p>
80
+ </section>
81
+ {% endif %}
82
+
83
+ {%- comment -%} Highlights {%- endcomment -%}
84
+ {% if session.highlights.size > 0 %}
85
+ <section class="highlights-section fade-in fade-in-d3" aria-label="Highlights">
86
+ <h2 class="section-title">Highlights</h2>
87
+ <ul class="highlights-list">
88
+ {% for h in session.highlights %}
89
+ <li>{{ h }}</li>
90
+ {% endfor %}
91
+ </ul>
92
+ </section>
93
+ {% endif %}
94
+
95
+ {%- comment -%} Execution Path (Beats) {%- endcomment -%}
96
+ {% if session.beats.size > 0 %}
97
+ <section class="beats-section fade-in fade-in-d4" aria-label="Execution path">
98
+ <h2 class="section-title">Execution Path</h2>
99
+ <ol class="beats-list">
100
+ {% for beat in session.beats %}
101
+ <li class="beat-item">
102
+ <span class="beat-dot">{{ beat.stepNumber }}</span>
103
+ <div class="beat-content">
104
+ <h3 class="beat-content__title">{{ beat.title }}</h3>
105
+ <p class="beat-content__desc">{{ beat.body }}</p>
106
+ </div>
107
+ </li>
108
+ {% endfor %}
109
+ </ol>
110
+ </section>
111
+ {% endif %}
112
+
113
+ {%- comment -%} Q&A {%- endcomment -%}
114
+ {% if session.qaPairs.size > 0 %}
115
+ <section class="qa-section fade-in fade-in-d5" aria-label="Questions and answers">
116
+ <h2 class="section-title">Q&amp;A</h2>
117
+ <div class="qa-list">
118
+ {% for qa in session.qaPairs %}
119
+ <article class="qa-item">
120
+ <h3 class="qa-question">{{ qa.question }}</h3>
121
+ <p class="qa-answer">{{ qa.answer }}</p>
122
+ </article>
123
+ {% endfor %}
124
+ </div>
125
+ </section>
126
+ {% endif %}
127
+
128
+ {%- comment -%} Agent Summary {%- endcomment -%}
129
+ {% if session.agentSummary %}
130
+ <section class="agents-section fade-in fade-in-d6" aria-label="Agent roles">
131
+ <h2 class="section-title">Agent Summary</h2>
132
+ <div class="agents-chart">
133
+ {% assign agent_colors = '#0891b2,#059669,#7c3aed,#475569,#e11d48,#d97706' | split: ',' %}
134
+ {% assign maxAgentLoc = 0 %}
135
+ {% for agent in session.agentSummary.agents %}
136
+ {% if agent.loc_changed > maxAgentLoc %}
137
+ {% assign maxAgentLoc = agent.loc_changed %}
138
+ {% endif %}
139
+ {% endfor %}
140
+
141
+ {% for agent in session.agentSummary.agents %}
142
+ <div class="agent-bar-row">
143
+ <span class="agent-bar-label">{{ agent.role }}</span>
144
+ <div class="agent-bar-track">
145
+ {% if maxAgentLoc > 0 %}
146
+ {% assign barPct = agent.loc_changed | times: 100 | divided_by: maxAgentLoc %}
147
+ {% else %}
148
+ {% assign barPct = 0 %}
149
+ {% endif %}
150
+ {% assign colorIdx = forloop.index0 | modulo: 6 %}
151
+ <div class="agent-bar-fill" style="width: {{ barPct }}%; background: {{ agent_colors[colorIdx] }};"></div>
152
+ </div>
153
+ <span class="agent-bar-meta">{{ agent.duration_minutes }}m / {{ agent.loc_changed }} LOC</span>
154
+ </div>
155
+ {% endfor %}
156
+
157
+ <div class="agents-legend">
158
+ {% for agent in session.agentSummary.agents %}
159
+ {% assign colorIdx = forloop.index0 | modulo: 6 %}
160
+ <div class="agent-legend-item">
161
+ <span class="agent-legend-dot" style="background: {{ agent_colors[colorIdx] }}"></span>
162
+ {{ agent.role }}
163
+ </div>
164
+ {% endfor %}
165
+ </div>
166
+ </div>
167
+ </section>
168
+ {% endif %}
169
+ </div>
170
+
171
+ {%- comment -%} Sidebar {%- endcomment -%}
172
+ <aside class="sidebar fade-in fade-in-d3" aria-label="Session details">
173
+ {%- comment -%} Tools Used {%- endcomment -%}
174
+ {% if session.toolBreakdown.size > 0 %}
175
+ <div class="sidebar-section">
176
+ <h2 class="sidebar-title">Tools Used</h2>
177
+ <ul class="tools-list">
178
+ {% for t in session.toolBreakdown %}
179
+ <li>
180
+ <span class="tool-name">{{ t.tool }}</span>
181
+ <span class="tool-count">{{ t.count }}</span>
182
+ </li>
183
+ {% endfor %}
184
+ </ul>
185
+ </div>
186
+ {% endif %}
187
+
188
+ {%- comment -%} Files Changed {%- endcomment -%}
189
+ {% if session.topFiles.size > 0 %}
190
+ <div class="sidebar-section">
191
+ <h2 class="sidebar-title">Files Changed</h2>
192
+ <ul class="files-list">
193
+ {% for f in session.topFiles %}
194
+ <li>
195
+ <span class="file-path">{{ f.path }}</span>
196
+ <span class="file-diff">+{{ f.additions }}{% if f.deletions > 0 %} -{{ f.deletions }}{% endif %}</span>
197
+ </li>
198
+ {% endfor %}
199
+ </ul>
200
+ </div>
201
+ {% endif %}
202
+
203
+ {%- comment -%} Skills {%- endcomment -%}
204
+ {% if session.skills.size > 0 %}
205
+ <div class="sidebar-section">
206
+ <h2 class="sidebar-title">Skills</h2>
207
+ <div class="sidebar-skills">
208
+ {% for skill in session.skills %}
209
+ <span class="sidebar-skill">{{ skill }}</span>
210
+ {% endfor %}
211
+ </div>
212
+ </div>
213
+ {% endif %}
214
+ </aside>
215
+ </div>
216
+
217
+ </div>
218
+
219
+ {%- comment -%} Agent bar animation script {%- endcomment -%}
220
+ {% if session.agentSummary %}
221
+ {% endif %}
222
+
223
+ </div>