heyiam 0.3.4 → 0.3.6

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 (45) hide show
  1. package/dist/export.js +19 -5
  2. package/dist/mount.js +9 -7
  3. package/dist/public/assets/{index-Cq04whgG.js → index-7mUuxgqY.js} +3 -3
  4. package/dist/public/index.html +1 -1
  5. package/dist/render/build-render-data.js +1 -0
  6. package/dist/render/liquid.js +12 -2
  7. package/dist/render/templates/aurora/project.liquid +1 -1
  8. package/dist/render/templates/bauhaus/project.liquid +1 -1
  9. package/dist/render/templates/blueprint/project.liquid +1 -1
  10. package/dist/render/templates/canvas/project.liquid +1 -1
  11. package/dist/render/templates/carbon/project.liquid +1 -1
  12. package/dist/render/templates/chalk/project.liquid +1 -1
  13. package/dist/render/templates/circuit/project.liquid +1 -1
  14. package/dist/render/templates/cosmos/project.liquid +1 -1
  15. package/dist/render/templates/daylight/project.liquid +1 -1
  16. package/dist/render/templates/editorial/project.liquid +1 -1
  17. package/dist/render/templates/ember/project.liquid +1 -1
  18. package/dist/render/templates/glacier/project.liquid +1 -1
  19. package/dist/render/templates/grid/project.liquid +1 -1
  20. package/dist/render/templates/kinetic/portfolio.liquid +1 -1
  21. package/dist/render/templates/kinetic/project.liquid +1 -1
  22. package/dist/render/templates/kinetic/styles.css +15 -9
  23. package/dist/render/templates/meridian/project.liquid +1 -1
  24. package/dist/render/templates/minimal/project.liquid +1 -1
  25. package/dist/render/templates/mono/project.liquid +1 -1
  26. package/dist/render/templates/neon/project.liquid +1 -1
  27. package/dist/render/templates/noir/project.liquid +1 -1
  28. package/dist/render/templates/obsidian/project.liquid +1 -1
  29. package/dist/render/templates/paper/project.liquid +1 -1
  30. package/dist/render/templates/parallax/project.liquid +1 -1
  31. package/dist/render/templates/parchment/project.liquid +1 -1
  32. package/dist/render/templates/project.liquid +1 -1
  33. package/dist/render/templates/radar/project.liquid +1 -1
  34. package/dist/render/templates/showcase/project.liquid +1 -1
  35. package/dist/render/templates/signal/project.liquid +1 -1
  36. package/dist/render/templates/strata/project.liquid +1 -1
  37. package/dist/render/templates/terminal/project.liquid +1 -1
  38. package/dist/render/templates/verdant/project.liquid +1 -1
  39. package/dist/render/templates/zen/project.liquid +1 -1
  40. package/dist/routes/portfolio-render-data.js +2 -2
  41. package/dist/routes/preview.js +18 -2
  42. package/dist/routes/project-session-upload.js +4 -3
  43. package/dist/routes/publish.js +118 -16
  44. package/dist/settings.js +14 -1
  45. package/package.json +1 -1
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <title>app</title>
8
- <script type="module" crossorigin src="/assets/index-Cq04whgG.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-7mUuxgqY.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-CMyamplX.css">
10
10
  </head>
11
11
  <body>
@@ -104,5 +104,6 @@ export function buildProjectRenderData(opts) {
104
104
  sessions: opts.sessionCards,
105
105
  allSessions: opts.allSessionCards,
106
106
  sessionBaseUrl: opts.sessionBaseUrl,
107
+ sessionSuffix: opts.sessionSuffix,
107
108
  };
108
109
  }
@@ -90,6 +90,11 @@ export function renderProject(data, extras, templateName) {
90
90
  }
91
91
  // Use full session data when available (charts need complete Session objects).
92
92
  // Strip rawLog and turnTimeline — huge, unused by charts, and could break HTML attributes.
