heyiam 0.3.7 → 0.3.10
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.
- package/dist/archive.js +28 -0
- package/dist/db.js +9 -0
- package/dist/export.js +3 -0
- package/dist/index.js +27 -10
- package/dist/mount.js +43 -18
- package/dist/public/assets/index-DU5On5Al.js +37 -0
- package/dist/public/index.html +1 -1
- package/dist/render/build-render-data.js +1 -0
- package/dist/render/liquid.js +14 -1
- package/dist/render/templates/bauhaus/project.liquid +2 -2
- package/dist/render/templates/editorial/project.liquid +1 -1
- package/dist/render/templates/glacier/project.liquid +3 -3
- package/dist/render/templates/kinetic/project.liquid +1 -1
- package/dist/render/templates/minimal/project.liquid +1 -1
- package/dist/render/templates/paper/project.liquid +2 -2
- package/dist/render/templates/partials/_work-timeline.liquid +1 -1
- package/dist/render/templates/project.liquid +1 -1
- package/dist/render/templates/radar/project.liquid +1 -1
- package/dist/render/templates/showcase/project.liquid +1 -1
- package/dist/render/templates/terminal/project.liquid +1 -1
- package/dist/routes/context.js +26 -3
- package/dist/routes/delete.js +18 -1
- package/dist/routes/enhance.js +10 -3
- package/dist/routes/preview.js +16 -0
- package/dist/routes/project-session-upload.js +63 -1
- package/dist/routes/projects.js +8 -1
- package/dist/routes/publish.js +34 -5
- package/dist/routes/sessions.js +36 -11
- package/dist/settings.js +2 -0
- package/package.json +1 -1
- package/dist/public/assets/index-BDh4ne9u.js +0 -37
package/dist/archive.js
CHANGED
|
@@ -64,6 +64,34 @@ function archiveDestination(originalPath, archiveBase, projectDir) {
|
|
|
64
64
|
// Codex/Gemini: use projectDir + filename
|
|
65
65
|
return join(archiveBase, projectDir, basename(originalPath));
|
|
66
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Public version of archiveDestination — given an original tool path,
|
|
69
|
+
* return the deterministic archive location.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveArchivePath(originalPath, projectDir, configDir) {
|
|
72
|
+
const archiveBase = getArchiveDir(configDir);
|
|
73
|
+
if (originalPath.startsWith(archiveBase + "/"))
|
|
74
|
+
return originalPath;
|
|
75
|
+
return archiveDestination(originalPath, archiveBase, projectDir);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Return a readable path for a session — the original if it still exists,
|
|
79
|
+
* otherwise the archive copy if it exists. Returns null if neither exists.
|
|
80
|
+
*
|
|
81
|
+
* Use this when reading session content by stored path: tool dirs (e.g.
|
|
82
|
+
* Claude Code) garbage-collect their own files, but the archive is under
|
|
83
|
+
* our control and survives.
|
|
84
|
+
*/
|
|
85
|
+
export async function findReadableSessionPath(originalPath, projectDir, configDir) {
|
|
86
|
+
if (await stat(originalPath).catch(() => null))
|
|
87
|
+
return originalPath;
|
|
88
|
+
const archivePath = resolveArchivePath(originalPath, projectDir, configDir);
|
|
89
|
+
if (archivePath === originalPath)
|
|
90
|
+
return null;
|
|
91
|
+
if (await stat(archivePath).catch(() => null))
|
|
92
|
+
return archivePath;
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
67
95
|
/**
|
|
68
96
|
* Archive a Cursor session by exporting its parsed data as JSONL.
|
|
69
97
|
* Cursor stores conversations in its own SQLite DB — we can't hard-link.
|
package/dist/db.js
CHANGED
|
@@ -374,6 +374,15 @@ export function deleteSession(db, sessionId) {
|
|
|
374
374
|
});
|
|
375
375
|
tx();
|
|
376
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Update the stored file_path for a session. Used to heal the cache after
|
|
379
|
+
* we discover the originally-indexed path has been deleted by its source
|
|
380
|
+
* tool (e.g., Claude Code's 30-day cleanup) and we've fallen back to an
|
|
381
|
+
* archived copy.
|
|
382
|
+
*/
|
|
383
|
+
export function updateSessionPath(db, sessionId, newPath) {
|
|
384
|
+
db.prepare('UPDATE sessions SET file_path = ? WHERE id = ?').run(newPath, sessionId);
|
|
385
|
+
}
|
|
377
386
|
// ── Rebuild Index ────────────────────────────────────────────
|
|
378
387
|
export function rebuildIndex(db, onProgress) {
|
|
379
388
|
const tx = db.transaction(() => {
|
package/dist/export.js
CHANGED
|
@@ -266,6 +266,7 @@ export async function exportHtml(dirName, cache, sessions, outputPath, username
|
|
|
266
266
|
sessionCards,
|
|
267
267
|
sessionBaseUrl: './sessions',
|
|
268
268
|
sessionSuffix: '.html',
|
|
269
|
+
hideSessionDates: cache.hideSessionDates,
|
|
269
270
|
});
|
|
270
271
|
const templateName = resolveTemplate(undefined, getDefaultTemplate());
|
|
271
272
|
const projectBody = renderProjectHtml(projectRenderData, {
|
|
@@ -369,6 +370,7 @@ export function generateProjectHtmlFragment(dirName, cache, sessions, username =
|
|
|
369
370
|
sessionCards,
|
|
370
371
|
sessionBaseUrl: `/${username}/${slug}`,
|
|
371
372
|
sessionSuffix: '',
|
|
373
|
+
hideSessionDates: cache.hideSessionDates,
|
|
372
374
|
});
|
|
373
375
|
const templateName = resolveTemplate(undefined, getDefaultTemplate());
|
|
374
376
|
return renderProjectHtml(renderData, {
|
|
@@ -394,6 +396,7 @@ export function generateHtmlFiles(dirName, cache, sessions, username = 'local',
|
|
|
394
396
|
sessionCards,
|
|
395
397
|
sessionBaseUrl: './sessions',
|
|
396
398
|
sessionSuffix: '.html',
|
|
399
|
+
hideSessionDates: cache.hideSessionDates,
|
|
397
400
|
});
|
|
398
401
|
const projectBody = renderProjectHtml(projectRenderData, {
|
|
399
402
|
arc: result.arc,
|
package/dist/index.js
CHANGED
|
@@ -23,14 +23,7 @@ program
|
|
|
23
23
|
.name('heyiam')
|
|
24
24
|
.description('Turn AI coding sessions into portfolio case studies')
|
|
25
25
|
.version(pkg.version);
|
|
26
|
-
|
|
27
|
-
.command('open')
|
|
28
|
-
.description('Start the local server and open the browser')
|
|
29
|
-
.option('-p, --port <number>', 'Port to run on', '17845')
|
|
30
|
-
.option('--no-open', 'Start server without opening browser')
|
|
31
|
-
.option('--demo', 'Start with fake data for screenshots and recordings')
|
|
32
|
-
.option('--verbose', 'Show detailed sync and discovery logs')
|
|
33
|
-
.action(async (opts) => {
|
|
26
|
+
async function runOpenServer(opts) {
|
|
34
27
|
if (opts.verbose)
|
|
35
28
|
process.env.HEYIAM_VERBOSE = '1';
|
|
36
29
|
const port = parseInt(opts.port, 10);
|
|
@@ -120,6 +113,23 @@ program
|
|
|
120
113
|
process.on('SIGINT', shutdown);
|
|
121
114
|
process.on('SIGTERM', shutdown);
|
|
122
115
|
setInterval(() => { }, 60_000);
|
|
116
|
+
}
|
|
117
|
+
program
|
|
118
|
+
.command('open')
|
|
119
|
+
.description('Start the local server and open the browser')
|
|
120
|
+
.option('-p, --port <number>', 'Port to run on', '17845')
|
|
121
|
+
.option('--no-open', 'Start server without opening browser')
|
|
122
|
+
.option('--demo', 'Start with fake data for screenshots and recordings')
|
|
123
|
+
.option('--verbose', 'Show detailed sync and discovery logs')
|
|
124
|
+
.action(runOpenServer);
|
|
125
|
+
program
|
|
126
|
+
.command('login')
|
|
127
|
+
.description('Sign in to heyiam.com (opens the dashboard in your browser)')
|
|
128
|
+
.option('-p, --port <number>', 'Port to run on', '17845')
|
|
129
|
+
.option('--verbose', 'Show detailed sync and discovery logs')
|
|
130
|
+
.action(async (opts) => {
|
|
131
|
+
console.log('\n Sign in from the dashboard that opens in your browser.\n');
|
|
132
|
+
await runOpenServer({ port: opts.port, open: true, verbose: opts.verbose });
|
|
123
133
|
});
|
|
124
134
|
program
|
|
125
135
|
.command('time')
|
|
@@ -847,9 +857,16 @@ const resolvedArgv = process.argv[1] ? realpathSync(process.argv[1]) : '';
|
|
|
847
857
|
const isDirectRun = resolvedArgv.endsWith('/dist/index.js') ||
|
|
848
858
|
resolvedArgv.endsWith('/src/index.ts');
|
|
849
859
|
if (isDirectRun) {
|
|
860
|
+
// Default to `open` when the user runs `heyiam` bare or `heyiam --flag …`,
|
|
861
|
+
// so `heyiam --port 1234` becomes `heyiam open --port 1234`. We only do
|
|
862
|
+
// this for *flags* (args starting with `-`) — bare unknown words like
|
|
863
|
+
// `heyiam blargh` should fall through to Commander so the user sees a
|
|
864
|
+
// clean "unknown command" error instead of a confusing "too many
|
|
865
|
+
// arguments for 'open'".
|
|
850
866
|
const args = process.argv.slice(2);
|
|
851
|
-
const
|
|
852
|
-
|
|
867
|
+
const firstArg = args[0];
|
|
868
|
+
const shouldDefaultToOpen = args.length === 0 || (firstArg !== undefined && firstArg.startsWith('-'));
|
|
869
|
+
if (shouldDefaultToOpen) {
|
|
853
870
|
process.argv.splice(2, 0, 'open');
|
|
854
871
|
}
|
|
855
872
|
program.parseAsync(process.argv).catch((err) => {
|
package/dist/mount.js
CHANGED
|
@@ -21929,11 +21929,11 @@
|
|
|
21929
21929
|
withTime.forEach((c, i) => laneMap.set(c.id, i));
|
|
21930
21930
|
return { laneMap, laneCount: withTime.length };
|
|
21931
21931
|
}
|
|
21932
|
-
function buildTooltip(s) {
|
|
21932
|
+
function buildTooltip(s, ordinalLabel) {
|
|
21933
21933
|
const kids = getChildren(s);
|
|
21934
21934
|
return {
|
|
21935
21935
|
title: s.title,
|
|
21936
|
-
timestamp: formatTimestamp(s.date),
|
|
21936
|
+
timestamp: ordinalLabel ?? formatTimestamp(s.date),
|
|
21937
21937
|
duration: formatDuration(s.wallClockMinutes ?? s.durationMinutes),
|
|
21938
21938
|
linesOfCode: s.linesOfCode,
|
|
21939
21939
|
agentCount: kids.length,
|
|
@@ -21961,12 +21961,12 @@
|
|
|
21961
21961
|
count
|
|
21962
21962
|
}));
|
|
21963
21963
|
}
|
|
21964
|
-
function buildLegendEntries(sessionRanges) {
|
|
21965
|
-
return sessionRanges.map((r) => {
|
|
21964
|
+
function buildLegendEntries(sessionRanges, hideDates) {
|
|
21965
|
+
return sessionRanges.map((r, i) => {
|
|
21966
21966
|
const kids = getChildren(r.session);
|
|
21967
21967
|
return {
|
|
21968
21968
|
title: r.session.title,
|
|
21969
|
-
timestamp: formatTimestamp(r.session.date),
|
|
21969
|
+
timestamp: hideDates ? `Session ${i + 1}` : formatTimestamp(r.session.date),
|
|
21970
21970
|
agents: aggregateAgents(kids),
|
|
21971
21971
|
totalAgents: kids.length,
|
|
21972
21972
|
xStart: r.xStart,
|
|
@@ -22003,7 +22003,7 @@
|
|
|
22003
22003
|
].join(" ");
|
|
22004
22004
|
}
|
|
22005
22005
|
var DEFAULT_MAX_CONCURRENT = 8;
|
|
22006
|
-
function layoutSegments(segments, maxConcurrent = DEFAULT_MAX_CONCURRENT, themeColors) {
|
|
22006
|
+
function layoutSegments(segments, maxConcurrent = DEFAULT_MAX_CONCURRENT, themeColors, hideDates) {
|
|
22007
22007
|
const _mainColor = themeColors?.main ?? MAIN_COLOR;
|
|
22008
22008
|
const _textMuted = themeColors?.muted ?? TEXT_MUTED;
|
|
22009
22009
|
const nodes = [];
|
|
@@ -22014,6 +22014,8 @@
|
|
|
22014
22014
|
const cY = 0;
|
|
22015
22015
|
let minY = 0, maxY = 0;
|
|
22016
22016
|
const threadStart = cx;
|
|
22017
|
+
let ordinalIdx = 0;
|
|
22018
|
+
const nextOrdinal = () => `Session ${++ordinalIdx}`;
|
|
22017
22019
|
const bound = (y, h) => {
|
|
22018
22020
|
if (y < minY) minY = y;
|
|
22019
22021
|
if (y + h > maxY) maxY = y + h;
|
|
@@ -22034,8 +22036,9 @@
|
|
|
22034
22036
|
const agentMinW = kids.length > 0 ? Math.max(MIN_W, kids.length * 30 + 100) : MIN_W;
|
|
22035
22037
|
const w = kids.length > 0 ? Math.min(Math.max(dur * PX_PER_MIN, agentMinW), MAX_CONCURRENT_W) : Math.min(Math.max(timeToPx(dur), MIN_W), MAX_W);
|
|
22036
22038
|
const sub = formatDuration(s.durationMinutes);
|
|
22037
|
-
const
|
|
22038
|
-
const
|
|
22039
|
+
const ordinal = hideDates ? nextOrdinal() : void 0;
|
|
22040
|
+
const tooltip = buildTooltip(s, ordinal);
|
|
22041
|
+
const ts = ordinal ?? formatTimestamp(s.date);
|
|
22039
22042
|
if (kids.length > 0) {
|
|
22040
22043
|
const visible = kids.slice(0, MAX_AGENTS);
|
|
22041
22044
|
const parentStartMs = sessionStart(s);
|
|
@@ -22108,8 +22111,9 @@
|
|
|
22108
22111
|
const lane = laneMap.get(s.id) ?? 0;
|
|
22109
22112
|
const trackY = cY + lane * dynamicTrackGap;
|
|
22110
22113
|
const kids = getChildren(s);
|
|
22111
|
-
const
|
|
22112
|
-
const
|
|
22114
|
+
const ordinal = hideDates ? nextOrdinal() : void 0;
|
|
22115
|
+
const tooltip = buildTooltip(s, ordinal);
|
|
22116
|
+
const ts = ordinal ?? formatTimestamp(s.date);
|
|
22113
22117
|
const sub = formatDuration(s.durationMinutes);
|
|
22114
22118
|
const sXStart = timeToX(sessionStart(s), rangeStartMs, rangeEndMs, segXStart, segXEnd);
|
|
22115
22119
|
const barW = Math.min(Math.max(s.durationMinutes * PX_PER_MIN, 20), segXEnd - sXStart);
|
|
@@ -22283,21 +22287,37 @@
|
|
|
22283
22287
|
] })
|
|
22284
22288
|
] });
|
|
22285
22289
|
}
|
|
22286
|
-
function WorkTimeline({ sessions, onSessionClick, maxHeight, accentColor, isDark }) {
|
|
22290
|
+
function WorkTimeline({ sessions, onSessionClick, maxHeight, accentColor, isDark, hideDates }) {
|
|
22287
22291
|
const mainColor = accentColor ?? (isDark ? "#f97316" : MAIN_COLOR);
|
|
22288
22292
|
const threadColor = isDark ? "rgba(255,255,255,0.15)" : THREAD_COLOR;
|
|
22289
22293
|
const textSecondary = isDark ? "rgba(255,255,255,0.65)" : TEXT_SECONDARY;
|
|
22290
22294
|
const textMuted = isDark ? "rgba(255,255,255,0.4)" : TEXT_MUTED;
|
|
22291
22295
|
const bgSurface = isDark ? "#111" : "#f8f9fb";
|
|
22292
|
-
const
|
|
22296
|
+
const effectiveSessions = (0, import_react.useMemo)(() => {
|
|
22297
|
+
if (!hideDates) return sessions;
|
|
22298
|
+
return sessions.map((s, i) => ({
|
|
22299
|
+
...s,
|
|
22300
|
+
date: s.date ?? new Date(i * 60 * 6e4).toISOString(),
|
|
22301
|
+
// Drop endTime to prevent any chance the renderer derives a real
|
|
22302
|
+
// wall-clock window from it.
|
|
22303
|
+
endTime: void 0,
|
|
22304
|
+
// Strip date from children too — agent lane math falls back to the
|
|
22305
|
+
// parent start when child dates are absent (already handled).
|
|
22306
|
+
children: s.children?.map((c) => ({ ...c, date: void 0 }))
|
|
22307
|
+
}));
|
|
22308
|
+
}, [sessions, hideDates]);
|
|
22309
|
+
const segments = (0, import_react.useMemo)(() => {
|
|
22310
|
+
const segs = computeSegments(effectiveSessions);
|
|
22311
|
+
return hideDates ? segs.filter((s) => s.type !== "gap") : segs;
|
|
22312
|
+
}, [effectiveSessions, hideDates]);
|
|
22293
22313
|
const [expanded, setExpanded] = (0, import_react.useState)(false);
|
|
22294
22314
|
const [fullscreen, setFullscreen] = (0, import_react.useState)(false);
|
|
22295
22315
|
const [playing, setPlaying] = (0, import_react.useState)(false);
|
|
22296
22316
|
const playRef = (0, import_react.useRef)(null);
|
|
22297
22317
|
const concurrentLimit = expanded ? 999 : DEFAULT_MAX_CONCURRENT;
|
|
22298
22318
|
const themeColors = (0, import_react.useMemo)(() => ({ main: mainColor, muted: textMuted }), [mainColor, textMuted]);
|
|
22299
|
-
const L = (0, import_react.useMemo)(() => layoutSegments(segments, concurrentLimit, themeColors), [segments, concurrentLimit, themeColors]);
|
|
22300
|
-
const legendEntries = (0, import_react.useMemo)(() => buildLegendEntries(L.sessionRanges), [L.sessionRanges]);
|
|
22319
|
+
const L = (0, import_react.useMemo)(() => layoutSegments(segments, concurrentLimit, themeColors, hideDates), [segments, concurrentLimit, themeColors, hideDates]);
|
|
22320
|
+
const legendEntries = (0, import_react.useMemo)(() => buildLegendEntries(L.sessionRanges, hideDates), [L.sessionRanges, hideDates]);
|
|
22301
22321
|
const scrollRef = (0, import_react.useRef)(null);
|
|
22302
22322
|
const [hovered, setHovered] = (0, import_react.useState)(null);
|
|
22303
22323
|
const [focusedEntry, setFocusedEntry] = (0, import_react.useState)(null);
|
|
@@ -22998,7 +23018,7 @@
|
|
|
22998
23018
|
return iso;
|
|
22999
23019
|
}
|
|
23000
23020
|
}
|
|
23001
|
-
function SessionOverlay({ session, sessionPageUrl, onClose }) {
|
|
23021
|
+
function SessionOverlay({ session, sessionPageUrl, onClose, hideDates }) {
|
|
23002
23022
|
const handleKeyDown = (0, import_react2.useCallback)((e) => {
|
|
23003
23023
|
if (e.key === "Escape") onClose();
|
|
23004
23024
|
}, [onClose]);
|
|
@@ -23040,8 +23060,8 @@
|
|
|
23040
23060
|
] }),
|
|
23041
23061
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { style: { fontFamily: "var(--font-display, sans-serif)", fontSize: "1.5rem", fontWeight: 700, color: "var(--on-surface, #191c1e)", marginBottom: "0.5rem" }, children: session.title }),
|
|
23042
23062
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontFamily: "var(--font-mono, monospace)", fontSize: "0.6875rem", textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--on-surface-variant, #6b7280)", marginBottom: "1rem" }, children: [
|
|
23043
|
-
formatDate(session.date),
|
|
23044
|
-
session.source && ` \xB7 ${session.source}
|
|
23063
|
+
!hideDates && session.date && formatDate(session.date),
|
|
23064
|
+
session.source && (hideDates || !session.date ? session.source : ` \xB7 ${session.source}`),
|
|
23045
23065
|
session.context && ` \xB7 ${session.context}`
|
|
23046
23066
|
] }),
|
|
23047
23067
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: "0.75rem", marginBottom: "1.25rem" }, children: [
|
|
@@ -23059,7 +23079,7 @@
|
|
|
23059
23079
|
" turns over ",
|
|
23060
23080
|
formatDuration2(session.durationMinutes)
|
|
23061
23081
|
] }),
|
|
23062
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WorkTimeline, { sessions: [session], maxHeight: 200 })
|
|
23082
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WorkTimeline, { sessions: [session], maxHeight: 200, hideDates })
|
|
23063
23083
|
] }),
|
|
23064
23084
|
session.executionPath && session.executionPath.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { marginBottom: "1.25rem" }, children: [
|
|
23065
23085
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SectionLabel, { children: "Execution Path" }),
|
|
@@ -23135,6 +23155,7 @@
|
|
|
23135
23155
|
}
|
|
23136
23156
|
var allSessions = /* @__PURE__ */ new Map();
|
|
23137
23157
|
var showOverlay = null;
|
|
23158
|
+
var pageHideDates = false;
|
|
23138
23159
|
function OverlayRoot() {
|
|
23139
23160
|
const [active, setActive] = (0, import_react3.useState)(null);
|
|
23140
23161
|
showOverlay = (session) => setActive(session);
|
|
@@ -23160,6 +23181,7 @@
|
|
|
23160
23181
|
{
|
|
23161
23182
|
session: active,
|
|
23162
23183
|
sessionPageUrl,
|
|
23184
|
+
hideDates: pageHideDates,
|
|
23163
23185
|
onClose: () => setActive(null)
|
|
23164
23186
|
}
|
|
23165
23187
|
);
|
|
@@ -23169,6 +23191,8 @@
|
|
|
23169
23191
|
document.querySelectorAll("[data-work-timeline]").forEach((el) => {
|
|
23170
23192
|
const sessions = parseSessions(el);
|
|
23171
23193
|
if (sessions.length === 0) return;
|
|
23194
|
+
const hideDates = el.dataset.hideDates === "1";
|
|
23195
|
+
if (hideDates) pageHideDates = true;
|
|
23172
23196
|
for (const s of sessions) allSessions.set(s.id, s);
|
|
23173
23197
|
(0, import_client.createRoot)(el).render(
|
|
23174
23198
|
import_react3.default.createElement(WorkTimeline, {
|
|
@@ -23176,6 +23200,7 @@
|
|
|
23176
23200
|
maxHeight: 300,
|
|
23177
23201
|
isDark,
|
|
23178
23202
|
accentColor,
|
|
23203
|
+
hideDates,
|
|
23179
23204
|
onSessionClick: (session) => {
|
|
23180
23205
|
if (showOverlay) showOverlay(session);
|
|
23181
23206
|
}
|