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,180 @@
1
+ <div class="strata-portfolio" data-render-version="2" data-template="strata" data-username="{{ user.username }}">
2
+
3
+ {% if hasProfile %}
4
+ {%- comment -%} Hero: Overlapping Card Portrait {%- endcomment -%}
5
+ <header class="strata-hero">
6
+ {% if user.photoUrl %}
7
+ <div class="strata-hero__photo-card">
8
+ <img src="{{ user.photoUrl }}" alt="{{ user.displayName }}" class="strata-hero__photo" data-portfolio-field="photoBase64"{% unless user.photoUrl %} data-portfolio-empty="true"{% endunless %}>
9
+ </div>
10
+ {% endif %}
11
+ <div class="strata-hero__bio-card">
12
+ {% if user.displayName != blank %}
13
+ <h1 class="strata-hero__name" data-portfolio-field="displayName">{{ user.displayName }}</h1>
14
+ {% endif %}
15
+ {% if user.location %}
16
+ <div class="strata-hero__location">
17
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M8 14s-5-4.5-5-8a5 5 0 1 1 10 0c0 3.5-5 8-5 8z"/><circle cx="8" cy="6" r="1.5"/></svg>
18
+ <span data-portfolio-field="location">{{ user.location }}</span>
19
+ </div>
20
+ {% endif %}
21
+ {% if user.bio %}
22
+ <p class="strata-hero__bio" data-portfolio-field="bio">{{ user.bio }}</p>
23
+ {% endif %}
24
+ <div class="strata-contact-row">
25
+ <a href="{% if user.email %}mailto:{{ user.email }}{% endif %}" data-portfolio-field="email"{% unless user.email %} data-portfolio-empty="true"{% endunless %}><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>
26
+ <span data-portfolio-field="phone"{% unless user.phone %} data-portfolio-empty="true"{% endunless %}><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.79 19.79 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.362 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.338 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/></svg> {{ user.phone }}</span>
27
+ <a href="{% if user.linkedinUrl %}{{ user.linkedinUrl }}{% endif %}" target="_blank" rel="noopener" data-portfolio-field="linkedinUrl"{% unless user.linkedinUrl %} data-portfolio-empty="true"{% endunless %}><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>
28
+ <a href="{% if user.githubUrl %}{{ user.githubUrl }}{% endif %}" target="_blank" rel="noopener" data-portfolio-field="githubUrl"{% unless user.githubUrl %} data-portfolio-empty="true"{% endunless %}><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>
29
+ <a href="{% if user.twitterHandle %}https://x.com/{{ user.twitterHandle }}{% endif %}" target="_blank" rel="noopener" data-portfolio-field="twitterHandle"{% unless user.twitterHandle %} data-portfolio-empty="true"{% endunless %}><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>
30
+ <a href="{% if user.websiteUrl %}{{ user.websiteUrl }}{% endif %}" target="_blank" rel="noopener" data-portfolio-field="websiteUrl"{% unless user.websiteUrl %} data-portfolio-empty="true"{% endunless %}><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>
31
+ {% if user.resumeUrl %}
32
+ <a href="{{ user.resumeUrl }}" class="strata-resume-btn"><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>
33
+ {% endif %}
34
+ </div>
35
+ </div>
36
+ </header>
37
+ {% endif %}
38
+
39
+ {%- comment -%} Aggregate Stats (overlapping stat cards) {%- endcomment -%}
40
+ <div class="strata-agg-stats" aria-label="Portfolio statistics" id="agg-stats">
41
+ <div class="strata-agg-stat">
42
+ <span class="strata-agg-stat__value">{{ projects.size }}</span>
43
+ <span class="strata-agg-stat__label">Projects</span>
44
+ </div>
45
+ <div class="strata-agg-stat">
46
+ <span class="strata-agg-stat__value">{{ totalSessions }}</span>
47
+ <span class="strata-agg-stat__label">Sessions</span>
48
+ </div>
49
+ <div class="strata-agg-stat">
50
+ <span class="strata-agg-stat__value">{{ totalDurationMinutes | formatDuration }}</span>
51
+ <span class="strata-agg-stat__label">{{ durationLabel }}</span>
52
+ </div>
53
+ {% if efficiencyMultiplier %}
54
+ <div class="strata-agg-stat">
55
+ <span class="strata-agg-stat__value" style="color: var(--strata-accent);">{{ totalAgentDurationMinutes | formatDuration }}</span>
56
+ <span class="strata-agg-stat__label">Agents</span>
57
+ </div>
58
+ <div class="strata-agg-stat">
59
+ <span class="strata-agg-stat__value" style="color: var(--strata-accent);">{{ efficiencyMultiplier }}</span>
60
+ <span class="strata-agg-stat__label">Multiplier</span>
61
+ </div>
62
+ {% endif %}
63
+ <div class="strata-agg-stat">
64
+ <span class="strata-agg-stat__value">{{ totalLoc | formatLoc }}</span>
65
+ <span class="strata-agg-stat__label">Lines</span>
66
+ </div>
67
+ </div>
68
+
69
+ {%- comment -%} Project Cards {%- endcomment -%}
70
+ {% if projects.size > 0 %}
71
+ <section aria-label="Projects">
72
+ <div class="strata-layer" style="z-index: 1;">
73
+ <div class="strata-section-header">
74
+ <h2 class="strata-section-header__title">Projects</h2>
75
+ <span class="strata-section-header__meta">{{ projects.size }} total</span>
76
+ </div>
77
+ <div class="strata-project-cards">
78
+ {% for p in projects %}
79
+ <a class="strata-project-card" href="/{{ user.username }}/{{ p.slug }}" aria-label="{{ p.title }} project">
80
+ <div class="strata-project-card__accent" style="background: var(--strata-accent);"></div>
81
+ <div class="strata-project-card__title">{{ p.title }}</div>
82
+ {% if p.narrative %}
83
+ <div class="strata-project-card__desc">{{ p.narrative }}</div>
84
+ {% endif %}
85
+ <div class="strata-project-card__meta">{{ p.totalSessions }} sessions &middot; {{ p.totalDurationMinutes | formatDuration }} &middot; {{ p.totalLoc | formatLoc }} LOC</div>
86
+ {% if p.sourceCounts.size > 0 %}
87
+ <div class="strata-source-mix" aria-label="Source mix: {% for sc in p.sourceCounts %}{{ sc.tool }} {% assign pct = sc.count | times: 100 | divided_by: p.totalSessions %}{{ pct }}%{% unless forloop.last %}, {% endunless %}{% endfor %}">
88
+ {% for sc in p.sourceCounts %}
89
+ {% assign pct = sc.count | times: 100 | divided_by: p.totalSessions %}
90
+ <div class="strata-source-mix__segment" style="width: {{ pct }}%; background: var({% if forloop.first %}--strata-accent{% else %}--strata-text-tertiary{% endif %});" aria-hidden="true"></div>
91
+ {% endfor %}
92
+ </div>
93
+ <div class="strata-project-card__source">{% for sc in p.sourceCounts %}{% assign pct = sc.count | times: 100 | divided_by: p.totalSessions %}{{ sc.tool }} {{ pct }}%{% unless forloop.last %} &middot; {% endunless %}{% endfor %}</div>
94
+ {% endif %}
95
+ {% if p.skills.size > 0 %}
96
+ <div class="strata-project-card__chips">
97
+ {% for skill in p.skills %}
98
+ <span class="strata-project-card__chip">{{ skill }}</span>
99
+ {% endfor %}
100
+ </div>
101
+ {% endif %}
102
+ </a>
103
+ {% endfor %}
104
+ </div>
105
+ </div>
106
+
107
+ {%- comment -%} Technologies {%- endcomment -%}
108
+ {% assign allSkills = "" %}
109
+ {% for p in projects %}
110
+ {% for skill in p.skills %}
111
+ {% unless allSkills contains skill %}
112
+ {% if allSkills != "" %}{% assign allSkills = allSkills | append: "|||" %}{% endif %}
113
+ {% assign allSkills = allSkills | append: skill %}
114
+ {% endunless %}
115
+ {% endfor %}
116
+ {% endfor %}
117
+ {% assign skillList = allSkills | split: "|||" %}
118
+ {% if skillList.size > 0 %}
119
+ <div class="strata-layer" style="z-index: 2;">
120
+ <div class="strata-section-header">
121
+ <h2 class="strata-section-header__title">Technologies</h2>
122
+ </div>
123
+ <div class="strata-chips">
124
+ {% for skill in skillList %}
125
+ <span class="strata-chip">{{ skill }}</span>
126
+ {% endfor %}
127
+ </div>
128
+ </div>
129
+ {% endif %}
130
+ </section>
131
+ {% endif %}
132
+
133
+ {%- comment -%} Activity Chart {%- endcomment -%}
134
+ {% if activityByMonth.size > 0 %}
135
+ <div class="strata-layer" style="z-index: 2;" id="activity-section">
136
+ <div class="strata-section-header">
137
+ <h2 class="strata-section-header__title">Activity Overview</h2>
138
+ <span class="strata-section-header__meta">{{ totalSessions }} sessions across {{ projects.size }} projects</span>
139
+ </div>
140
+ <div class="strata-chart">
141
+ {% assign maxSessions = 0 %}
142
+ {% for m in activityByMonth %}
143
+ {% if m.sessions > maxSessions %}{% assign maxSessions = m.sessions %}{% endif %}
144
+ {% endfor %}
145
+ <svg viewBox="0 0 800 200" preserveAspectRatio="xMidYMid meet" aria-label="Monthly activity overview showing session count">
146
+ <line x1="60" y1="30" x2="760" y2="30" stroke="#f5f5f4" stroke-width="1" stroke-dasharray="4,4"/>
147
+ <line x1="60" y1="70" x2="760" y2="70" stroke="#f5f5f4" stroke-width="1" stroke-dasharray="4,4"/>
148
+ <line x1="60" y1="110" x2="760" y2="110" stroke="#f5f5f4" stroke-width="1" stroke-dasharray="4,4"/>
149
+ <line x1="60" y1="150" x2="760" y2="150" stroke="#f5f5f4" stroke-width="1" stroke-dasharray="4,4"/>
150
+ <line x1="60" y1="180" x2="760" y2="180" stroke="#e7e5e4" stroke-width="1"/>
151
+ {% assign barAreaWidth = 700 | divided_by: activityByMonth.size %}
152
+ {% for m in activityByMonth %}
153
+ {% if maxSessions > 0 %}
154
+ {% assign barHeight = m.sessions | times: 140 | divided_by: maxSessions %}
155
+ {% else %}
156
+ {% assign barHeight = 0 %}
157
+ {% endif %}
158
+ {% assign barY = 180 | minus: barHeight %}
159
+ {% assign barX = forloop.index0 | times: barAreaWidth | plus: 80 %}
160
+ {% assign delay = forloop.index0 | times: 80 %}
161
+ <rect class="strata-chart-bar" x="{{ barX }}" y="{{ barY }}" width="30" height="{{ barHeight }}" rx="4" ry="4" fill="#d97706" opacity="0.85" style="transition-delay: {{ delay }}ms"/>
162
+ {% assign labelX = barX | plus: 15 %}
163
+ <text x="{{ labelX }}" y="196" text-anchor="middle" fill="#a8a29e" font-family="IBM Plex Mono, monospace" font-size="9">{{ m.month }}</text>
164
+ {% assign valueY = barY | minus: 6 %}
165
+ <text x="{{ labelX }}" y="{{ valueY }}" text-anchor="middle" fill="#78716c" font-family="IBM Plex Mono, monospace" font-size="8">{{ m.sessions }}</text>
166
+ {% endfor %}
167
+ </svg>
168
+ </div>
169
+ </div>
170
+ {% endif %}
171
+
172
+ {%- comment -%} Footer {%- endcomment -%}
173
+ <footer class="strata-footer">
174
+ <div class="strata-footer__text">
175
+ Built with <a href="https://heyi.am">heyi.am</a>
176
+ </div>
177
+ </footer>
178
+
179
+
180
+ </div>
@@ -0,0 +1,282 @@
1
+ <div class="strata-project" data-render-version="2" data-template="strata"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
+
3
+ {%- comment -%} Title {%- endcomment -%}
4
+ <h1 class="strata-title" data-editable="title">{{ project.title }}</h1>
5
+
6
+ {%- comment -%} Links {%- endcomment -%}
7
+ {% if project.repoUrl or project.projectUrl %}
8
+ <nav class="strata-links" aria-label="Project links">
9
+ {% if project.repoUrl %}
10
+ <a class="strata-link" href="{{ project.repoUrl }}" target="_blank" rel="noopener">
11
+ <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
12
+ {{ project.repoUrl | stripProtocol }}
13
+ </a>
14
+ {% endif %}
15
+ {% if project.projectUrl %}
16
+ <a class="strata-link" href="{{ project.projectUrl }}" target="_blank" rel="noopener">
17
+ <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><circle cx="8" cy="8" r="6.25"/><path d="M3.5 8h9M8 1.75c-1.5 1.75-2 3.75-2 6.25s.5 4.5 2 6.25M8 1.75c1.5 1.75 2 3.75 2 6.25s-.5 4.5-2 6.25"/></svg>
18
+ {{ project.projectUrl | stripProtocol }}
19
+ </a>
20
+ {% endif %}
21
+ </nav>
22
+ {% endif %}
23
+
24
+ {%- comment -%} Screenshot {%- endcomment -%}
25
+ {% if project.screenshotUrl %}
26
+ <div class="strata-layer strata-layer--screenshot" style="z-index: 1;">
27
+ <div class="browser-chrome">
28
+ <div class="browser-chrome__bar">
29
+ <span class="browser-chrome__dot browser-chrome__dot--red"></span>
30
+ <span class="browser-chrome__dot browser-chrome__dot--yellow"></span>
31
+ <span class="browser-chrome__dot browser-chrome__dot--green"></span>
32
+ {% if project.projectUrl %}
33
+ <span class="browser-chrome__url">{{ project.projectUrl | stripProtocol }}</span>
34
+ {% endif %}
35
+ </div>
36
+ <div class="browser-chrome__viewport">
37
+ <img src="{{ project.screenshotUrl }}" alt="{{ project.title }} screenshot" style="width: 100%; display: block;">
38
+ </div>
39
+ </div>
40
+ </div>
41
+ {% endif %}
42
+
43
+ {%- comment -%} Narrative {%- endcomment -%}
44
+ {% if project.narrative %}
45
+ <div class="strata-layer" style="z-index: 2; padding: 2.5rem 3rem;">
46
+ <div class="strata-narrative">
47
+ <p>{{ project.narrative }}</p>
48
+ </div>
49
+ </div>
50
+ {% endif %}
51
+
52
+ {%- comment -%} Stats {%- endcomment -%}
53
+ <div class="strata-layer" style="z-index: 3;" id="stats-section">
54
+ <div class="strata-stats-grid">
55
+ <div class="strata-stat">
56
+ <span class="strata-stat__label">Sessions</span>
57
+ <span class="strata-stat__value">{{ project.totalSessions }}</span>
58
+ </div>
59
+ <div class="strata-stat">
60
+ <span class="strata-stat__label">{{ durationLabel }}</span>
61
+ <span class="strata-stat__value">{{ project.totalDurationMinutes | formatDuration }}</span>
62
+ </div>
63
+ {% if efficiencyMultiplier %}
64
+ <div class="strata-stat">
65
+ <span class="strata-stat__label">Agents</span>
66
+ <span class="strata-stat__value strata-stat__value--accent">{{ project.totalAgentDurationMinutes | formatDuration }}</span>
67
+ </div>
68
+ <div class="strata-stat">
69
+ <span class="strata-stat__label">Multiplier</span>
70
+ <span class="strata-stat__value strata-stat__value--accent">{{ efficiencyMultiplier }}</span>
71
+ </div>
72
+ {% endif %}
73
+ <div class="strata-stat">
74
+ <span class="strata-stat__label">Lines Changed</span>
75
+ <span class="strata-stat__value">{{ project.totalLoc | localeNumber }}</span>
76
+ </div>
77
+ <div class="strata-stat">
78
+ <span class="strata-stat__label">Files</span>
79
+ <span class="strata-stat__value">{{ project.totalFilesChanged }}</span>
80
+ </div>
81
+ {% if project.totalTokens %}
82
+ <div class="strata-stat">
83
+ <span class="strata-stat__label">Tokens</span>
84
+ <span class="strata-stat__value">{{ project.totalTokens | formatTokens }}</span>
85
+ </div>
86
+ {% endif %}
87
+ </div>
88
+ </div>
89
+
90
+ {%- comment -%} Work Timeline (CSS-only SVG bars) {%- endcomment -%}
91
+ {% if featuredSessions.size > 0 %}
92
+ <div class="strata-layer" style="z-index: 4;">
93
+ <div class="strata-section-header">
94
+ <h2 class="strata-section-header__title">Work Timeline</h2>
95
+ </div>
96
+ {% assign maxLoc = 1 %}
97
+ {% for s in featuredSessions %}
98
+ {% if s.locChanged > maxLoc %}{% assign maxLoc = s.locChanged %}{% endif %}
99
+ {% endfor %}
100
+ {% assign barCount = featuredSessions.size %}
101
+ {% assign barWidth = 50 %}
102
+ {% assign barGap = 90 %}
103
+ {% assign chartLeft = 60 %}
104
+ {% assign chartHeight = 180 %}
105
+ {% assign chartBottom = 210 %}
106
+ {% assign svgWidth = barCount | times: barGap | plus: chartLeft | plus: 40 %}
107
+ <div class="strata-chart">
108
+ <svg viewBox="0 0 {{ svgWidth }} 240" role="img" aria-label="Bar chart showing lines of code written per session">
109
+ <line x1="{{ chartLeft }}" y1="30" x2="{{ svgWidth | minus: 20 }}" y2="30" stroke="var(--strata-border, #e7e5e4)" stroke-width="1" />
110
+ <line x1="{{ chartLeft }}" y1="75" x2="{{ svgWidth | minus: 20 }}" y2="75" stroke="var(--strata-border, #e7e5e4)" stroke-width="1" />
111
+ <line x1="{{ chartLeft }}" y1="120" x2="{{ svgWidth | minus: 20 }}" y2="120" stroke="var(--strata-border, #e7e5e4)" stroke-width="1" />
112
+ <line x1="{{ chartLeft }}" y1="165" x2="{{ svgWidth | minus: 20 }}" y2="165" stroke="var(--strata-border, #e7e5e4)" stroke-width="1" />
113
+ <line x1="{{ chartLeft }}" y1="210" x2="{{ svgWidth | minus: 20 }}" y2="210" stroke="var(--strata-border, #e7e5e4)" stroke-width="1" />
114
+ <text x="55" y="34" text-anchor="end" fill="var(--strata-text-tertiary, #a8a29e)" font-family="var(--strata-font-mono)" font-size="10">{{ maxLoc }}</text>
115
+ <text x="55" y="124" text-anchor="end" fill="var(--strata-text-tertiary, #a8a29e)" font-family="var(--strata-font-mono)" font-size="10">{{ maxLoc | divided_by: 2 }}</text>
116
+ <text x="55" y="214" text-anchor="end" fill="var(--strata-text-tertiary, #a8a29e)" font-family="var(--strata-font-mono)" font-size="10">0</text>
117
+ {% for s in featuredSessions %}
118
+ {% assign barX = forloop.index0 | times: barGap | plus: chartLeft %}
119
+ {% assign barCenterX = barX | plus: 25 %}
120
+ {% if maxLoc > 0 %}
121
+ {% assign barH = s.locChanged | times: chartHeight | divided_by: maxLoc %}
122
+ {% else %}
123
+ {% assign barH = 0 %}
124
+ {% endif %}
125
+ {% assign barY = chartBottom | minus: barH %}
126
+ <rect class="strata-chart-bar" x="{{ barX }}" y="{{ barY }}" width="{{ barWidth }}" height="{{ barH }}" rx="4" ry="4" fill="var(--strata-accent, #d97706)" opacity="0.85" style="transition-delay: {{ forloop.index0 | times: 60 }}ms" />
127
+ <text x="{{ barCenterX }}" y="{{ barY | minus: 6 }}" text-anchor="middle" fill="var(--strata-text-secondary, #78716c)" font-family="var(--strata-font-mono)" font-size="10">{{ s.locChanged | localeNumber }}</text>
128
+ <text x="{{ barCenterX }}" y="228" text-anchor="middle" fill="var(--strata-text-tertiary, #a8a29e)" font-family="var(--strata-font-mono)" font-size="10">S{{ forloop.index }}</text>
129
+ {% endfor %}
130
+ </svg>
131
+ </div>
132
+ </div>
133
+ {% endif %}
134
+
135
+ {%- comment -%} Growth Chart (JS-driven mount) {%- endcomment -%}
136
+ {% if growthJson != blank %}
137
+ <div class="strata-layer" style="z-index: 4;">
138
+ <div class="strata-section-header">
139
+ <h2 class="strata-section-header__title">Lines Changed</h2>
140
+ </div>
141
+ <div class="strata-growth-chart">
142
+ <div data-growth-chart data-variant="strata" data-project-slug="{{ project.slug }}" data-total-loc="{{ project.totalLoc }}" data-total-files="{{ project.totalFilesChanged }}" data-sessions='{{ growthJson | raw }}'></div>
143
+ </div>
144
+ </div>
145
+ {% endif %}
146
+
147
+ {%- comment -%} Two-Column: Key Decisions + Source Breakdown {%- endcomment -%}
148
+ {% if arc.size > 0 or sourceCounts.size > 0 %}
149
+ <div class="strata-layer" style="z-index: 6;">
150
+ <div class="strata-two-col">
151
+ {% if arc.size > 0 %}
152
+ <div>
153
+ <h2 class="strata-col-heading">Key Decisions</h2>
154
+ <div class="strata-decisions">
155
+ {% for item in arc %}
156
+ <div class="strata-decision">
157
+ <div class="strata-decision__title">{{ item.title }}</div>
158
+ <div class="strata-decision__desc">{{ item.description }}</div>
159
+ </div>
160
+ {% endfor %}
161
+ </div>
162
+ </div>
163
+ {% endif %}
164
+ {% if arc.size > 0 and sourceCounts.size > 0 %}
165
+ <div class="strata-col-divider"></div>
166
+ {% endif %}
167
+ {% if sourceCounts.size > 0 %}
168
+ <div>
169
+ <h2 class="strata-col-heading">Source Breakdown</h2>
170
+ {% assign totalSourceSessions = 0 %}
171
+ {% for src in sourceCounts %}
172
+ {% assign totalSourceSessions = totalSourceSessions | plus: src.count %}
173
+ {% endfor %}
174
+ <table class="strata-source-table">
175
+ <thead>
176
+ <tr>
177
+ <th>Source</th>
178
+ <th>Sessions</th>
179
+ <th>%</th>
180
+ </tr>
181
+ </thead>
182
+ <tbody>
183
+ {% for src in sourceCounts %}
184
+ <tr>
185
+ <td>
186
+ {{ src.tool }}
187
+ {% if totalSourceSessions > 0 %}
188
+ {% assign srcPct = src.count | times: 100.0 | divided_by: totalSourceSessions | round: 1 %}
189
+ <span class="strata-source-bar" style="width: {{ srcPct }}%;"></span>
190
+ {% endif %}
191
+ </td>
192
+ <td>{{ src.count }}</td>
193
+ {% if totalSourceSessions > 0 %}
194
+ {% assign srcPctDisplay = src.count | times: 100.0 | divided_by: totalSourceSessions | round %}
195
+ <td>{{ srcPctDisplay }}%</td>
196
+ {% else %}
197
+ <td></td>
198
+ {% endif %}
199
+ </tr>
200
+ {% endfor %}
201
+ </tbody>
202
+ </table>
203
+ </div>
204
+ {% endif %}
205
+ </div>
206
+ </div>
207
+ {% endif %}
208
+
209
+ {%- comment -%} Phase Timeline {%- endcomment -%}
210
+ {% if arc.size > 0 %}
211
+ <div class="strata-layer" style="z-index: 7;">
212
+ <div class="strata-section-header">
213
+ <h2 class="strata-section-header__title">Project Phases</h2>
214
+ </div>
215
+ <div class="strata-phases">
216
+ <div class="strata-phases__line"></div>
217
+ {% for item in arc %}
218
+ <div class="strata-phase">
219
+ <div class="strata-phase__dot"></div>
220
+ <div class="strata-phase__title">{{ item.title }}</div>
221
+ <div class="strata-phase__desc">{{ item.description }}</div>
222
+ </div>
223
+ {% endfor %}
224
+ </div>
225
+ </div>
226
+ {% endif %}
227
+
228
+ {%- comment -%} Skills {%- endcomment -%}
229
+ {% if project.skills.size > 0 %}
230
+ <div class="strata-layer" style="z-index: 8;">
231
+ <div class="strata-section-header">
232
+ <h2 class="strata-section-header__title">Technologies</h2>
233
+ </div>
234
+ <div class="strata-chips">
235
+ {% for skill in project.skills %}
236
+ <span class="strata-chip">{{ skill }}</span>
237
+ {% endfor %}
238
+ </div>
239
+ </div>
240
+ {% endif %}
241
+
242
+ {%- comment -%} Session Cards {%- endcomment -%}
243
+ {% if featuredSessions.size > 0 %}
244
+ <div class="strata-layer" style="z-index: 9;">
245
+ <div class="strata-section-header">
246
+ <h2 class="strata-section-header__title">Sessions</h2>
247
+ <span class="strata-section-header__meta">{{ project.totalSessions }} total</span>
248
+ </div>
249
+ <div class="strata-session-grid strata-session-grid--stacked">
250
+ {% for s in featuredSessions %}
251
+ <a class="strata-session-card" href="{{ sessionBaseUrl }}/{{ s.slug }}{{ sessionSuffix }}">
252
+ <div class="strata-session-card__bar" style="background: var(--strata-accent);"></div>
253
+ <div class="strata-session-card__body">
254
+ <div class="strata-session-card__title">{{ s.title }}</div>
255
+ <div class="strata-session-card__meta">{{ s.durationMinutes | formatDuration }} &middot; {{ s.recordedAt | formatDateShort }}</div>
256
+ <div class="strata-session-card__lines">{{ s.locChanged | localeNumber }} lines</div>
257
+ {% if s.sourceTool %}
258
+ <div class="strata-session-card__source">{{ s.sourceTool }}{% if s.agentSummary %} &middot; {{ s.agentSummary.agents.size }} agents{% endif %}</div>
259
+ {% endif %}
260
+ {% if s.skills.size > 0 %}
261
+ <div class="strata-session-card__chips">
262
+ {% for skill in s.skills limit: 3 %}
263
+ <span class="strata-session-card__chip">{{ skill }}</span>
264
+ {% endfor %}
265
+ </div>
266
+ {% endif %}
267
+ </div>
268
+ </a>
269
+ {% endfor %}
270
+ </div>
271
+ </div>
272
+ {% endif %}
273
+
274
+ {%- comment -%} Footer {%- endcomment -%}
275
+ <footer class="strata-footer">
276
+ <div class="strata-footer__text">
277
+ Built with <a href="https://heyi.am">heyi.am</a>
278
+ </div>
279
+ </footer>
280
+
281
+
282
+ </div>