93
+ // Raw Session (from the analyzer) has no `slug` field; SessionCard does. Merge
94
+ // slug by token so the overlay can build a valid "View full session" URL.
95
+ const slugByToken = new Map();
96
+ for (const s of allSessions)
97
+ slugByToken.set(s.token, s.slug);
93
98
  const chartSessions = (extras?.fullSessions ?? allSessions.map((s) => ({
94
99
  id: s.token, slug: s.slug, title: s.title, date: s.recordedAt,
95
100
  durationMinutes: s.durationMinutes, turns: s.turns,
@@ -100,7 +105,12 @@ export function renderProject(data, extras, templateName) {
100
105
  filesChanged: s.filesChanged,
101
106
  }))).map((s) => {
102
107
  const { rawLog, turnTimeline, ...rest } = s;
103
- return { ...rest, rawLog: [] };
108
+ const id = String(rest.id ?? '');
109
+ const existingSlug = rest.slug;
110
+ const slug = typeof existingSlug === 'string' && existingSlug !== ''
111
+ ? existingSlug
112
+ : (slugByToken.get(id) ?? '');
113
+ return { ...rest, slug, rawLog: [] };
104
114
  });
105
115
  // Encode JSON safe for single-quoted HTML attributes
106
116
  const sessionsJson = JSON.stringify(chartSessions).replace(/'/g, '&#39;');
@@ -193,7 +203,7 @@ export function renderProject(data, extras, templateName) {
193
203
  seen.add(s.token);
194
204
  return true;
195
205
  }).slice(0, 6);
196
- const sessionSuffix = data.sessionBaseUrl ? '.html' : '';
206
+ const sessionSuffix = data.sessionSuffix ?? '';
197
207
  const html = engine.renderFileSync(`${template}/project`, {
198
208
  ...data,
199
209
  arc: extras?.arc ?? [],
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project aurora" data-render-version="2" data-template="aurora"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project aurora" data-render-version="2" data-template="aurora"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Aurora Header {%- endcomment -%}
4
4
  <header class="aurora-header" role="banner">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project bauhaus" data-render-version="2" data-template="bauhaus"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project bauhaus" data-render-version="2" data-template="bauhaus"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Breadcrumb {%- endcomment -%}
4
4
  <nav class="breadcrumb" aria-label="Breadcrumb">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project blueprint" data-render-version="2" data-template="blueprint"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project blueprint" data-render-version="2" data-template="blueprint"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Crosshatch SVG defs {%- endcomment -%}
4
4
  <svg class="bp-crosshatch-defs" aria-hidden="true">
@@ -1,4 +1,4 @@
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 }}">
1
+ <div class="heyiam-project canvas" data-render-version="2" data-template="canvas"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="page-wrapper">
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project carbon" data-render-version="2" data-template="carbon"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project carbon" data-render-version="2" data-template="carbon"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Breadcrumb {%- endcomment -%}
4
4
  <nav class="breadcrumb" aria-label="Breadcrumb">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project chalk" data-render-version="2" data-template="chalk"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project chalk" data-render-version="2" data-template="chalk"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} SVG filters {%- endcomment -%}
4
4
  <svg class="svg-filters" aria-hidden="true">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project circuit" data-render-version="2" data-template="circuit"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project circuit" data-render-version="2" data-template="circuit"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Breadcrumb {%- endcomment -%}
4
4
  <div class="breadcrumb" aria-label="Breadcrumb">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project cosmos" data-render-version="2" data-template="cosmos"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project cosmos" data-render-version="2" data-template="cosmos"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="starfield" aria-hidden="true"></div>
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project daylight" data-render-version="2" data-template="daylight"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project daylight" data-render-version="2" data-template="daylight"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="dl-container">
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project editorial" data-render-version="2" data-template="editorial"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project editorial" data-render-version="2" data-template="editorial"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Breadcrumb {%- endcomment -%}
4
4
  <div class="ed-breadcrumb" aria-label="Breadcrumb">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project ember" data-render-version="2" data-template="ember"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project ember" data-render-version="2" data-template="ember"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Breadcrumb {%- endcomment -%}
4
4
  <div class="breadcrumb ember-section" aria-label="Breadcrumb">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project glacier" data-render-version="2" data-template="glacier"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project glacier" data-render-version="2" data-template="glacier"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Breadcrumb {%- endcomment -%}
4
4
  <div class="container">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project grid" data-render-version="2" data-template="grid"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project grid" data-render-version="2" data-template="grid"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="container">
4
4
  <div class="breadcrumb" aria-label="Breadcrumb">
@@ -81,7 +81,7 @@
81
81
  {% if projects.size > 0 %}
82
82
  <section class="section" aria-label="Projects">
83
83
  <div class="section-tag">Projects</div>
84
- <h2 class="section-title">{{ projects.size }} project{{ projects.size | minus: 1 | at_least: 1 | times: 0 | plus: projects.size | minus: 1 }}{% if projects.size != 1 %}s{% endif %} built with AI</h2>
84
+ <h2 class="section-title">{{ projects.size }} project{% if projects.size != 1 %}s{% endif %} built with AI</h2>
85
85
  <p class="section-desc">Each project is a case study with real sessions, real decisions, and real stats.</p>
