@cluesmith/codev 2.1.0 → 2.1.2
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/dashboard/dist/assets/index-CXX5mK4v.css +32 -0
- package/dashboard/dist/assets/index-DrG-moyC.js +194 -0
- package/dashboard/dist/assets/index-DrG-moyC.js.map +1 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
- package/dist/agent-farm/commands/cleanup.js +20 -4
- package/dist/agent-farm/commands/cleanup.js.map +1 -1
- package/dist/agent-farm/commands/open.d.ts.map +1 -1
- package/dist/agent-farm/commands/open.js +11 -4
- package/dist/agent-farm/commands/open.js.map +1 -1
- package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
- package/dist/agent-farm/commands/spawn.js +5 -0
- package/dist/agent-farm/commands/spawn.js.map +1 -1
- package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
- package/dist/agent-farm/lib/tunnel-client.js +12 -0
- package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
- package/dist/agent-farm/servers/analytics.d.ts +13 -19
- package/dist/agent-farm/servers/analytics.d.ts.map +1 -1
- package/dist/agent-farm/servers/analytics.js +130 -70
- package/dist/agent-farm/servers/analytics.js.map +1 -1
- package/dist/agent-farm/servers/tower-routes.js +4 -6
- package/dist/agent-farm/servers/tower-routes.js.map +1 -1
- package/dist/commands/consult/metrics.d.ts +6 -0
- package/dist/commands/consult/metrics.d.ts.map +1 -1
- package/dist/commands/consult/metrics.js +31 -0
- package/dist/commands/consult/metrics.js.map +1 -1
- package/dist/commands/porch/config.d.ts +20 -0
- package/dist/commands/porch/config.d.ts.map +1 -0
- package/dist/commands/porch/config.js +52 -0
- package/dist/commands/porch/config.js.map +1 -0
- package/dist/commands/porch/index.d.ts.map +1 -1
- package/dist/commands/porch/index.js +81 -22
- package/dist/commands/porch/index.js.map +1 -1
- package/dist/commands/porch/next.d.ts.map +1 -1
- package/dist/commands/porch/next.js +38 -7
- package/dist/commands/porch/next.js.map +1 -1
- package/dist/commands/porch/protocol.d.ts +21 -5
- package/dist/commands/porch/protocol.d.ts.map +1 -1
- package/dist/commands/porch/protocol.js +87 -7
- package/dist/commands/porch/protocol.js.map +1 -1
- package/dist/commands/porch/types.d.ts +11 -0
- package/dist/commands/porch/types.d.ts.map +1 -1
- package/dist/lib/github.d.ts +13 -0
- package/dist/lib/github.d.ts.map +1 -1
- package/dist/lib/github.js +69 -1
- package/dist/lib/github.js.map +1 -1
- package/package.json +1 -1
- package/skeleton/.claude/skills/af/SKILL.md +10 -4
- package/skeleton/protocols/air/protocol.json +1 -1
- package/skeleton/protocols/aspir/protocol.json +1 -1
- package/skeleton/protocols/spir/protocol.json +1 -1
- package/skeleton/resources/commands/agent-farm.md +32 -11
- package/skeleton/roles/architect.md +37 -19
- package/templates/open.html +37 -6
- package/dashboard/dist/assets/index-CUjY8HZv.css +0 -32
- package/dashboard/dist/assets/index-HdzAypSC.js +0 -194
- package/dashboard/dist/assets/index-HdzAypSC.js.map +0 -1
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Analytics aggregation service for the dashboard Analytics tab.
|
|
3
3
|
*
|
|
4
|
-
* Aggregates data from
|
|
5
|
-
* - GitHub CLI (merged PRs, closed issues,
|
|
4
|
+
* Aggregates data from two sources:
|
|
5
|
+
* - GitHub CLI (merged PRs, closed issues, protocol breakdown from branch names)
|
|
6
6
|
* - Consultation metrics DB (~/.codev/metrics.db)
|
|
7
|
-
* - Active builder count (passed in from tower context)
|
|
8
7
|
*
|
|
9
8
|
* Each data source fails independently — partial results are returned
|
|
10
9
|
* with error messages in the `errors` field.
|
|
11
10
|
*/
|
|
12
|
-
import { fetchMergedPRs, fetchClosedIssues,
|
|
11
|
+
import { fetchMergedPRs, fetchClosedIssues, parseAllLinkedIssues, fetchOnItTimestamps, } from '../../lib/github.js';
|
|
13
12
|
import { MetricsDB } from '../../commands/consult/metrics.js';
|
|
14
13
|
const CACHE_TTL_MS = 60_000; // 60 seconds
|
|
15
14
|
const cache = new Map();
|
|
@@ -41,77 +40,55 @@ function rangeToSinceDate(range) {
|
|
|
41
40
|
const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
42
41
|
return since.toISOString().split('T')[0]; // YYYY-MM-DD
|
|
43
42
|
}
|
|
44
|
-
function rangeToWeeks(range) {
|
|
45
|
-
if (range === '1')
|
|
46
|
-
return 1 / 7;
|
|
47
|
-
if (range === '7')
|
|
48
|
-
return 1;
|
|
49
|
-
if (range === '30')
|
|
50
|
-
return 30 / 7;
|
|
51
|
-
// For "all", we can't know the true range without data, so return 1
|
|
52
|
-
// (throughput = projectsCompleted / 1 = total projects)
|
|
53
|
-
return 1;
|
|
54
|
-
}
|
|
55
43
|
// =============================================================================
|
|
56
44
|
// GitHub metrics computation
|
|
57
45
|
// =============================================================================
|
|
58
|
-
function
|
|
46
|
+
function computeMedianHours(items) {
|
|
59
47
|
if (items.length === 0)
|
|
60
48
|
return null;
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
49
|
+
const hours = items
|
|
50
|
+
.map(item => (new Date(item.end).getTime() - new Date(item.start).getTime()) / (1000 * 60 * 60))
|
|
51
|
+
.sort((a, b) => a - b);
|
|
52
|
+
const mid = Math.floor(hours.length / 2);
|
|
53
|
+
return hours.length % 2 === 0
|
|
54
|
+
? (hours[mid - 1] + hours[mid]) / 2
|
|
55
|
+
: hours[mid];
|
|
65
56
|
}
|
|
66
57
|
async function computeGitHubMetrics(since, cwd) {
|
|
67
58
|
// Fetch merged PRs and closed issues in parallel
|
|
68
|
-
const [mergedPRs, closedIssues
|
|
59
|
+
const [mergedPRs, closedIssues] = await Promise.all([
|
|
69
60
|
fetchMergedPRs(since, cwd),
|
|
70
61
|
fetchClosedIssues(since, cwd),
|
|
71
|
-
fetchIssueList(cwd),
|
|
72
62
|
]);
|
|
73
|
-
if (mergedPRs === null && closedIssues === null
|
|
63
|
+
if (mergedPRs === null && closedIssues === null) {
|
|
74
64
|
throw new Error('GitHub CLI unavailable');
|
|
75
65
|
}
|
|
76
66
|
// PRs merged
|
|
77
67
|
const prs = mergedPRs ?? [];
|
|
78
68
|
const prsMerged = prs.length;
|
|
79
|
-
//
|
|
80
|
-
const
|
|
81
|
-
// Backlogs (from open issues)
|
|
82
|
-
const issues = openIssues ?? [];
|
|
83
|
-
const bugBacklog = issues.filter(i => i.labels.some(l => l.name === 'bug')).length;
|
|
84
|
-
const nonBugBacklog = issues.length - bugBacklog;
|
|
69
|
+
// Median time to merge
|
|
70
|
+
const medianTimeToMergeHours = computeMedianHours(prs.filter(pr => pr.mergedAt).map(pr => ({ start: pr.createdAt, end: pr.mergedAt })));
|
|
85
71
|
// Closed issues
|
|
86
72
|
const closed = closedIssues ?? [];
|
|
87
73
|
const issuesClosed = closed.length;
|
|
88
|
-
//
|
|
74
|
+
// Median time to close bugs
|
|
89
75
|
const closedBugs = closed.filter(i => i.labels.some(l => l.name === 'bug') && i.closedAt);
|
|
90
|
-
const
|
|
91
|
-
// Projects completed (distinct issue numbers from merged PRs via parseAllLinkedIssues)
|
|
92
|
-
const linkedIssues = new Set();
|
|
93
|
-
for (const pr of prs) {
|
|
94
|
-
for (const issueNum of parseAllLinkedIssues(pr.body ?? '', pr.title)) {
|
|
95
|
-
linkedIssues.add(issueNum);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const projectsCompleted = linkedIssues.size;
|
|
76
|
+
const medianTimeToCloseBugsHours = computeMedianHours(closedBugs.map(i => ({ start: i.createdAt, end: i.closedAt })));
|
|
99
77
|
return {
|
|
100
78
|
prsMerged,
|
|
101
|
-
|
|
102
|
-
bugBacklog,
|
|
103
|
-
nonBugBacklog,
|
|
79
|
+
medianTimeToMergeHours,
|
|
104
80
|
issuesClosed,
|
|
105
|
-
|
|
106
|
-
|
|
81
|
+
medianTimeToCloseBugsHours,
|
|
82
|
+
mergedPRList: prs,
|
|
107
83
|
};
|
|
108
84
|
}
|
|
109
|
-
function computeConsultationMetrics(days) {
|
|
85
|
+
function computeConsultationMetrics(days, workspacePath) {
|
|
110
86
|
const db = new MetricsDB();
|
|
111
87
|
try {
|
|
112
|
-
const filters =
|
|
88
|
+
const filters = { workspace: workspacePath };
|
|
89
|
+
if (days)
|
|
90
|
+
filters.days = days;
|
|
113
91
|
const summary = db.summary(filters);
|
|
114
|
-
const projectCosts = db.costByProject(filters);
|
|
115
92
|
// Derive costByModel from summary.byModel
|
|
116
93
|
const costByModel = {};
|
|
117
94
|
for (const m of summary.byModel) {
|
|
@@ -148,7 +125,6 @@ function computeConsultationMetrics(days) {
|
|
|
148
125
|
})),
|
|
149
126
|
byReviewType,
|
|
150
127
|
byProtocol,
|
|
151
|
-
costByProject: projectCosts,
|
|
152
128
|
};
|
|
153
129
|
}
|
|
154
130
|
finally {
|
|
@@ -156,6 +132,83 @@ function computeConsultationMetrics(days) {
|
|
|
156
132
|
}
|
|
157
133
|
}
|
|
158
134
|
// =============================================================================
|
|
135
|
+
// Project protocol breakdown (from PR branch names)
|
|
136
|
+
// =============================================================================
|
|
137
|
+
/**
|
|
138
|
+
* Known branch-name prefixes that map to protocols.
|
|
139
|
+
* Checked in order; first match wins.
|
|
140
|
+
*/
|
|
141
|
+
const BRANCH_PROTOCOL_PATTERNS = [
|
|
142
|
+
{ pattern: /^builder\/bugfix-/, protocol: 'bugfix' },
|
|
143
|
+
{ pattern: /^builder\/spir-/, protocol: 'spir' },
|
|
144
|
+
{ pattern: /^spir\//, protocol: 'spir' },
|
|
145
|
+
{ pattern: /^builder\/aspir-/, protocol: 'aspir' },
|
|
146
|
+
{ pattern: /^builder\/air-/, protocol: 'air' },
|
|
147
|
+
{ pattern: /^builder\/tick-/, protocol: 'tick' },
|
|
148
|
+
];
|
|
149
|
+
export function protocolFromBranch(branch) {
|
|
150
|
+
for (const { pattern, protocol } of BRANCH_PROTOCOL_PATTERNS) {
|
|
151
|
+
if (pattern.test(branch))
|
|
152
|
+
return protocol;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
async function computeProjectsByProtocol(mergedPRs, cwd, agentTimeByProtocol) {
|
|
157
|
+
// Group PRs by protocol and collect linked issue numbers
|
|
158
|
+
const byProtocol = new Map();
|
|
159
|
+
const issueToProtocolPRs = new Map();
|
|
160
|
+
for (const pr of mergedPRs) {
|
|
161
|
+
const protocol = protocolFromBranch(pr.headRefName ?? '');
|
|
162
|
+
if (!protocol)
|
|
163
|
+
continue;
|
|
164
|
+
if (!byProtocol.has(protocol))
|
|
165
|
+
byProtocol.set(protocol, []);
|
|
166
|
+
byProtocol.get(protocol).push(pr);
|
|
167
|
+
// Track linked issues for "on it" timestamp lookup
|
|
168
|
+
for (const issueNum of parseAllLinkedIssues(pr.body ?? '', pr.title)) {
|
|
169
|
+
if (!issueToProtocolPRs.has(issueNum))
|
|
170
|
+
issueToProtocolPRs.set(issueNum, []);
|
|
171
|
+
issueToProtocolPRs.get(issueNum).push(pr);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Fetch "on it" timestamps for all linked issues
|
|
175
|
+
const onItTimestamps = await fetchOnItTimestamps([...issueToProtocolPRs.keys()], cwd);
|
|
176
|
+
// Build a map from PR number → start time (on-it or fallback to PR createdAt)
|
|
177
|
+
const prStartTime = new Map();
|
|
178
|
+
for (const [issueNum, prs] of issueToProtocolPRs) {
|
|
179
|
+
const onIt = onItTimestamps.get(issueNum);
|
|
180
|
+
if (onIt) {
|
|
181
|
+
for (const pr of prs) {
|
|
182
|
+
// Only set if not already set (first linked issue wins)
|
|
183
|
+
if (!prStartTime.has(pr.number)) {
|
|
184
|
+
prStartTime.set(pr.number, onIt);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Compute per-protocol stats
|
|
190
|
+
const result = {};
|
|
191
|
+
for (const [protocol, prs] of byProtocol) {
|
|
192
|
+
const wallClockHours = [];
|
|
193
|
+
for (const pr of prs) {
|
|
194
|
+
if (!pr.mergedAt)
|
|
195
|
+
continue;
|
|
196
|
+
const start = prStartTime.get(pr.number) ?? pr.createdAt;
|
|
197
|
+
const ms = new Date(pr.mergedAt).getTime() - new Date(start).getTime();
|
|
198
|
+
wallClockHours.push(ms / (1000 * 60 * 60));
|
|
199
|
+
}
|
|
200
|
+
const avgAgentSec = agentTimeByProtocol?.get(protocol);
|
|
201
|
+
result[protocol] = {
|
|
202
|
+
count: prs.length,
|
|
203
|
+
avgWallClockHours: wallClockHours.length > 0
|
|
204
|
+
? wallClockHours.reduce((a, b) => a + b, 0) / wallClockHours.length
|
|
205
|
+
: null,
|
|
206
|
+
avgAgentTimeHours: avgAgentSec != null ? avgAgentSec / 3600 : null,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
// =============================================================================
|
|
159
212
|
// Main computation
|
|
160
213
|
// =============================================================================
|
|
161
214
|
/**
|
|
@@ -163,10 +216,9 @@ function computeConsultationMetrics(days) {
|
|
|
163
216
|
*
|
|
164
217
|
* @param workspaceRoot - Path to the workspace root (used as cwd for gh CLI)
|
|
165
218
|
* @param range - Time range: '1', '7', '30', or 'all'
|
|
166
|
-
* @param activeBuilders - Current active builder count (from tower context)
|
|
167
219
|
* @param refresh - If true, bypass the cache
|
|
168
220
|
*/
|
|
169
|
-
export async function computeAnalytics(workspaceRoot, range,
|
|
221
|
+
export async function computeAnalytics(workspaceRoot, range, refresh = false) {
|
|
170
222
|
const cacheKey = `${workspaceRoot}:${range}`;
|
|
171
223
|
// Check cache
|
|
172
224
|
if (!refresh) {
|
|
@@ -177,7 +229,6 @@ export async function computeAnalytics(workspaceRoot, range, activeBuilders, ref
|
|
|
177
229
|
}
|
|
178
230
|
const since = rangeToSinceDate(range);
|
|
179
231
|
const days = rangeToDays(range);
|
|
180
|
-
const weeks = rangeToWeeks(range);
|
|
181
232
|
const errors = {};
|
|
182
233
|
// GitHub metrics
|
|
183
234
|
let githubMetrics;
|
|
@@ -189,18 +240,16 @@ export async function computeAnalytics(workspaceRoot, range, activeBuilders, ref
|
|
|
189
240
|
errors.github = msg;
|
|
190
241
|
githubMetrics = {
|
|
191
242
|
prsMerged: 0,
|
|
192
|
-
|
|
193
|
-
bugBacklog: 0,
|
|
194
|
-
nonBugBacklog: 0,
|
|
243
|
+
medianTimeToMergeHours: null,
|
|
195
244
|
issuesClosed: 0,
|
|
196
|
-
|
|
197
|
-
|
|
245
|
+
medianTimeToCloseBugsHours: null,
|
|
246
|
+
mergedPRList: [],
|
|
198
247
|
};
|
|
199
248
|
}
|
|
200
249
|
// Consultation metrics
|
|
201
250
|
let consultMetrics;
|
|
202
251
|
try {
|
|
203
|
-
consultMetrics = computeConsultationMetrics(days);
|
|
252
|
+
consultMetrics = computeConsultationMetrics(days, workspaceRoot);
|
|
204
253
|
}
|
|
205
254
|
catch (err) {
|
|
206
255
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -214,25 +263,36 @@ export async function computeAnalytics(workspaceRoot, range, activeBuilders, ref
|
|
|
214
263
|
byModel: [],
|
|
215
264
|
byReviewType: {},
|
|
216
265
|
byProtocol: {},
|
|
217
|
-
costByProject: [],
|
|
218
266
|
};
|
|
219
267
|
}
|
|
268
|
+
// Agent time by protocol from consultation metrics
|
|
269
|
+
let agentTimeByProtocol;
|
|
270
|
+
try {
|
|
271
|
+
const db = new MetricsDB();
|
|
272
|
+
try {
|
|
273
|
+
const agentFilters = { workspace: workspaceRoot };
|
|
274
|
+
if (days)
|
|
275
|
+
agentFilters.days = days;
|
|
276
|
+
const agentTimeRows = db.agentTimeByProtocol(agentFilters);
|
|
277
|
+
agentTimeByProtocol = new Map(agentTimeRows.map(r => [r.protocol, r.avgAgentTimeSeconds]));
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
db.close();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
// Agent time is best-effort; don't fail if MetricsDB is unavailable
|
|
285
|
+
}
|
|
286
|
+
// Protocol breakdown with avg wall clock times (from PR branch names + "on it" timestamps)
|
|
287
|
+
const projectsByProtocol = await computeProjectsByProtocol(githubMetrics.mergedPRList, workspaceRoot, agentTimeByProtocol);
|
|
220
288
|
const result = {
|
|
221
289
|
timeRange: rangeToLabel(range),
|
|
222
|
-
|
|
290
|
+
activity: {
|
|
223
291
|
prsMerged: githubMetrics.prsMerged,
|
|
224
|
-
|
|
225
|
-
bugBacklog: githubMetrics.bugBacklog,
|
|
226
|
-
nonBugBacklog: githubMetrics.nonBugBacklog,
|
|
292
|
+
medianTimeToMergeHours: githubMetrics.medianTimeToMergeHours,
|
|
227
293
|
issuesClosed: githubMetrics.issuesClosed,
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
builders: {
|
|
231
|
-
projectsCompleted: githubMetrics.projectsCompleted,
|
|
232
|
-
throughputPerWeek: weeks > 0
|
|
233
|
-
? Math.round((githubMetrics.projectsCompleted / weeks) * 10) / 10
|
|
234
|
-
: 0,
|
|
235
|
-
activeBuilders,
|
|
294
|
+
medianTimeToCloseBugsHours: githubMetrics.medianTimeToCloseBugsHours,
|
|
295
|
+
projectsByProtocol,
|
|
236
296
|
},
|
|
237
297
|
consultation: consultMetrics,
|
|
238
298
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/analytics.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -559,16 +559,12 @@ async function handleAnalytics(res, url, workspaceOverride) {
|
|
|
559
559
|
const rangeLabel = rangeParam === 'all' ? 'all' : rangeParam === '1' ? '24h' : `${rangeParam}d`;
|
|
560
560
|
if (!workspaceRoot) {
|
|
561
561
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
562
|
-
res.end(JSON.stringify({ timeRange: rangeLabel,
|
|
562
|
+
res.end(JSON.stringify({ timeRange: rangeLabel, activity: { prsMerged: 0, medianTimeToMergeHours: null, issuesClosed: 0, medianTimeToCloseBugsHours: null, projectsByProtocol: {} }, consultation: { totalCount: 0, totalCostUsd: null, costByModel: {}, avgLatencySeconds: null, successRate: null, byModel: [], byReviewType: {}, byProtocol: {} } }));
|
|
563
563
|
return;
|
|
564
564
|
}
|
|
565
565
|
const range = rangeParam;
|
|
566
566
|
const refresh = url.searchParams.get('refresh') === '1';
|
|
567
|
-
|
|
568
|
-
const wsTerminals = getWorkspaceTerminals();
|
|
569
|
-
const entry = wsTerminals.get(normalizeWorkspacePath(workspaceRoot));
|
|
570
|
-
const activeBuilders = entry?.builders.size ?? 0;
|
|
571
|
-
const data = await computeAnalytics(workspaceRoot, range, activeBuilders, refresh);
|
|
567
|
+
const data = await computeAnalytics(workspaceRoot, range, refresh);
|
|
572
568
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
573
569
|
res.end(JSON.stringify(data));
|
|
574
570
|
}
|
|
@@ -1606,6 +1602,7 @@ function handleWorkspaceAnnotate(req, res, ctx, url, workspacePath, annotateMatc
|
|
|
1606
1602
|
const is3D = ['stl', '3mf'].includes(ext);
|
|
1607
1603
|
const isPdf = ext === 'pdf';
|
|
1608
1604
|
const isMarkdown = ext === 'md';
|
|
1605
|
+
const isHtml = ['html', 'htm'].includes(ext);
|
|
1609
1606
|
// Sub-route: GET /file — re-read file content from disk
|
|
1610
1607
|
if (req.method === 'GET' && subRoute === 'file') {
|
|
1611
1608
|
try {
|
|
@@ -1723,6 +1720,7 @@ function handleWorkspaceAnnotate(req, res, ctx, url, workspacePath, annotateMatc
|
|
|
1723
1720
|
html = html.replace(/\{\{IS_IMAGE\}\}/g, String(isImage));
|
|
1724
1721
|
html = html.replace(/\{\{IS_VIDEO\}\}/g, String(isVideo));
|
|
1725
1722
|
html = html.replace(/\{\{IS_PDF\}\}/g, String(isPdf));
|
|
1723
|
+
html = html.replace(/\{\{IS_HTML\}\}/g, String(isHtml));
|
|
1726
1724
|
html = html.replace(/\{\{FILE_SIZE\}\}/g, String(fileSize));
|
|
1727
1725
|
// Inject initialization script (template loads content via fetch)
|
|
1728
1726
|
let initScript;
|