pi-studio 0.5.52 → 0.5.54
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/CHANGELOG.md +21 -0
- package/README.md +3 -3
- package/client/studio-annotation-helpers.js +67 -0
- package/client/studio-client.js +596 -89
- package/client/studio.css +176 -0
- package/index.ts +332 -3
- package/package.json +1 -1
- package/shared/studio-markdown-latex-literals.js +203 -0
package/client/studio-client.js
CHANGED
|
@@ -61,6 +61,10 @@
|
|
|
61
61
|
const sourceBadgeEl = document.getElementById("sourceBadge");
|
|
62
62
|
const syncBadgeEl = document.getElementById("syncBadge");
|
|
63
63
|
let critiqueViewEl = document.getElementById("critiqueView");
|
|
64
|
+
const responseActionsEl = document.getElementById("responseActions");
|
|
65
|
+
const responseWrapEl = responseActionsEl && typeof responseActionsEl.closest === "function"
|
|
66
|
+
? responseActionsEl.closest(".response-wrap")
|
|
67
|
+
: null;
|
|
64
68
|
const referenceBadgeEl = document.getElementById("referenceBadge");
|
|
65
69
|
const editorViewSelect = document.getElementById("editorViewSelect");
|
|
66
70
|
const rightViewSelect = document.getElementById("rightViewSelect");
|
|
@@ -179,6 +183,10 @@
|
|
|
179
183
|
let latestCritiqueNotesNormalized = "";
|
|
180
184
|
let responseHistory = [];
|
|
181
185
|
let responseHistoryIndex = -1;
|
|
186
|
+
let traceState = null;
|
|
187
|
+
let traceFilter = "all";
|
|
188
|
+
let traceAutoScroll = true;
|
|
189
|
+
let traceRenderRaf = null;
|
|
182
190
|
let studioRunChainActive = false;
|
|
183
191
|
let queuedSteeringCount = 0;
|
|
184
192
|
let agentBusyFromServer = false;
|
|
@@ -231,6 +239,262 @@
|
|
|
231
239
|
return changed;
|
|
232
240
|
}
|
|
233
241
|
|
|
242
|
+
function createEmptyTraceState() {
|
|
243
|
+
return {
|
|
244
|
+
runId: null,
|
|
245
|
+
requestId: null,
|
|
246
|
+
requestKind: null,
|
|
247
|
+
status: "idle",
|
|
248
|
+
startedAt: null,
|
|
249
|
+
updatedAt: null,
|
|
250
|
+
entries: [],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function normalizeTraceStatus(status) {
|
|
255
|
+
return status === "running" || status === "complete" ? status : "idle";
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function normalizeTraceEntryStatus(status) {
|
|
259
|
+
return status === "streaming" || status === "pending" || status === "complete" || status === "error"
|
|
260
|
+
? status
|
|
261
|
+
: "pending";
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function normalizeTraceEntry(entry, fallbackIndex) {
|
|
265
|
+
if (!entry || typeof entry !== "object") return null;
|
|
266
|
+
if (entry.type === "assistant") {
|
|
267
|
+
return {
|
|
268
|
+
id: typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : ("trace-assistant-" + fallbackIndex),
|
|
269
|
+
type: "assistant",
|
|
270
|
+
startedAt: parseFiniteNumber(entry.startedAt) || Date.now(),
|
|
271
|
+
updatedAt: parseFiniteNumber(entry.updatedAt) || Date.now(),
|
|
272
|
+
thinking: typeof entry.thinking === "string" ? entry.thinking : "",
|
|
273
|
+
text: typeof entry.text === "string" ? entry.text : "",
|
|
274
|
+
status: normalizeTraceEntryStatus(entry.status),
|
|
275
|
+
stopReason: typeof entry.stopReason === "string" && entry.stopReason.trim() ? entry.stopReason.trim() : null,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (entry.type === "tool") {
|
|
279
|
+
return {
|
|
280
|
+
id: typeof entry.id === "string" && entry.id.trim() ? entry.id.trim() : ("trace-tool-" + fallbackIndex),
|
|
281
|
+
type: "tool",
|
|
282
|
+
toolCallId: typeof entry.toolCallId === "string" ? entry.toolCallId : ("tool-" + fallbackIndex),
|
|
283
|
+
toolName: typeof entry.toolName === "string" ? entry.toolName : "tool",
|
|
284
|
+
label: parseNonEmptyString(entry.label),
|
|
285
|
+
argsSummary: parseNonEmptyString(entry.argsSummary),
|
|
286
|
+
output: typeof entry.output === "string" ? entry.output : "",
|
|
287
|
+
startedAt: parseFiniteNumber(entry.startedAt) || Date.now(),
|
|
288
|
+
updatedAt: parseFiniteNumber(entry.updatedAt) || Date.now(),
|
|
289
|
+
status: normalizeTraceEntryStatus(entry.status),
|
|
290
|
+
isError: Boolean(entry.isError),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function normalizeTraceState(raw) {
|
|
297
|
+
const fallback = createEmptyTraceState();
|
|
298
|
+
if (!raw || typeof raw !== "object") return fallback;
|
|
299
|
+
const entries = Array.isArray(raw.entries)
|
|
300
|
+
? raw.entries.map((entry, index) => normalizeTraceEntry(entry, index)).filter(Boolean)
|
|
301
|
+
: [];
|
|
302
|
+
return {
|
|
303
|
+
runId: parseNonEmptyString(raw.runId),
|
|
304
|
+
requestId: parseNonEmptyString(raw.requestId),
|
|
305
|
+
requestKind: parseNonEmptyString(raw.requestKind),
|
|
306
|
+
status: normalizeTraceStatus(raw.status),
|
|
307
|
+
startedAt: parseFiniteNumber(raw.startedAt),
|
|
308
|
+
updatedAt: parseFiniteNumber(raw.updatedAt),
|
|
309
|
+
entries,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function ensureTraceState() {
|
|
314
|
+
if (!traceState) traceState = createEmptyTraceState();
|
|
315
|
+
return traceState;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function replaceTraceState(nextState) {
|
|
319
|
+
traceState = normalizeTraceState(nextState);
|
|
320
|
+
renderTraceViewIfActive();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function upsertTraceEntry(entry) {
|
|
324
|
+
const normalized = normalizeTraceEntry(entry, ensureTraceState().entries.length);
|
|
325
|
+
if (!normalized) return;
|
|
326
|
+
const state = ensureTraceState();
|
|
327
|
+
const index = state.entries.findIndex((candidate) => candidate.id === normalized.id);
|
|
328
|
+
if (index >= 0) {
|
|
329
|
+
state.entries[index] = normalized;
|
|
330
|
+
} else {
|
|
331
|
+
state.entries.push(normalized);
|
|
332
|
+
}
|
|
333
|
+
state.updatedAt = normalized.updatedAt;
|
|
334
|
+
renderTraceViewIfActive();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function appendTraceAssistantDelta(entryId, deltaKind, delta, updatedAt) {
|
|
338
|
+
if (typeof delta !== "string" || !delta) return;
|
|
339
|
+
const state = ensureTraceState();
|
|
340
|
+
const targetId = typeof entryId === "string" && entryId.trim() ? entryId.trim() : null;
|
|
341
|
+
let entry = targetId ? state.entries.find((candidate) => candidate.id === targetId) : null;
|
|
342
|
+
if (!entry || entry.type !== "assistant") {
|
|
343
|
+
entry = normalizeTraceEntry({
|
|
344
|
+
id: targetId || ("trace-assistant-live-" + Date.now()),
|
|
345
|
+
type: "assistant",
|
|
346
|
+
startedAt: updatedAt,
|
|
347
|
+
updatedAt,
|
|
348
|
+
thinking: "",
|
|
349
|
+
text: "",
|
|
350
|
+
status: "streaming",
|
|
351
|
+
stopReason: null,
|
|
352
|
+
}, state.entries.length);
|
|
353
|
+
if (!entry) return;
|
|
354
|
+
state.entries.push(entry);
|
|
355
|
+
}
|
|
356
|
+
if (deltaKind === "thinking") {
|
|
357
|
+
entry.thinking += delta;
|
|
358
|
+
} else {
|
|
359
|
+
entry.text += delta;
|
|
360
|
+
}
|
|
361
|
+
entry.status = "streaming";
|
|
362
|
+
entry.updatedAt = parseFiniteNumber(updatedAt) || Date.now();
|
|
363
|
+
state.updatedAt = entry.updatedAt;
|
|
364
|
+
renderTraceViewIfActive();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function updateTraceStatusFromMessage(message) {
|
|
368
|
+
if (!message || typeof message !== "object") return;
|
|
369
|
+
const state = ensureTraceState();
|
|
370
|
+
state.runId = parseNonEmptyString(message.runId) || state.runId;
|
|
371
|
+
if (Object.prototype.hasOwnProperty.call(message, "requestId")) {
|
|
372
|
+
state.requestId = parseNonEmptyString(message.requestId);
|
|
373
|
+
}
|
|
374
|
+
if (Object.prototype.hasOwnProperty.call(message, "requestKind")) {
|
|
375
|
+
state.requestKind = parseNonEmptyString(message.requestKind);
|
|
376
|
+
}
|
|
377
|
+
if (Object.prototype.hasOwnProperty.call(message, "startedAt")) {
|
|
378
|
+
state.startedAt = parseFiniteNumber(message.startedAt);
|
|
379
|
+
}
|
|
380
|
+
if (Object.prototype.hasOwnProperty.call(message, "updatedAt")) {
|
|
381
|
+
state.updatedAt = parseFiniteNumber(message.updatedAt);
|
|
382
|
+
}
|
|
383
|
+
if (Object.prototype.hasOwnProperty.call(message, "status")) {
|
|
384
|
+
state.status = normalizeTraceStatus(message.status);
|
|
385
|
+
}
|
|
386
|
+
renderTraceViewIfActive();
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function normalizeTraceFilter(filter) {
|
|
390
|
+
return filter === "thinking" || filter === "tools" ? filter : "all";
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function setTraceFilter(nextFilter) {
|
|
394
|
+
const normalized = normalizeTraceFilter(nextFilter);
|
|
395
|
+
if (traceFilter === normalized) return;
|
|
396
|
+
traceFilter = normalized;
|
|
397
|
+
traceAutoScroll = true;
|
|
398
|
+
renderTraceViewIfActive();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function getTraceEntriesForFilter(filterOverride) {
|
|
402
|
+
const state = traceState || createEmptyTraceState();
|
|
403
|
+
const filter = normalizeTraceFilter(filterOverride || traceFilter);
|
|
404
|
+
const entries = Array.isArray(state.entries) ? state.entries : [];
|
|
405
|
+
if (filter === "tools") {
|
|
406
|
+
return entries.filter((entry) => entry.type === "tool");
|
|
407
|
+
}
|
|
408
|
+
if (filter === "thinking") {
|
|
409
|
+
return entries.filter((entry) => entry.type === "assistant" && String(entry.thinking || "").trim());
|
|
410
|
+
}
|
|
411
|
+
return entries.filter((entry) => {
|
|
412
|
+
if (entry.type === "assistant") {
|
|
413
|
+
return Boolean(String(entry.thinking || "").trim() || String(entry.text || "").trim());
|
|
414
|
+
}
|
|
415
|
+
return true;
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function buildVisibleWorkingText(filterOverride) {
|
|
420
|
+
const filter = normalizeTraceFilter(filterOverride || traceFilter);
|
|
421
|
+
const entries = getTraceEntriesForFilter(filter);
|
|
422
|
+
if (!entries.length) return "";
|
|
423
|
+
|
|
424
|
+
if (filter === "thinking") {
|
|
425
|
+
return entries
|
|
426
|
+
.map((entry) => entry && entry.type === "assistant" ? String(entry.thinking || "").trim() : "")
|
|
427
|
+
.filter(Boolean)
|
|
428
|
+
.join("\n\n");
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return entries.map((entry) => {
|
|
432
|
+
if (entry.type === "assistant") {
|
|
433
|
+
const parts = [];
|
|
434
|
+
if (String(entry.thinking || "").trim()) {
|
|
435
|
+
parts.push("[Thinking]\n" + String(entry.thinking || "").trim());
|
|
436
|
+
}
|
|
437
|
+
if (filter === "all" && String(entry.text || "").trim()) {
|
|
438
|
+
parts.push("[Response]\n" + String(entry.text || "").trim());
|
|
439
|
+
}
|
|
440
|
+
return ["Assistant", ...parts].join("\n\n").trim();
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const header = entry.label && entry.label !== entry.toolName
|
|
444
|
+
? ("Tool: " + String(entry.toolName || "tool") + " — " + entry.label)
|
|
445
|
+
: ("Tool: " + String(entry.toolName || "tool"));
|
|
446
|
+
const parts = [header];
|
|
447
|
+
if (String(entry.argsSummary || "").trim()) {
|
|
448
|
+
parts.push("Input:\n" + String(entry.argsSummary || "").trim());
|
|
449
|
+
}
|
|
450
|
+
if (String(entry.output || "").trim()) {
|
|
451
|
+
parts.push("Output:\n" + String(entry.output || "").trim());
|
|
452
|
+
}
|
|
453
|
+
return parts.join("\n\n").trim();
|
|
454
|
+
}).filter(Boolean).join("\n\n---\n\n");
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function getWorkingDocumentLabel(filterOverride) {
|
|
458
|
+
const filter = normalizeTraceFilter(filterOverride || traceFilter);
|
|
459
|
+
if (filter === "thinking") return "working (thinking)";
|
|
460
|
+
if (filter === "tools") return "working (tools)";
|
|
461
|
+
return "working";
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
async function copyVisibleWorkingToClipboard() {
|
|
465
|
+
const content = buildVisibleWorkingText();
|
|
466
|
+
if (!content.trim()) {
|
|
467
|
+
setStatus("No visible working details to copy yet.", "warning");
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
await navigator.clipboard.writeText(content);
|
|
472
|
+
setStatus("Copied visible working text.", "success");
|
|
473
|
+
} catch {
|
|
474
|
+
setStatus("Clipboard write failed.", "warning");
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function loadVisibleWorkingIntoEditor() {
|
|
479
|
+
const content = buildVisibleWorkingText();
|
|
480
|
+
if (!content.trim()) {
|
|
481
|
+
setStatus("No visible working details to load yet.", "warning");
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
setEditorText(content, { preserveScroll: false, preserveSelection: false });
|
|
485
|
+
setSourceState({ source: "blank", label: getWorkingDocumentLabel(), path: null });
|
|
486
|
+
setStatus("Loaded visible working into editor.", "success");
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function renderTraceViewIfActive() {
|
|
490
|
+
if (rightView !== "trace") return;
|
|
491
|
+
if (traceRenderRaf !== null) return;
|
|
492
|
+
traceRenderRaf = window.requestAnimationFrame(() => {
|
|
493
|
+
traceRenderRaf = null;
|
|
494
|
+
refreshResponseUi();
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
234
498
|
contextTokens = parseFiniteNumber(document.body && document.body.dataset ? document.body.dataset.contextTokens : null);
|
|
235
499
|
contextWindow = parseFiniteNumber(document.body && document.body.dataset ? document.body.dataset.contextWindow : null);
|
|
236
500
|
contextPercent = parseFiniteNumber(document.body && document.body.dataset ? document.body.dataset.contextPercent : null);
|
|
@@ -412,6 +676,17 @@
|
|
|
412
676
|
if (typeof message.label === "string") summary.label = message.label;
|
|
413
677
|
if (Array.isArray(message.responseHistory)) summary.responseHistoryCount = message.responseHistory.length;
|
|
414
678
|
if (Array.isArray(message.items)) summary.itemsCount = message.items.length;
|
|
679
|
+
if (message.traceState && typeof message.traceState === "object" && Array.isArray(message.traceState.entries)) {
|
|
680
|
+
summary.traceEntries = message.traceState.entries.length;
|
|
681
|
+
summary.traceStatus = message.traceState.status;
|
|
682
|
+
}
|
|
683
|
+
if (message.trace && typeof message.trace === "object" && Array.isArray(message.trace.entries)) {
|
|
684
|
+
summary.traceEntries = message.trace.entries.length;
|
|
685
|
+
summary.traceStatus = message.trace.status;
|
|
686
|
+
}
|
|
687
|
+
if (typeof message.entryId === "string") summary.entryId = message.entryId;
|
|
688
|
+
if (typeof message.deltaKind === "string") summary.deltaKind = message.deltaKind;
|
|
689
|
+
if (typeof message.delta === "string") summary.deltaLength = message.delta.length;
|
|
415
690
|
if (typeof message.details === "object" && message.details !== null) summary.details = message.details;
|
|
416
691
|
return summary;
|
|
417
692
|
}
|
|
@@ -1419,6 +1694,21 @@
|
|
|
1419
1694
|
function updateReferenceBadge() {
|
|
1420
1695
|
if (!referenceBadgeEl) return;
|
|
1421
1696
|
|
|
1697
|
+
if (rightView === "trace") {
|
|
1698
|
+
const state = traceState || createEmptyTraceState();
|
|
1699
|
+
const entryCount = getTraceEntriesForFilter(traceFilter).length;
|
|
1700
|
+
const time = formatReferenceTime(state.startedAt || state.updatedAt);
|
|
1701
|
+
if (state.status === "idle") {
|
|
1702
|
+
referenceBadgeEl.textContent = "Working: no active run yet";
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
const statusLabel = state.status === "running" ? "live" : "complete";
|
|
1706
|
+
referenceBadgeEl.textContent = "Working: " + statusLabel
|
|
1707
|
+
+ (entryCount ? (" · " + entryCount + " entr" + (entryCount === 1 ? "y" : "ies")) : "")
|
|
1708
|
+
+ (time ? (" · " + time) : "");
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1422
1712
|
if (rightView === "editor-preview") {
|
|
1423
1713
|
const hasResponse = Boolean(latestResponseMarkdown && latestResponseMarkdown.trim());
|
|
1424
1714
|
if (hasResponse) {
|
|
@@ -1432,26 +1722,6 @@
|
|
|
1432
1722
|
}
|
|
1433
1723
|
|
|
1434
1724
|
const hasResponse = Boolean(latestResponseMarkdown && latestResponseMarkdown.trim());
|
|
1435
|
-
const hasThinking = Boolean(latestResponseThinking && latestResponseThinking.trim());
|
|
1436
|
-
if (rightView === "thinking") {
|
|
1437
|
-
if (!hasResponse && !hasThinking) {
|
|
1438
|
-
referenceBadgeEl.textContent = "Thinking: none";
|
|
1439
|
-
return;
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
const time = formatReferenceTime(latestResponseTimestamp);
|
|
1443
|
-
const total = Array.isArray(responseHistory) ? responseHistory.length : 0;
|
|
1444
|
-
const selected = total > 0 && responseHistoryIndex >= 0 && responseHistoryIndex < total
|
|
1445
|
-
? responseHistoryIndex + 1
|
|
1446
|
-
: 0;
|
|
1447
|
-
const historyPrefix = total > 0 ? "Response history " + selected + "/" + total + " · " : "";
|
|
1448
|
-
const thinkingLabel = hasThinking ? "assistant thinking" : "assistant thinking unavailable";
|
|
1449
|
-
referenceBadgeEl.textContent = time
|
|
1450
|
-
? historyPrefix + thinkingLabel + " · " + time
|
|
1451
|
-
: historyPrefix + thinkingLabel;
|
|
1452
|
-
return;
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
1725
|
if (!hasResponse) {
|
|
1456
1726
|
referenceBadgeEl.textContent = "Latest response: none";
|
|
1457
1727
|
return;
|
|
@@ -1527,14 +1797,15 @@
|
|
|
1527
1797
|
function updateSyncBadge(normalizedEditorText) {
|
|
1528
1798
|
if (!syncBadgeEl) return;
|
|
1529
1799
|
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1800
|
+
if (rightView === "trace") {
|
|
1801
|
+
syncBadgeEl.hidden = true;
|
|
1802
|
+
syncBadgeEl.classList.remove("sync");
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1534
1805
|
|
|
1535
|
-
if (!
|
|
1806
|
+
if (!latestResponseHasContent) {
|
|
1536
1807
|
syncBadgeEl.hidden = true;
|
|
1537
|
-
syncBadgeEl.textContent =
|
|
1808
|
+
syncBadgeEl.textContent = "In sync with response";
|
|
1538
1809
|
syncBadgeEl.classList.remove("sync");
|
|
1539
1810
|
return;
|
|
1540
1811
|
}
|
|
@@ -1542,10 +1813,9 @@
|
|
|
1542
1813
|
const normalizedEditor = typeof normalizedEditorText === "string"
|
|
1543
1814
|
? normalizedEditorText
|
|
1544
1815
|
: normalizeForCompare(sourceTextEl.value);
|
|
1545
|
-
const
|
|
1546
|
-
const inSync = normalizedEditor === targetNormalized;
|
|
1816
|
+
const inSync = normalizedEditor === latestResponseNormalized;
|
|
1547
1817
|
syncBadgeEl.hidden = !inSync;
|
|
1548
|
-
syncBadgeEl.textContent =
|
|
1818
|
+
syncBadgeEl.textContent = "In sync with response";
|
|
1549
1819
|
|
|
1550
1820
|
if (inSync) {
|
|
1551
1821
|
syncBadgeEl.classList.add("sync");
|
|
@@ -2194,6 +2464,40 @@
|
|
|
2194
2464
|
});
|
|
2195
2465
|
}
|
|
2196
2466
|
|
|
2467
|
+
function handleTracePaneScroll() {
|
|
2468
|
+
if (rightView !== "trace") return;
|
|
2469
|
+
traceAutoScroll = shouldStickTraceToBottom();
|
|
2470
|
+
}
|
|
2471
|
+
|
|
2472
|
+
async function handleTracePaneClick(event) {
|
|
2473
|
+
if (rightView !== "trace") return;
|
|
2474
|
+
const target = event.target;
|
|
2475
|
+
const filterBtn = target instanceof Element ? target.closest("[data-trace-filter]") : null;
|
|
2476
|
+
if (filterBtn) {
|
|
2477
|
+
event.preventDefault();
|
|
2478
|
+
const nextFilter = filterBtn.getAttribute("data-trace-filter") || "all";
|
|
2479
|
+
setTraceFilter(nextFilter);
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
const actionBtn = target instanceof Element ? target.closest("[data-trace-action]") : null;
|
|
2483
|
+
if (!actionBtn) return;
|
|
2484
|
+
event.preventDefault();
|
|
2485
|
+
const action = actionBtn.getAttribute("data-trace-action") || "";
|
|
2486
|
+
if (action === "copy") {
|
|
2487
|
+
await copyVisibleWorkingToClipboard();
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
if (action === "load") {
|
|
2491
|
+
loadVisibleWorkingIntoEditor();
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
function attachResponsePaneInteractionHandlers() {
|
|
2496
|
+
if (!critiqueViewEl) return;
|
|
2497
|
+
critiqueViewEl.addEventListener("scroll", handleTracePaneScroll);
|
|
2498
|
+
critiqueViewEl.addEventListener("click", handleTracePaneClick);
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2197
2501
|
function replaceResponsePaneWithClone() {
|
|
2198
2502
|
const currentEl = critiqueViewEl;
|
|
2199
2503
|
if (!currentEl || !currentEl.parentNode || typeof currentEl.cloneNode !== "function") {
|
|
@@ -2207,6 +2511,7 @@
|
|
|
2207
2511
|
|
|
2208
2512
|
currentEl.parentNode.replaceChild(replacement, currentEl);
|
|
2209
2513
|
critiqueViewEl = replacement;
|
|
2514
|
+
attachResponsePaneInteractionHandlers();
|
|
2210
2515
|
return critiqueViewEl;
|
|
2211
2516
|
}
|
|
2212
2517
|
|
|
@@ -2702,7 +3007,134 @@
|
|
|
2702
3007
|
}, delay);
|
|
2703
3008
|
}
|
|
2704
3009
|
|
|
3010
|
+
function shouldStickTraceToBottom() {
|
|
3011
|
+
if (!critiqueViewEl) return true;
|
|
3012
|
+
const remaining = critiqueViewEl.scrollHeight - critiqueViewEl.scrollTop - critiqueViewEl.clientHeight;
|
|
3013
|
+
return remaining < 56;
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
function buildTracePanelHtml() {
|
|
3017
|
+
const state = traceState || createEmptyTraceState();
|
|
3018
|
+
const filter = normalizeTraceFilter(traceFilter);
|
|
3019
|
+
const entries = getTraceEntriesForFilter(filter);
|
|
3020
|
+
const visibleWorking = buildVisibleWorkingText(filter);
|
|
3021
|
+
const hasVisibleContent = Boolean(visibleWorking.trim());
|
|
3022
|
+
const started = formatReferenceTime(state.startedAt || state.updatedAt);
|
|
3023
|
+
const statusLabel = state.status === "running"
|
|
3024
|
+
? "Live"
|
|
3025
|
+
: (state.status === "complete" ? "Complete" : "Idle");
|
|
3026
|
+
const filterMeta = filter === "thinking"
|
|
3027
|
+
? "Thinking only"
|
|
3028
|
+
: (filter === "tools" ? "Tools only" : null);
|
|
3029
|
+
const toolbar = "<div class='trace-toolbar'>"
|
|
3030
|
+
+ "<div class='trace-summary'>"
|
|
3031
|
+
+ "<span class='trace-summary-badge'>Working</span>"
|
|
3032
|
+
+ "<span class='trace-summary-status trace-status-" + escapeHtml(String(state.status || "idle")) + "'>" + escapeHtml(statusLabel) + "</span>"
|
|
3033
|
+
+ (started ? ("<span class='trace-summary-meta'>Started " + escapeHtml(started) + "</span>") : "")
|
|
3034
|
+
+ (filterMeta ? ("<span class='trace-summary-meta'>" + escapeHtml(filterMeta) + "</span>") : "")
|
|
3035
|
+
+ "</div>"
|
|
3036
|
+
+ "<div class='trace-controls'>"
|
|
3037
|
+
+ "<div class='trace-filter-group' role='tablist' aria-label='Working components'>"
|
|
3038
|
+
+ "<button type='button' class='trace-filter-btn" + (filter === "all" ? " is-active" : "") + "' data-trace-filter='all' aria-pressed='" + (filter === "all" ? "true" : "false") + "'>All</button>"
|
|
3039
|
+
+ "<button type='button' class='trace-filter-btn" + (filter === "thinking" ? " is-active" : "") + "' data-trace-filter='thinking' aria-pressed='" + (filter === "thinking" ? "true" : "false") + "'>Thinking</button>"
|
|
3040
|
+
+ "<button type='button' class='trace-filter-btn" + (filter === "tools" ? " is-active" : "") + "' data-trace-filter='tools' aria-pressed='" + (filter === "tools" ? "true" : "false") + "'>Tools</button>"
|
|
3041
|
+
+ "</div>"
|
|
3042
|
+
+ "<button type='button' class='trace-action-btn' data-trace-action='load'" + (hasVisibleContent ? "" : " disabled") + ">Load visible into editor</button>"
|
|
3043
|
+
+ "<button type='button' class='trace-action-btn' data-trace-action='copy'" + (hasVisibleContent ? "" : " disabled") + ">Copy visible</button>"
|
|
3044
|
+
+ "</div>"
|
|
3045
|
+
+ "</div>";
|
|
3046
|
+
|
|
3047
|
+
if (!entries.length) {
|
|
3048
|
+
const emptyMessage = filter === "thinking"
|
|
3049
|
+
? "No thinking steps in this working view yet."
|
|
3050
|
+
: (filter === "tools"
|
|
3051
|
+
? "No tool steps in this working view yet."
|
|
3052
|
+
: (state.status === "running"
|
|
3053
|
+
? "Waiting for the first model or tool update…"
|
|
3054
|
+
: "No live working view yet. Start a run or critique to watch working details here."));
|
|
3055
|
+
return "<div class='trace-panel'>" + toolbar + "<div class='trace-empty'>" + escapeHtml(emptyMessage) + "</div></div>";
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3058
|
+
const cards = entries.map((entry) => {
|
|
3059
|
+
if (entry.type === "assistant") {
|
|
3060
|
+
const sections = [];
|
|
3061
|
+
if (String(entry.thinking || "").trim()) {
|
|
3062
|
+
sections.push(
|
|
3063
|
+
"<div class='trace-section'>"
|
|
3064
|
+
+ "<div class='trace-section-label'>Thinking</div>"
|
|
3065
|
+
+ "<pre class='plain-markdown trace-output'>" + escapeHtml(entry.thinking) + "</pre>"
|
|
3066
|
+
+ "</div>"
|
|
3067
|
+
);
|
|
3068
|
+
}
|
|
3069
|
+
if (filter === "all" && String(entry.text || "").trim()) {
|
|
3070
|
+
sections.push(
|
|
3071
|
+
"<div class='trace-section'>"
|
|
3072
|
+
+ "<div class='trace-section-label'>Response</div>"
|
|
3073
|
+
+ "<pre class='plain-markdown trace-output'>" + escapeHtml(entry.text) + "</pre>"
|
|
3074
|
+
+ "</div>"
|
|
3075
|
+
);
|
|
3076
|
+
}
|
|
3077
|
+
if (!sections.length) {
|
|
3078
|
+
sections.push("<div class='trace-empty-inline'>Waiting for streamed content…</div>");
|
|
3079
|
+
}
|
|
3080
|
+
return "<article class='trace-card trace-card-assistant'>"
|
|
3081
|
+
+ "<div class='trace-card-header'>"
|
|
3082
|
+
+ "<span class='trace-kind-badge'>" + escapeHtml(filter === "thinking" ? "Thinking" : "Assistant") + "</span>"
|
|
3083
|
+
+ "<span class='trace-card-meta'>" + escapeHtml(formatReferenceTime(entry.updatedAt) || "live") + "</span>"
|
|
3084
|
+
+ "<span class='trace-entry-status trace-entry-status-" + escapeHtml(entry.status) + "'>" + escapeHtml(entry.status === "streaming" ? "Live" : "Complete") + "</span>"
|
|
3085
|
+
+ (entry.stopReason ? ("<span class='trace-card-meta'>stop: " + escapeHtml(entry.stopReason) + "</span>") : "")
|
|
3086
|
+
+ "</div>"
|
|
3087
|
+
+ sections.join("")
|
|
3088
|
+
+ "</article>";
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
const title = entry.label || entry.toolName || "tool";
|
|
3092
|
+
const argsSummary = entry.argsSummary
|
|
3093
|
+
? "<div class='trace-section'><div class='trace-section-label'>Input</div><pre class='plain-markdown trace-output'>" + escapeHtml(entry.argsSummary) + "</pre></div>"
|
|
3094
|
+
: "";
|
|
3095
|
+
const output = entry.output
|
|
3096
|
+
? "<div class='trace-section'><div class='trace-section-label'>Output</div><pre class='plain-markdown trace-output'>" + escapeHtml(entry.output) + "</pre></div>"
|
|
3097
|
+
: "<div class='trace-empty-inline'>No output yet.</div>";
|
|
3098
|
+
const toolStatusLabel = entry.isError
|
|
3099
|
+
? "Error"
|
|
3100
|
+
: (entry.status === "streaming" || entry.status === "pending" ? "Live" : "Complete");
|
|
3101
|
+
return "<article class='trace-card trace-card-tool'>"
|
|
3102
|
+
+ "<div class='trace-card-header'>"
|
|
3103
|
+
+ "<span class='trace-kind-badge'>" + escapeHtml(entry.toolName || "tool") + "</span>"
|
|
3104
|
+
+ "<span class='trace-card-title'>" + escapeHtml(title) + "</span>"
|
|
3105
|
+
+ "<span class='trace-card-meta'>" + escapeHtml(formatReferenceTime(entry.updatedAt) || "live") + "</span>"
|
|
3106
|
+
+ "<span class='trace-entry-status trace-entry-status-" + escapeHtml(entry.status) + "'>" + escapeHtml(toolStatusLabel) + "</span>"
|
|
3107
|
+
+ "</div>"
|
|
3108
|
+
+ argsSummary
|
|
3109
|
+
+ output
|
|
3110
|
+
+ "</article>";
|
|
3111
|
+
}).join("");
|
|
3112
|
+
|
|
3113
|
+
return "<div class='trace-panel'>" + toolbar + "<div class='trace-list'>" + cards + "</div></div>";
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
function renderTraceView() {
|
|
3117
|
+
if (!critiqueViewEl) return;
|
|
3118
|
+
const shouldStick = traceAutoScroll || shouldStickTraceToBottom();
|
|
3119
|
+
const previousScrollTop = critiqueViewEl.scrollTop;
|
|
3120
|
+
finishPreviewRender(critiqueViewEl);
|
|
3121
|
+
critiqueViewEl.innerHTML = buildTracePanelHtml();
|
|
3122
|
+
critiqueViewEl.classList.remove("response-scroll-resetting");
|
|
3123
|
+
if (shouldStick) {
|
|
3124
|
+
critiqueViewEl.scrollTop = critiqueViewEl.scrollHeight;
|
|
3125
|
+
traceAutoScroll = true;
|
|
3126
|
+
} else {
|
|
3127
|
+
critiqueViewEl.scrollTop = previousScrollTop;
|
|
3128
|
+
}
|
|
3129
|
+
scheduleResponsePaneRepaintNudge();
|
|
3130
|
+
}
|
|
3131
|
+
|
|
2705
3132
|
function renderActiveResult() {
|
|
3133
|
+
if (rightView === "trace") {
|
|
3134
|
+
renderTraceView();
|
|
3135
|
+
return;
|
|
3136
|
+
}
|
|
3137
|
+
|
|
2706
3138
|
if (rightView === "editor-preview") {
|
|
2707
3139
|
const editorText = prepareEditorTextForPreview(sourceTextEl.value || "");
|
|
2708
3140
|
if (!editorText.trim()) {
|
|
@@ -2721,17 +3153,6 @@
|
|
|
2721
3153
|
return;
|
|
2722
3154
|
}
|
|
2723
3155
|
|
|
2724
|
-
if (rightView === "thinking") {
|
|
2725
|
-
const thinking = latestResponseThinking;
|
|
2726
|
-
finishPreviewRender(critiqueViewEl);
|
|
2727
|
-
critiqueViewEl.innerHTML = thinking && thinking.trim()
|
|
2728
|
-
? buildPlainMarkdownHtml(thinking)
|
|
2729
|
-
: "<pre class='plain-markdown'>No thinking available for this response.</pre>";
|
|
2730
|
-
applyPendingResponseScrollReset();
|
|
2731
|
-
scheduleResponsePaneRepaintNudge();
|
|
2732
|
-
return;
|
|
2733
|
-
}
|
|
2734
|
-
|
|
2735
3156
|
const markdown = latestResponseMarkdown;
|
|
2736
3157
|
if (!markdown || !markdown.trim()) {
|
|
2737
3158
|
finishPreviewRender(critiqueViewEl);
|
|
@@ -2775,55 +3196,43 @@
|
|
|
2775
3196
|
|
|
2776
3197
|
function updateResultActionButtons(normalizedEditorText) {
|
|
2777
3198
|
const hasResponse = latestResponseHasContent;
|
|
2778
|
-
const hasThinking = Boolean(latestResponseThinking && latestResponseThinking.trim());
|
|
2779
3199
|
const normalizedEditor = typeof normalizedEditorText === "string"
|
|
2780
3200
|
? normalizedEditorText
|
|
2781
3201
|
: normalizeForCompare(sourceTextEl.value);
|
|
2782
3202
|
const responseLoaded = hasResponse && normalizedEditor === latestResponseNormalized;
|
|
2783
|
-
const thinkingLoaded = hasThinking && normalizedEditor === latestResponseThinkingNormalized;
|
|
2784
3203
|
const isCritiqueResponse = hasResponse && latestResponseIsStructuredCritique;
|
|
2785
|
-
const
|
|
3204
|
+
const showingTrace = rightView === "trace";
|
|
3205
|
+
|
|
3206
|
+
if (responseWrapEl) {
|
|
3207
|
+
responseWrapEl.hidden = showingTrace;
|
|
3208
|
+
}
|
|
2786
3209
|
|
|
2787
3210
|
const critiqueNotes = isCritiqueResponse ? latestCritiqueNotes : "";
|
|
2788
3211
|
const critiqueNotesLoaded = Boolean(critiqueNotes) && normalizedEditor === latestCritiqueNotesNormalized;
|
|
2789
3212
|
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
loadCritiqueFullBtn.hidden = true;
|
|
2794
|
-
|
|
2795
|
-
loadResponseBtn.disabled = uiBusy || !hasThinking || thinkingLoaded;
|
|
2796
|
-
loadResponseBtn.textContent = !hasThinking
|
|
2797
|
-
? "Thinking unavailable"
|
|
2798
|
-
: (thinkingLoaded ? "Thinking already in editor" : "Load thinking into editor");
|
|
2799
|
-
|
|
2800
|
-
copyResponseBtn.disabled = uiBusy || !hasThinking;
|
|
2801
|
-
copyResponseBtn.textContent = "Copy thinking text";
|
|
2802
|
-
} else {
|
|
2803
|
-
loadResponseBtn.hidden = isCritiqueResponse;
|
|
2804
|
-
loadCritiqueNotesBtn.hidden = !isCritiqueResponse;
|
|
2805
|
-
loadCritiqueFullBtn.hidden = !isCritiqueResponse;
|
|
3213
|
+
loadResponseBtn.hidden = isCritiqueResponse;
|
|
3214
|
+
loadCritiqueNotesBtn.hidden = !isCritiqueResponse;
|
|
3215
|
+
loadCritiqueFullBtn.hidden = !isCritiqueResponse;
|
|
2806
3216
|
|
|
2807
|
-
|
|
2808
|
-
|
|
3217
|
+
loadResponseBtn.disabled = uiBusy || !hasResponse || responseLoaded || isCritiqueResponse;
|
|
3218
|
+
loadResponseBtn.textContent = responseLoaded ? "Response already in editor" : "Load response into editor";
|
|
2809
3219
|
|
|
2810
|
-
|
|
2811
|
-
|
|
3220
|
+
loadCritiqueNotesBtn.disabled = uiBusy || !isCritiqueResponse || !critiqueNotes || critiqueNotesLoaded;
|
|
3221
|
+
loadCritiqueNotesBtn.textContent = critiqueNotesLoaded ? "Critique notes already in editor" : "Load critique notes into editor";
|
|
2812
3222
|
|
|
2813
|
-
|
|
2814
|
-
|
|
3223
|
+
loadCritiqueFullBtn.disabled = uiBusy || !isCritiqueResponse || responseLoaded;
|
|
3224
|
+
loadCritiqueFullBtn.textContent = responseLoaded ? "Full critique already in editor" : "Load full critique into editor";
|
|
2815
3225
|
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
}
|
|
3226
|
+
copyResponseBtn.disabled = uiBusy || !hasResponse;
|
|
3227
|
+
copyResponseBtn.textContent = "Copy response text";
|
|
2819
3228
|
|
|
2820
3229
|
const rightPaneShowsPreview = rightView === "preview" || rightView === "editor-preview";
|
|
2821
3230
|
const exportText = rightView === "editor-preview" ? prepareEditorTextForPreview(sourceTextEl.value) : latestResponseMarkdown;
|
|
2822
3231
|
const canExportPdf = rightPaneShowsPreview && Boolean(String(exportText || "").trim());
|
|
2823
3232
|
if (exportPdfBtn) {
|
|
2824
3233
|
exportPdfBtn.disabled = uiBusy || pdfExportInProgress || !canExportPdf;
|
|
2825
|
-
if (rightView === "
|
|
2826
|
-
exportPdfBtn.title = "
|
|
3234
|
+
if (rightView === "trace") {
|
|
3235
|
+
exportPdfBtn.title = "Working view does not support PDF export.";
|
|
2827
3236
|
} else if (rightView === "markdown") {
|
|
2828
3237
|
exportPdfBtn.title = "Switch right pane to Response (Preview) or Editor (Preview) to export PDF.";
|
|
2829
3238
|
} else if (!canExportPdf) {
|
|
@@ -3052,12 +3461,16 @@
|
|
|
3052
3461
|
}
|
|
3053
3462
|
|
|
3054
3463
|
function setRightView(nextView) {
|
|
3464
|
+
const previousView = rightView;
|
|
3055
3465
|
rightView = nextView === "preview"
|
|
3056
3466
|
? "preview"
|
|
3057
3467
|
: (nextView === "editor-preview"
|
|
3058
3468
|
? "editor-preview"
|
|
3059
|
-
: (nextView === "thinking" ? "
|
|
3469
|
+
: ((nextView === "trace" || nextView === "thinking") ? "trace" : "markdown"));
|
|
3060
3470
|
rightViewSelect.value = rightView;
|
|
3471
|
+
if (rightView === "trace" && previousView !== "trace") {
|
|
3472
|
+
traceAutoScroll = true;
|
|
3473
|
+
}
|
|
3061
3474
|
|
|
3062
3475
|
if (rightView !== "editor-preview" && responseEditorPreviewTimer) {
|
|
3063
3476
|
window.clearTimeout(responseEditorPreviewTimer);
|
|
@@ -4896,6 +5309,7 @@
|
|
|
4896
5309
|
|| kind === "blockquote"
|
|
4897
5310
|
|| kind === "list"
|
|
4898
5311
|
|| kind === "math"
|
|
5312
|
+
|| kind === "code"
|
|
4899
5313
|
|| kind === "code-line"
|
|
4900
5314
|
|| kind === "diff-line"
|
|
4901
5315
|
|| kind === "text-line";
|
|
@@ -6009,7 +6423,7 @@
|
|
|
6009
6423
|
|
|
6010
6424
|
function buildPreviewSelectionDisplayMap(blockText, kind) {
|
|
6011
6425
|
const body = buildPreviewSelectionSourceBody(blockText, kind);
|
|
6012
|
-
if (kind === "code-line" || kind === "diff-line" || kind === "text-line") {
|
|
6426
|
+
if (kind === "code" || kind === "code-line" || kind === "diff-line" || kind === "text-line") {
|
|
6013
6427
|
return buildLiteralPreviewDisplayMap(body.text, body.rawOffsets);
|
|
6014
6428
|
}
|
|
6015
6429
|
const inlineMap = buildPreviewInlineDisplayMap(body.text, body.rawOffsets);
|
|
@@ -6281,6 +6695,15 @@
|
|
|
6281
6695
|
};
|
|
6282
6696
|
}
|
|
6283
6697
|
|
|
6698
|
+
function getChunkText(startLineIndex, endLineIndex) {
|
|
6699
|
+
const safeStartLine = Math.max(0, Math.min(startLineIndex, Math.max(0, lines.length - 1)));
|
|
6700
|
+
const safeEndLine = Math.max(safeStartLine, Math.min(endLineIndex, Math.max(0, lines.length - 1)));
|
|
6701
|
+
return source.slice(
|
|
6702
|
+
lineOffsets[safeStartLine] || 0,
|
|
6703
|
+
(lineOffsets[safeEndLine] || 0) + getLine(safeEndLine).length,
|
|
6704
|
+
);
|
|
6705
|
+
}
|
|
6706
|
+
|
|
6284
6707
|
const blocks = [];
|
|
6285
6708
|
let index = 0;
|
|
6286
6709
|
|
|
@@ -6413,7 +6836,11 @@
|
|
|
6413
6836
|
}
|
|
6414
6837
|
endParagraph = i;
|
|
6415
6838
|
}
|
|
6416
|
-
|
|
6839
|
+
const paragraphText = getChunkText(index, endParagraph);
|
|
6840
|
+
const markdownFigureCaption = annotationHelpers && typeof annotationHelpers.extractStandaloneMarkdownImageCaptionText === "function"
|
|
6841
|
+
? annotationHelpers.extractStandaloneMarkdownImageCaptionText(paragraphText)
|
|
6842
|
+
: null;
|
|
6843
|
+
blocks.push(makeBlock(markdownFigureCaption != null ? "figure" : "paragraph", index, endParagraph));
|
|
6417
6844
|
index = endParagraph + 1;
|
|
6418
6845
|
}
|
|
6419
6846
|
|
|
@@ -6732,6 +7159,42 @@
|
|
|
6732
7159
|
});
|
|
6733
7160
|
}
|
|
6734
7161
|
|
|
7162
|
+
function isPreviewMediaOnlyParagraphElement(element) {
|
|
7163
|
+
if (!element || !(element instanceof Element)) return false;
|
|
7164
|
+
if ((element.tagName ? element.tagName.toUpperCase() : "") !== "P") return false;
|
|
7165
|
+
|
|
7166
|
+
let hasMedia = false;
|
|
7167
|
+
for (const childNode of Array.from(element.childNodes || [])) {
|
|
7168
|
+
if (!childNode) continue;
|
|
7169
|
+
if (childNode.nodeType === Node.TEXT_NODE) {
|
|
7170
|
+
if (normalizeVisiblePreviewText(childNode.nodeValue || "")) {
|
|
7171
|
+
return false;
|
|
7172
|
+
}
|
|
7173
|
+
continue;
|
|
7174
|
+
}
|
|
7175
|
+
if (!(childNode instanceof Element)) continue;
|
|
7176
|
+
|
|
7177
|
+
const childTag = childNode.tagName ? childNode.tagName.toUpperCase() : "";
|
|
7178
|
+
if (childTag === "BR") continue;
|
|
7179
|
+
if (childTag === "IMG" || childTag === "EMBED" || childTag === "OBJECT" || childTag === "IFRAME" || childTag === "CANVAS") {
|
|
7180
|
+
hasMedia = true;
|
|
7181
|
+
continue;
|
|
7182
|
+
}
|
|
7183
|
+
|
|
7184
|
+
const nestedMedia = typeof childNode.querySelector === "function"
|
|
7185
|
+
? childNode.querySelector("img, embed, object, iframe, canvas")
|
|
7186
|
+
: null;
|
|
7187
|
+
if (nestedMedia && !buildNormalizedPreviewSearchText(childNode)) {
|
|
7188
|
+
hasMedia = true;
|
|
7189
|
+
continue;
|
|
7190
|
+
}
|
|
7191
|
+
|
|
7192
|
+
return false;
|
|
7193
|
+
}
|
|
7194
|
+
|
|
7195
|
+
return hasMedia;
|
|
7196
|
+
}
|
|
7197
|
+
|
|
6735
7198
|
function getPreviewCommentTargetKind(element) {
|
|
6736
7199
|
if (!element || !(element instanceof Element)) return "";
|
|
6737
7200
|
if (element.classList && element.classList.contains("studio-mathjax-fallback-display")) {
|
|
@@ -6742,12 +7205,12 @@
|
|
|
6742
7205
|
}
|
|
6743
7206
|
const tag = element.tagName ? element.tagName.toUpperCase() : "";
|
|
6744
7207
|
if (/^H[1-6]$/.test(tag)) return "heading";
|
|
6745
|
-
if (tag === "P") return "paragraph";
|
|
7208
|
+
if (tag === "P") return isPreviewMediaOnlyParagraphElement(element) ? "figure" : "paragraph";
|
|
6746
7209
|
if (tag === "FIGURE") {
|
|
6747
7210
|
if (element.classList && element.classList.contains("studio-algorithm-block")) {
|
|
6748
7211
|
return "algorithm";
|
|
6749
7212
|
}
|
|
6750
|
-
return
|
|
7213
|
+
return "figure";
|
|
6751
7214
|
}
|
|
6752
7215
|
if (tag === "DIV" && element.classList) {
|
|
6753
7216
|
if (element.classList.contains("studio-display-equation")) {
|
|
@@ -6854,6 +7317,14 @@
|
|
|
6854
7317
|
const match = blockText.trim().match(/^\\(newpage|pagebreak|clearpage)/i);
|
|
6855
7318
|
return match ? String(match[1] || "").toLowerCase() : "page-break";
|
|
6856
7319
|
}
|
|
7320
|
+
if (sourceBlock.kind === "figure") {
|
|
7321
|
+
const figureCaption = annotationHelpers && typeof annotationHelpers.extractStandaloneMarkdownImageCaptionText === "function"
|
|
7322
|
+
? annotationHelpers.extractStandaloneMarkdownImageCaptionText(blockText)
|
|
7323
|
+
: null;
|
|
7324
|
+
if (figureCaption != null) {
|
|
7325
|
+
return normalizeVisiblePreviewText(figureCaption);
|
|
7326
|
+
}
|
|
7327
|
+
}
|
|
6857
7328
|
if (supportsPreviewSelectionCommentsForBlockKind(sourceBlock.kind)) {
|
|
6858
7329
|
return normalizeVisiblePreviewText(buildPreviewSelectionDisplayMap(blockText, sourceBlock.kind).text);
|
|
6859
7330
|
}
|
|
@@ -6874,6 +7345,23 @@
|
|
|
6874
7345
|
return normalizeVisiblePreviewText(blockText);
|
|
6875
7346
|
}
|
|
6876
7347
|
|
|
7348
|
+
function getPreviewFigureSearchText(element) {
|
|
7349
|
+
if (!element || !(element instanceof Element)) return "";
|
|
7350
|
+
const visibleText = buildNormalizedPreviewSearchText(element);
|
|
7351
|
+
if (visibleText) return visibleText;
|
|
7352
|
+
|
|
7353
|
+
const imageNodes = (element.tagName ? element.tagName.toUpperCase() : "") === "IMG"
|
|
7354
|
+
? [element]
|
|
7355
|
+
: (typeof element.querySelectorAll === "function" ? Array.from(element.querySelectorAll("img[alt], img[title]")) : []);
|
|
7356
|
+
const altText = imageNodes
|
|
7357
|
+
.filter((imageEl) => imageEl instanceof Element)
|
|
7358
|
+
.map((imageEl) => imageEl.getAttribute("alt") || imageEl.getAttribute("title") || "")
|
|
7359
|
+
.map((text) => normalizeVisiblePreviewText(text))
|
|
7360
|
+
.filter(Boolean)
|
|
7361
|
+
.join(" ");
|
|
7362
|
+
return altText;
|
|
7363
|
+
}
|
|
7364
|
+
|
|
6877
7365
|
function getNormalizedPreviewCommentTargetText(targetEntry) {
|
|
6878
7366
|
if (!targetEntry) return "";
|
|
6879
7367
|
if (typeof targetEntry.normalizedText === "string") return targetEntry.normalizedText;
|
|
@@ -6882,6 +7370,10 @@
|
|
|
6882
7370
|
targetEntry.normalizedText = String(element && element.getAttribute ? (element.getAttribute("data-page-break-kind") || "page-break") : "page-break").toLowerCase();
|
|
6883
7371
|
return targetEntry.normalizedText;
|
|
6884
7372
|
}
|
|
7373
|
+
if (targetEntry.kind === "figure") {
|
|
7374
|
+
targetEntry.normalizedText = getPreviewFigureSearchText(targetEntry.element);
|
|
7375
|
+
return targetEntry.normalizedText;
|
|
7376
|
+
}
|
|
6885
7377
|
targetEntry.normalizedText = buildNormalizedPreviewSearchText(targetEntry.element);
|
|
6886
7378
|
return targetEntry.normalizedText;
|
|
6887
7379
|
}
|
|
@@ -8580,6 +9072,10 @@
|
|
|
8580
9072
|
}
|
|
8581
9073
|
}
|
|
8582
9074
|
|
|
9075
|
+
if (message.traceState) {
|
|
9076
|
+
replaceTraceState(message.traceState);
|
|
9077
|
+
}
|
|
9078
|
+
|
|
8583
9079
|
let appliedHistory = false;
|
|
8584
9080
|
if (Array.isArray(message.responseHistory)) {
|
|
8585
9081
|
appliedHistory = setResponseHistory(message.responseHistory, {
|
|
@@ -8626,6 +9122,26 @@
|
|
|
8626
9122
|
return;
|
|
8627
9123
|
}
|
|
8628
9124
|
|
|
9125
|
+
if (message.type === "trace_reset") {
|
|
9126
|
+
replaceTraceState(message.trace);
|
|
9127
|
+
return;
|
|
9128
|
+
}
|
|
9129
|
+
|
|
9130
|
+
if (message.type === "trace_status") {
|
|
9131
|
+
updateTraceStatusFromMessage(message);
|
|
9132
|
+
return;
|
|
9133
|
+
}
|
|
9134
|
+
|
|
9135
|
+
if (message.type === "trace_entry_upsert") {
|
|
9136
|
+
upsertTraceEntry(message.entry);
|
|
9137
|
+
return;
|
|
9138
|
+
}
|
|
9139
|
+
|
|
9140
|
+
if (message.type === "trace_assistant_delta") {
|
|
9141
|
+
appendTraceAssistantDelta(message.entryId, message.deltaKind, message.delta, message.updatedAt);
|
|
9142
|
+
return;
|
|
9143
|
+
}
|
|
9144
|
+
|
|
8629
9145
|
if (message.type === "request_started") {
|
|
8630
9146
|
pendingRequestId = typeof message.requestId === "string" ? message.requestId : pendingRequestId;
|
|
8631
9147
|
pendingKind = typeof message.kind === "string" ? message.kind : "unknown";
|
|
@@ -9323,6 +9839,8 @@
|
|
|
9323
9839
|
setRightView(rightViewSelect.value);
|
|
9324
9840
|
});
|
|
9325
9841
|
|
|
9842
|
+
attachResponsePaneInteractionHandlers();
|
|
9843
|
+
|
|
9326
9844
|
followSelect.addEventListener("change", () => {
|
|
9327
9845
|
followLatest = followSelect.value !== "off";
|
|
9328
9846
|
if (followLatest && queuedLatestResponse) {
|
|
@@ -9565,17 +10083,6 @@
|
|
|
9565
10083
|
});
|
|
9566
10084
|
|
|
9567
10085
|
loadResponseBtn.addEventListener("click", () => {
|
|
9568
|
-
if (rightView === "thinking") {
|
|
9569
|
-
if (!latestResponseThinking.trim()) {
|
|
9570
|
-
setStatus("No thinking available for the selected response.", "warning");
|
|
9571
|
-
return;
|
|
9572
|
-
}
|
|
9573
|
-
setEditorText(latestResponseThinking, { preserveScroll: false, preserveSelection: false });
|
|
9574
|
-
setSourceState({ source: "blank", label: "assistant thinking", path: null });
|
|
9575
|
-
setStatus("Loaded thinking into editor.", "success");
|
|
9576
|
-
return;
|
|
9577
|
-
}
|
|
9578
|
-
|
|
9579
10086
|
if (!latestResponseMarkdown.trim()) {
|
|
9580
10087
|
setStatus("No response available yet.", "warning");
|
|
9581
10088
|
return;
|
|
@@ -9614,15 +10121,15 @@
|
|
|
9614
10121
|
});
|
|
9615
10122
|
|
|
9616
10123
|
copyResponseBtn.addEventListener("click", async () => {
|
|
9617
|
-
const content =
|
|
10124
|
+
const content = latestResponseMarkdown;
|
|
9618
10125
|
if (!content.trim()) {
|
|
9619
|
-
setStatus(
|
|
10126
|
+
setStatus("No response available yet.", "warning");
|
|
9620
10127
|
return;
|
|
9621
10128
|
}
|
|
9622
10129
|
|
|
9623
10130
|
try {
|
|
9624
10131
|
await navigator.clipboard.writeText(content);
|
|
9625
|
-
setStatus(
|
|
10132
|
+
setStatus("Copied response text.", "success");
|
|
9626
10133
|
} catch (error) {
|
|
9627
10134
|
setStatus("Clipboard write failed.", "warning");
|
|
9628
10135
|
}
|