heyiam 0.3.4 → 0.3.7

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 (107) hide show
  1. package/dist/export.js +19 -5
  2. package/dist/llm/project-enhance.js +11 -5
  3. package/dist/mount.js +9 -7
  4. package/dist/public/assets/{index-Cq04whgG.js → index-BDh4ne9u.js} +3 -3
  5. package/dist/public/assets/index-ByoBtx7P.css +1 -0
  6. package/dist/public/index.html +2 -2
  7. package/dist/render/build-render-data.js +1 -0
  8. package/dist/render/liquid.js +12 -2
  9. package/dist/render/mock-data.js +6 -0
  10. package/dist/render/select-profile-skills.js +54 -0
  11. package/dist/render/templates/aurora/portfolio.liquid +4 -4
  12. package/dist/render/templates/aurora/project.liquid +1 -1
  13. package/dist/render/templates/aurora/styles.css +6 -6
  14. package/dist/render/templates/bauhaus/portfolio.liquid +4 -4
  15. package/dist/render/templates/bauhaus/project.liquid +1 -1
  16. package/dist/render/templates/bauhaus/styles.css +1 -1
  17. package/dist/render/templates/blueprint/portfolio.liquid +4 -4
  18. package/dist/render/templates/blueprint/project.liquid +1 -1
  19. package/dist/render/templates/blueprint/styles.css +1 -1
  20. package/dist/render/templates/canvas/portfolio.liquid +4 -4
  21. package/dist/render/templates/canvas/project.liquid +1 -1
  22. package/dist/render/templates/canvas/styles.css +2 -2
  23. package/dist/render/templates/carbon/portfolio.liquid +4 -4
  24. package/dist/render/templates/carbon/project.liquid +1 -1
  25. package/dist/render/templates/carbon/styles.css +7 -7
  26. package/dist/render/templates/chalk/portfolio.liquid +4 -4
  27. package/dist/render/templates/chalk/project.liquid +1 -1
  28. package/dist/render/templates/chalk/styles.css +44 -2
  29. package/dist/render/templates/circuit/portfolio.liquid +4 -4
  30. package/dist/render/templates/circuit/project.liquid +1 -1
  31. package/dist/render/templates/cosmos/portfolio.liquid +4 -4
  32. package/dist/render/templates/cosmos/project.liquid +1 -1
  33. package/dist/render/templates/cosmos/styles.css +4 -4
  34. package/dist/render/templates/daylight/portfolio.liquid +4 -4
  35. package/dist/render/templates/daylight/project.liquid +1 -1
  36. package/dist/render/templates/editorial/portfolio.liquid +4 -4
  37. package/dist/render/templates/editorial/project.liquid +1 -1
  38. package/dist/render/templates/editorial/styles.css +6 -1
  39. package/dist/render/templates/ember/portfolio.liquid +4 -4
  40. package/dist/render/templates/ember/project.liquid +1 -1
  41. package/dist/render/templates/ember/styles.css +2 -2
  42. package/dist/render/templates/glacier/portfolio.liquid +4 -4
  43. package/dist/render/templates/glacier/project.liquid +1 -1
  44. package/dist/render/templates/glacier/styles.css +2 -2
  45. package/dist/render/templates/grid/portfolio.liquid +4 -4
  46. package/dist/render/templates/grid/project.liquid +1 -1
  47. package/dist/render/templates/grid/styles.css +2 -2
  48. package/dist/render/templates/kinetic/portfolio.liquid +5 -5
  49. package/dist/render/templates/kinetic/project.liquid +1 -1
  50. package/dist/render/templates/kinetic/styles.css +20 -14
  51. package/dist/render/templates/meridian/portfolio.liquid +4 -4
  52. package/dist/render/templates/meridian/project.liquid +1 -1
  53. package/dist/render/templates/meridian/styles.css +1 -1
  54. package/dist/render/templates/minimal/portfolio.liquid +3 -3
  55. package/dist/render/templates/minimal/project.liquid +1 -1
  56. package/dist/render/templates/mono/portfolio.liquid +4 -4
  57. package/dist/render/templates/mono/project.liquid +1 -1
  58. package/dist/render/templates/mono/styles.css +15 -1
  59. package/dist/render/templates/neon/portfolio.liquid +4 -4
  60. package/dist/render/templates/neon/project.liquid +1 -1
  61. package/dist/render/templates/neon/styles.css +11 -20
  62. package/dist/render/templates/noir/portfolio.liquid +4 -4
  63. package/dist/render/templates/noir/project.liquid +1 -1
  64. package/dist/render/templates/noir/styles.css +5 -5
  65. package/dist/render/templates/obsidian/portfolio.liquid +4 -4
  66. package/dist/render/templates/obsidian/project.liquid +1 -1
  67. package/dist/render/templates/obsidian/styles.css +2 -2
  68. package/dist/render/templates/paper/portfolio.liquid +4 -4
  69. package/dist/render/templates/paper/project.liquid +1 -1
  70. package/dist/render/templates/paper/styles.css +60 -1
  71. package/dist/render/templates/parallax/portfolio.liquid +4 -4
  72. package/dist/render/templates/parallax/project.liquid +1 -1
  73. package/dist/render/templates/parallax/styles.css +1 -1
  74. package/dist/render/templates/parchment/portfolio.liquid +4 -4
  75. package/dist/render/templates/parchment/project.liquid +1 -1
  76. package/dist/render/templates/parchment/styles.css +3 -3
  77. package/dist/render/templates/project.liquid +1 -1
  78. package/dist/render/templates/radar/portfolio.liquid +4 -4
  79. package/dist/render/templates/radar/project.liquid +1 -1
  80. package/dist/render/templates/radar/styles.css +4 -4
  81. package/dist/render/templates/showcase/portfolio.liquid +4 -4
  82. package/dist/render/templates/showcase/project.liquid +1 -1
  83. package/dist/render/templates/showcase/styles.css +7 -7
  84. package/dist/render/templates/signal/portfolio.liquid +4 -4
  85. package/dist/render/templates/signal/project.liquid +1 -1
  86. package/dist/render/templates/signal/styles.css +5 -5
  87. package/dist/render/templates/strata/portfolio.liquid +4 -4
  88. package/dist/render/templates/strata/project.liquid +1 -1
  89. package/dist/render/templates/strata/styles.css +3 -3
  90. package/dist/render/templates/styles.css +39 -28
  91. package/dist/render/templates/terminal/portfolio.liquid +2 -2
  92. package/dist/render/templates/terminal/project.liquid +1 -1
  93. package/dist/render/templates/verdant/portfolio.liquid +4 -4
  94. package/dist/render/templates/verdant/project.liquid +1 -1
  95. package/dist/render/templates/verdant/styles.css +17 -2
  96. package/dist/render/templates/zen/portfolio.liquid +4 -4
  97. package/dist/render/templates/zen/project.liquid +1 -1
  98. package/dist/render/templates/zen/styles.css +8 -8
  99. package/dist/routes/export.js +1 -0
  100. package/dist/routes/github.js +1 -1
  101. package/dist/routes/portfolio-render-data.js +17 -3
  102. package/dist/routes/preview.js +33 -3
  103. package/dist/routes/project-session-upload.js +4 -3
  104. package/dist/routes/publish.js +120 -17
  105. package/dist/settings.js +14 -1
  106. package/package.json +1 -1
  107. package/dist/public/assets/index-CMyamplX.css +0 -1