86
86
 
87
87
  <div class="projects-grid">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project kinetic" data-render-version="2" data-template="kinetic"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project kinetic" data-render-version="2" data-template="kinetic"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Hero {%- endcomment -%}
4
4
  <section class="hero hero--project" aria-label="Project header">
@@ -18,6 +18,9 @@
18
18
  --font-display: 'Space Grotesk', sans-serif;
19
19
  --font-body: 'Inter', -apple-system, sans-serif;
20
20
  --font-mono: 'IBM Plex Mono', monospace;
21
+ /* Scale up on wide monitors (wall-mounted 2k+) instead of leaving a dead
22
+ band on each side, but keep a ceiling so line length doesn't go wild. */
23
+ --content-max: min(2000px, calc(100vw - 4rem));
21
24
  }
22
25
 
23
26
  body {
@@ -66,6 +69,9 @@ body {
66
69
  .kinetic .hero-content {
67
70
  position: relative;
68
71
  z-index: 1;
72
+ width: 100%;
73
+ max-width: var(--content-max);
74
+ margin: 0 auto;
69
75
  }
70
76
  .kinetic .eyebrow {
71
77
  display: inline-flex; align-items: center; gap: 0.75rem;
@@ -167,11 +173,11 @@ body {
167
173
  .kinetic .stats-row {
168
174
  display: flex;
169
175
  gap: 2.5rem;
170
- padding: 2rem 0;
176
+ padding: 2rem 2.5rem;
171
177
  border-top: 1px solid var(--border);
172
178
  border-bottom: 1px solid var(--border);
173
179
  margin: 0 auto;
174
- max-width: 900px;
180
+ max-width: var(--content-max);
175
181
  }
176
182
  .kinetic .stat-item {
177
183
  display: flex;
@@ -251,7 +257,7 @@ body {
251
257
  /* ── Sections ── */
252
258
  .kinetic .section {
253
259
  padding: 6rem 2.5rem;
254
- max-width: 1100px;
260
+ max-width: var(--content-max);
255
261
  margin: 0 auto;
256
262
  }
257
263
  .kinetic .section--flush { padding-top: 0; }
@@ -507,7 +513,7 @@ body {
507
513
  /* ── Project hero (project page) ── */
508
514
  .kinetic .hero--project {
509
515
  min-height: 60vh;
510
- max-width: 1100px;
516
+ max-width: var(--content-max);
511
517
  margin: 0 auto;
512
518
  }
513
519
  .kinetic .hero--project h1 {
@@ -527,7 +533,7 @@ body {
527
533
  padding: 2rem 2.5rem;
528
534
  border-top: 1px solid var(--border);
529
535
  border-bottom: 1px solid var(--border);
530
- max-width: 1100px;
536
+ max-width: var(--content-max);
531
537
  margin: 0 auto;
532
538
  }
533
539
 
@@ -674,7 +680,7 @@ body {
674
680
  padding: 8rem 2.5rem 3rem;
675
681
  position: relative;
676
682
  overflow: hidden;
677
- max-width: 1100px;
683
+ max-width: var(--content-max);
678
684
  margin: 0 auto;
679
685
  }
680
686
  .kinetic .hero--session h1 {
@@ -704,7 +710,7 @@ body {
704
710
 
705
711
  /* ── Dev take section ── */
706
712
  .kinetic .dev-take-section {
707
- max-width: 1100px;
713
+ max-width: var(--content-max);
708
714
  margin: 0 auto;
709
715
  padding: 0 2.5rem 3rem;
710
716
  }
@@ -714,7 +720,7 @@ body {
714
720
  display: grid;
715
721
  grid-template-columns: 1fr 320px;
716
722
  gap: 2.5rem;
717
- max-width: 1100px;
723
+ max-width: var(--content-max);
718
724
  margin: 0 auto;
719
725
  padding: 0 2.5rem 6rem;
720
726
  }
@@ -900,7 +906,7 @@ body {
900
906
  border-top: 1px solid var(--border);
901
907
  display: flex; justify-content: space-between;
902
908
  font-size: 0.75rem; color: var(--text-3);
903
- max-width: 1100px; margin: 0 auto;
909
+ max-width: var(--content-max); margin: 0 auto;
904
910
  }
905
911
  .kinetic .footer a { color: var(--text-3); }
906
912
  .kinetic .footer a:hover { color: var(--orange); }
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project meridian" data-render-version="2" data-template="meridian"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project meridian" data-render-version="2" data-template="meridian"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Topographic contour background {%- endcomment -%}
4
4
  <div class="topo-bg" aria-hidden="true">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project minimal" data-render-version="2" data-template="minimal"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project minimal" data-render-version="2" data-template="minimal"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="mn-project">
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project mono" data-render-version="2" data-template="mono"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project mono" data-render-version="2" data-template="mono"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="mono-page">
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project neon" data-render-version="2" data-template="neon"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project neon" data-render-version="2" data-template="neon"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="grid-bg" aria-hidden="true"></div>
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="noir-page" data-render-version="2" data-template="noir"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="noir-page" data-render-version="2" data-template="noir"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <!-- Breadcrumb -->
4
4
  <nav class="noir-breadcrumb" aria-label="Breadcrumb">
@@ -1,4 +1,4 @@
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 }}">
1
+ <div class="heyiam-project obsidian" data-render-version="2" data-template="obsidian"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <a href="#main" class="skip-link">Skip to content</a>
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project paper" data-render-version="2" data-template="paper"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project paper" data-render-version="2" data-template="paper"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Masthead {%- endcomment -%}
4
4
  <header class="masthead fade-in fade-in-d1" role="banner">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project parallax" data-render-version="2" data-template="parallax"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project parallax" data-render-version="2" data-template="parallax"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <a href="#main-content" class="skip-link">Skip to content</a>
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project parchment" data-render-version="2" data-template="parchment"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project parchment" data-render-version="2" data-template="parchment"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
  <a href="#main-content" class="skip-link">Skip to content</a>
3
3
 
4
4
  <div class="page-wrapper">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project" data-render-version="2"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project" data-render-version="2"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Title {%- endcomment -%}
4
4
  <h1 class="project-title" data-editable="title">{{ project.title }}</h1>
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project radar" data-render-version="2" data-template="radar"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project radar" data-render-version="2" data-template="radar"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Breadcrumb {%- endcomment -%}
4
4
  <nav class="radar-breadcrumb" aria-label="Breadcrumb">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project showcase" data-render-version="2" data-template="showcase"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project showcase" data-render-version="2" data-template="showcase"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="sc-page">
4
4
  <main id="main-content">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project signal" data-render-version="2" data-template="signal"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project signal" data-render-version="2" data-template="signal"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <a href="#main" class="skip-link">Skip to content</a>
4
4
 
@@ -1,4 +1,4 @@
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 }}">
1
+ <div class="strata-project" data-render-version="2" data-template="strata"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Title {%- endcomment -%}
4
4
  <h1 class="strata-title" data-editable="title">{{ project.title }}</h1>
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project terminal" data-render-version="2" data-template="terminal"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project terminal" data-render-version="2" data-template="terminal"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  {%- comment -%} Prompt-style breadcrumb {%- endcomment -%}
4
4
  <div class="term-prompt">
@@ -1,4 +1,4 @@
1
- <div class="heyiam-project verdant" data-render-version="2" data-template="verdant"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="heyiam-project verdant" data-render-version="2" data-template="verdant"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <div class="vd-page">
4
4
 
@@ -1,4 +1,4 @@
1
- <div class="zen-project-page" data-render-version="2" data-template="zen"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
1
+ <div class="zen-project-page" data-render-version="2" data-template="zen"{% if sessionBaseUrl %} data-session-base-url="{{ sessionBaseUrl }}"{% endif %} data-session-suffix="{{ sessionSuffix }}" data-username="{{ user.username }}" data-project-slug="{{ project.slug }}">
2
2
 
3
3
  <main class="zen-container">
4
4
 
@@ -12,7 +12,7 @@ import { toSlug } from '../format-utils.js';
12
12
  * Mirrors the preview route's assembly logic. Projects that fail to load are
13
13
  * silently skipped — the portfolio still publishes with whatever succeeds.
14
14
  */
15
- export async function buildPortfolioRenderData(ctx, auth) {
15
+ export async function buildPortfolioRenderData(ctx, auth, opts = {}) {
16
16
  const profile = getPortfolioProfile();
17
17
  const allRawProjects = await ctx.getProjects();
18
18
  // Build a recency map from DB stats so the default-when-empty branch of
@@ -81,7 +81,7 @@ export async function buildPortfolioRenderData(ctx, auth) {
81
81
  status: 'active',
82
82
  email: profile.email,
83
83
  phone: profile.phone,
84
- photoUrl: profile.photoBase64 || undefined,
84
+ photoUrl: opts.photoUrlOverride || profile.photoBase64 || undefined,
85
85
  linkedinUrl: profile.linkedinUrl,
86
86
  githubUrl: profile.githubUrl,
87
87
  twitterHandle: profile.twitterHandle,
@@ -191,6 +191,7 @@ async function buildProjectPreviewData(ctx, projectParam, queryOverrides) {
191
191
  sessionCards,
192
192
  allSessionCards,
193
193
  sessionBaseUrl: `/preview/project/${encodeURIComponent(projectParam)}/session`,
194
+ sessionSuffix: '.html',
194
195
  });
195
196
  const result = { renderData, enhanceResult, projName: projAny.name };
196
197
  // Cache the result (template-agnostic data, re-rendered cheaply per template)
@@ -414,18 +415,33 @@ body { overflow: auto !important; min-height: auto !important; }
414
415
  router.get('/preview/project/:project/session/:sessionId', async (req, res) => {
415
416
  try {
416
417
  const projectParam = String(req.params.project);
417
- const sessionId = String(req.params.sessionId);
418
+ // Anchor hrefs in the project preview are built from the session slug
419
+ // (title-derived) with a ".html" suffix, but the CLI session files are
420
+ // keyed by Claude UUID. Strip the suffix and match by either.
421
+ const rawParam = String(req.params.sessionId).replace(/\.html$/, '');
418
422
  const rawProjects = await ctx.getProjects();
419
423
  const rawProj = rawProjects.find((p) => p.name === projectParam || p.dirName === projectParam);
420
424
  if (!rawProj) {
421
425
  res.status(404).send('Project not found');
422
426
  return;
423
427
  }
424
- const meta = rawProj.sessions.find((s) => s.sessionId === sessionId);
428
+ // The project preview's anchor hrefs use the title-derived slug produced
429
+ // by rowToCard() above, not the Claude UUID. Resolve either form here.
430
+ const rows = getSessionsByProject(ctx.db, rawProj.dirName);
431
+ const rowById = new Map(rows.map((r) => [r.id, r]));
432
+ const meta = rawProj.sessions.find((s) => {
433
+ if (s.sessionId === rawParam)
434
+ return true;
435
+ const row = rowById.get(s.sessionId);
436
+ const enh = loadEnhancedData(s.sessionId);
437
+ const title = enh?.title ?? row?.title ?? s.sessionId;
438
+ return toSlug(title, 80) === rawParam;
439
+ });
425
440
  if (!meta) {
426
441
  res.status(404).send('Session not found');
427
442
  return;
428
443
  }
444
+ const sessionId = meta.sessionId;
429
445
  const auth = getAuthToken();
430
446
  const session = await ctx.loadSession(meta.path, rawProj.name, sessionId);
431
447
  const enhanced = loadEnhancedData(sessionId);
@@ -12,7 +12,8 @@ import { buildAgentSummary } from './context.js';
12
12
  import { toSlug } from '../format-utils.js';
13
13
  import { getFileCountWithChildren } from '../db.js';
14
14
  export async function uploadSelectedSessions(ctx, auth, options) {
15
- const { proj, projectData, selectedSessionIds, send } = options;
15
+ const { proj, projectData, selectedSessionIds, sessionStatus, send } = options;
16
+ const shareStatus = sessionStatus ?? 'unlisted';
16
17
  const notify = send ?? ((_evt) => { });
17
18
  let uploadedCount = 0;
18
19
  const failedSessions = [];
@@ -79,7 +80,7 @@ export async function uploadSelectedSessions(ctx, auth, options) {
79
80
  project_name: proj.name,
80
81
  project_id: projectData.project_id,
81
82
  slug: sessionSlug,
82
- status: 'unlisted',
83
+ status: shareStatus,
83
84
  source_tool: sessionSourceTool,
84
85
  agent_summary: agentSummary,
85
86
  rendered_html: sessionRenderedHtml,
@@ -123,7 +124,7 @@ export async function uploadSelectedSessions(ctx, auth, options) {
123
124
  slug: sessionSlug,
124
125
  project_name: proj.name,
125
126
  narrative: sessionNarrative,
126
- status: 'unlisted',
127
+ status: shareStatus,
127
128
  raw_log: [],
128
129
  execution_path: (enhanced?.executionSteps ?? session.executionPath ?? []).map((s, i) => ({
129
130
  label: s.title ?? `Step ${i + 1}`,