package/dist/export.js CHANGED
@@ -42,8 +42,10 @@ function resolveScreenshotDataUri(dirName, cache) {
42
42
  return b64;
43
43
  return `data:image/png;base64,${b64}`;
44
44
  }
45
- // Try local screenshot file
46
- const slug = toSlug(dirName);
45
+ // Try local screenshot file. Screenshots are keyed by the clean slug that
46
+ // the publish flow uses (toSlug(displayNameFromDir(dirName))), not the raw
47
+ // encoded path.
48
+ const slug = toSlug(displayNameFromDir(dirName));
47
49
  const screenshotPath = join(SCREENSHOTS_DIR, `${slug}.png`);
48
50
  if (existsSync(screenshotPath)) {
49
51
  const buf = readFileSync(screenshotPath);
@@ -207,8 +209,12 @@ export async function exportHtml(dirName, cache, sessions, outputPath, username
207
209
  let totalBytes = 0;
208
210
  mkdirSync(outputPath, { recursive: true });
209
211
  const { result } = cache;
210
- const slug = slugify(dirName);
212
+ // dirName is Claude Code's encoded full path (e.g. `-Users-ben-Dev-agent-sync`).
213
+ // The Phoenix-side slug comes from `toSlug(displayNameFromDir(dirName))`, so
214
+ // match that here — otherwise anchor hrefs and zip folder names keep the
215
+ // entire encoded path.
211
216
  const title = opts?.title ?? cache.title ?? displayNameFromDir(dirName);
217
+ const slug = slugify(displayNameFromDir(dirName));
212
218
  // Use ALL sessions (same as dashboard), build cards for each
213
219
  const sessionCards = sessions.map((session) => {
214
220
  return buildSessionCard({
@@ -259,6 +265,7 @@ export async function exportHtml(dirName, cache, sessions, outputPath, username
259
265
  totalTokens,
260
266
  sessionCards,
261
267
  sessionBaseUrl: './sessions',
268
+ sessionSuffix: '.html',
262
269
  });
263
270
  const templateName = resolveTemplate(undefined, getDefaultTemplate());
264
271
  const projectBody = renderProjectHtml(projectRenderData, {
@@ -311,8 +318,12 @@ function getInlineCss() {
311
318
  // ── Shared render helpers ─────────────────────────────────────
312
319
  function buildProjectRenderInputs(dirName, cache, sessions, username, opts) {
313
320
  const { result } = cache;
314
- const slug = slugify(dirName);
321
+ // dirName is Claude Code's encoded full path (e.g. `-Users-ben-Dev-agent-sync`).
322
+ // The Phoenix-side slug comes from `toSlug(displayNameFromDir(dirName))`, so
323
+ // match that here — otherwise anchor hrefs and zip folder names keep the
324
+ // entire encoded path.
315
325
  const title = opts?.title ?? cache.title ?? displayNameFromDir(dirName);
326
+ const slug = slugify(displayNameFromDir(dirName));
316
327
  const sessionCards = sessions.map((session) => buildSessionCard({
317
328
  sessionId: session.id,
318
329
  session,
@@ -356,6 +367,8 @@ export function generateProjectHtmlFragment(dirName, cache, sessions, username =
356
367
  totalSessions: sessions.length,
357
368
  totalLoc, totalDurationMinutes, totalAgentDurationMinutes, totalFilesChanged, totalTokens,
358
369
  sessionCards,
370
+ sessionBaseUrl: `/${username}/${slug}`,
371
+ sessionSuffix: '',
359
372
  });
360
373
  const templateName = resolveTemplate(undefined, getDefaultTemplate());
361
374
  return renderProjectHtml(renderData, {
@@ -380,6 +393,7 @@ export function generateHtmlFiles(dirName, cache, sessions, username = 'local',
380
393
  totalLoc, totalDurationMinutes, totalAgentDurationMinutes, totalFilesChanged, totalTokens,
381
394
  sessionCards,
382
395
  sessionBaseUrl: './sessions',
396
+ sessionSuffix: '.html',
383
397
  });
384
398
  const projectBody = renderProjectHtml(projectRenderData, {
385
399
  arc: result.arc,
@@ -633,7 +647,7 @@ export async function generatePortfolioSite(portfolioData, projects, outputDir,
633
647
  const projectsRoot = join(outputDir, 'projects');
634
648
  mkdirSync(projectsRoot, { recursive: true });
635
649
  for (const p of projects) {
636
- const projectSlug = slugify(p.dirName);
650
+ const projectSlug = slugify(displayNameFromDir(p.dirName));
637
651
  const projectDir = join(projectsRoot, projectSlug);
638
652
  const result = await exportHtml(p.dirName, p.cache, p.sessions, projectDir, username, p.opts);
639
653
  files.push(...result.files);
@@ -4,11 +4,12 @@ import { getAnthropicApiKey } from '../settings.js';
4
4
  const PROJECT_ENHANCE_SYSTEM = `You are building a project narrative from multiple coding sessions for a developer portfolio on heyi.am.
5
5
 
6
6
  Your job:
7
- 1. Synthesize a 2-3 sentence project description that captures what was built and why it matters. Write in third person about the project, not the developer. No fluff every word earns its place.
8
- 2. Identify 4-7 project phases (the "arc") that show how the project evolved. Each phase should have a short title and one-sentence description.
9
- 3. Deduplicate and rank skills across all sessions.
10
- 4. Group sessions into timeline periods (e.g., "Week 1", "Days 1-3") with labels describing what happened in each period. Mark featured sessions (the most interesting ones) vs background sessions.
11
- 5. Generate 2-3 context-aware questions based on patterns you detect in the sessions (see instructions below).
7
+ 1. Write a tagline: 1-2 sentences, first-person, starting with "I built". State what the thing IS and the one thing that makes it interesting. Example voice: "I built a better NVR system that's simple and intuitive." / "I built a proof of work for the AI space." Not a pitch, not marketing — how a developer describes what they made when asked at a bar. No fluff words (leverage, robust, comprehensive, cutting-edge, seamless, innovative). If you can't name what it IS in concrete terms, the tagline is wrong.
8
+ 2. Synthesize a 2-3 sentence project description that captures what was built and why it matters. Write in third person about the project, not the developer. No fluff every word earns its place.
9
+ 3. Identify 4-7 project phases (the "arc") that show how the project evolved. Each phase should have a short title and one-sentence description.
10
+ 4. Deduplicate and rank skills across all sessions.
11
+ 5. Group sessions into timeline periods (e.g., "Week 1", "Days 1-3") with labels describing what happened in each period. Mark featured sessions (the most interesting ones) vs background sessions.
12
+ 6. Generate 2-3 context-aware questions based on patterns you detect in the sessions (see instructions below).
12
13
 
13
14
  For questions, look for these signals and generate questions that reference specific data:
14
15
  - High correction counts → ask about override strategy, referencing the count
@@ -24,6 +25,7 @@ Each question must have:
24
25
 
25
26
  Return valid JSON matching this exact structure:
26
27
  {
28
+ "tagline": "I built a ... — 1-2 sentences, first-person, concrete, no fluff",
27
29
  "narrative": "2-3 sentence project description",
28
30
  "arc": [{ "phase": 1, "title": "...", "description": "..." }],
29
31
  "skills": ["skill1", "skill2"],
@@ -183,6 +185,10 @@ export async function enhanceProject(sessions, skippedSessions, onProgress) {
183
185
  if (!result.narrative || !Array.isArray(result.arc) || !Array.isArray(result.skills)) {
184
186
  throw new Error('LLM returned incomplete project enhance result');
185
187
  }
188
+ // Tagline is new; tolerate legacy cache entries by defaulting to empty string.
189
+ if (typeof result.tagline !== 'string') {
190
+ result.tagline = '';
191
+ }
186
192
  // Ensure questions have IDs
187
193
  if (result.questions) {
188
194
  result.questions = result.questions.map((q, i) => ({
package/dist/mount.js CHANGED
@@ -21961,7 +21961,7 @@
21961
21961
  count
21962
21962
  }));
21963
21963
  }
21964
- function buildLegendEntries(segments, sessionRanges) {
21964
+ function buildLegendEntries(sessionRanges) {
21965
21965
  return sessionRanges.map((r) => {
21966
21966
  const kids = getChildren(r.session);
21967
21967
  return {
@@ -22297,7 +22297,7 @@
22297
22297
  const concurrentLimit = expanded ? 999 : DEFAULT_MAX_CONCURRENT;
22298
22298
  const themeColors = (0, import_react.useMemo)(() => ({ main: mainColor, muted: textMuted }), [mainColor, textMuted]);
22299
22299
  const L = (0, import_react.useMemo)(() => layoutSegments(segments, concurrentLimit, themeColors), [segments, concurrentLimit, themeColors]);
22300
- const legendEntries = (0, import_react.useMemo)(() => buildLegendEntries(segments, L.sessionRanges), [segments, L.sessionRanges]);
22300
+ const legendEntries = (0, import_react.useMemo)(() => buildLegendEntries(L.sessionRanges), [L.sessionRanges]);
22301
22301
  const scrollRef = (0, import_react.useRef)(null);
22302
22302
  const [hovered, setHovered] = (0, import_react.useState)(null);
22303
22303
  const [focusedEntry, setFocusedEntry] = (0, import_react.useState)(null);
@@ -23135,22 +23135,24 @@
23135
23135
  }
23136
23136
  var allSessions = /* @__PURE__ */ new Map();
23137
23137
  var showOverlay = null;
23138
- function OverlayRoot({ sessions }) {
23138
+ function OverlayRoot() {
23139
23139
  const [active, setActive] = (0, import_react3.useState)(null);
23140
23140
  showOverlay = (session) => setActive(session);
23141
23141
  if (!active) return null;
23142
23142
  const projectEl = document.querySelector(".heyiam-project");
23143
23143
  const baseUrl = projectEl?.getAttribute("data-session-base-url");
23144
+ const suffix = projectEl?.getAttribute("data-session-suffix") ?? "";
23144
23145
  let sessionPageUrl;
23145
23146
  if (baseUrl) {
23146
- const slug = active.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80) || "untitled";
23147
- sessionPageUrl = `${baseUrl}/${slug}.html`;
23147
+ const fromTitle = active.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80) || "untitled";
23148
+ const sessionSlug = active.slug ?? fromTitle;
23149
+ sessionPageUrl = `${baseUrl}/${sessionSlug}${suffix}`;
23148
23150
  } else {
23149
23151
  const username = projectEl?.getAttribute("data-username");
23150
23152
  const projectSlug = projectEl?.getAttribute("data-project-slug");
23151
23153
  const sessionSlug = active.slug;
23152
23154
  if (username && projectSlug && sessionSlug) {
23153
- sessionPageUrl = `/@${username}/${projectSlug}/${sessionSlug}`;
23155
+ sessionPageUrl = `/${username}/${projectSlug}/${sessionSlug}`;
23154
23156
  }
23155
23157
  }
23156
23158
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -23219,7 +23221,7 @@
23219
23221
  overlayEl.id = "heyiam-overlay-root";
23220
23222
  document.body.appendChild(overlayEl);
23221
23223
  (0, import_client.createRoot)(overlayEl).render(
23222
- import_react3.default.createElement(OverlayRoot, { sessions: allSessions })
23224
+ import_react3.default.createElement(OverlayRoot)
23223
23225
  );
23224
23226
  }
23225
23227
  }