pi-studio 0.5.3 → 0.5.5
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 +2 -1
- package/index.ts +165 -33
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -88,6 +88,27 @@ All notable changes to `pi-studio` are documented here.
|
|
|
88
88
|
|
|
89
89
|
## [Unreleased]
|
|
90
90
|
|
|
91
|
+
## [0.5.5] — 2026-03-09
|
|
92
|
+
|
|
93
|
+
### Fixed
|
|
94
|
+
- Improved raw-editor caret/overlay alignment in Syntax highlight mode:
|
|
95
|
+
- width-neutral annotation highlight styling
|
|
96
|
+
- more textarea-like wrap behavior in the highlight overlay
|
|
97
|
+
- preserved empty trailing lines in highlighted output so end-of-file blank lines stay aligned
|
|
98
|
+
- reduced raw overlay metric drift for comment/quote styling
|
|
99
|
+
|
|
100
|
+
## [0.5.4] — 2026-03-09
|
|
101
|
+
|
|
102
|
+
### Added
|
|
103
|
+
- New right-pane **Thinking (Raw)** view for assistant/model thinking when available.
|
|
104
|
+
|
|
105
|
+
### Changed
|
|
106
|
+
- Response history and latest-response syncing now preserve associated thinking content.
|
|
107
|
+
- In Thinking view, right-pane actions adapt to the selected reasoning trace:
|
|
108
|
+
- **Load thinking into editor**
|
|
109
|
+
- **Copy thinking text**
|
|
110
|
+
- thinking-aware reference/sync badges
|
|
111
|
+
|
|
91
112
|
## [0.5.3] — 2026-03-06
|
|
92
113
|
|
|
93
114
|
### Added
|
package/README.md
CHANGED
|
@@ -14,11 +14,12 @@ Experimental extension for [pi](https://github.com/badlogic/pi-mono) that opens
|
|
|
14
14
|
|
|
15
15
|
## What it does
|
|
16
16
|
|
|
17
|
-
- Opens a two-pane browser workspace: **Editor** (left) + **Response/Editor Preview** (right)
|
|
17
|
+
- Opens a two-pane browser workspace: **Editor** (left) + **Response/Thinking/Editor Preview** (right)
|
|
18
18
|
- Runs editor text directly, or asks for structured critique (auto/writing/code focus)
|
|
19
19
|
- Browses response history (`Prev/Next`) and loads either:
|
|
20
20
|
- response text
|
|
21
21
|
- critique notes/full critique
|
|
22
|
+
- assistant thinking (when available)
|
|
22
23
|
- the prompt that generated a selected response
|
|
23
24
|
- Supports an annotation workflow for `[an: ...]` markers:
|
|
24
25
|
- inserts/removes the annotated-reply header
|
package/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ interface ActiveStudioRequest {
|
|
|
32
32
|
|
|
33
33
|
interface LastStudioResponse {
|
|
34
34
|
markdown: string;
|
|
35
|
+
thinking: string | null;
|
|
35
36
|
timestamp: number;
|
|
36
37
|
kind: StudioRequestKind;
|
|
37
38
|
}
|
|
@@ -39,6 +40,7 @@ interface LastStudioResponse {
|
|
|
39
40
|
interface StudioResponseHistoryItem {
|
|
40
41
|
id: string;
|
|
41
42
|
markdown: string;
|
|
43
|
+
thinking: string | null;
|
|
42
44
|
timestamp: number;
|
|
43
45
|
kind: StudioRequestKind;
|
|
44
46
|
prompt: string | null;
|
|
@@ -1259,6 +1261,27 @@ function extractAssistantText(message: unknown): string | null {
|
|
|
1259
1261
|
return text.length > 0 ? text : null;
|
|
1260
1262
|
}
|
|
1261
1263
|
|
|
1264
|
+
function extractAssistantThinking(message: unknown): string | null {
|
|
1265
|
+
const msg = message as {
|
|
1266
|
+
role?: string;
|
|
1267
|
+
content?: Array<{ type?: string; thinking?: string }> | string;
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
if (!msg || msg.role !== "assistant" || !Array.isArray(msg.content)) return null;
|
|
1271
|
+
|
|
1272
|
+
const blocks: string[] = [];
|
|
1273
|
+
for (const part of msg.content) {
|
|
1274
|
+
if (!part || typeof part !== "object") continue;
|
|
1275
|
+
if (part.type !== "thinking") continue;
|
|
1276
|
+
if (typeof part.thinking === "string" && part.thinking.trim()) {
|
|
1277
|
+
blocks.push(part.thinking);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
const thinking = blocks.join("\n\n").trim();
|
|
1282
|
+
return thinking.length > 0 ? thinking : null;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1262
1285
|
function extractLatestAssistantFromEntries(entries: SessionEntry[]): string | null {
|
|
1263
1286
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
1264
1287
|
const entry = entries[i];
|
|
@@ -1330,9 +1353,11 @@ function buildResponseHistoryFromEntries(entries: SessionEntry[], limit = RESPON
|
|
|
1330
1353
|
if (role !== "assistant") continue;
|
|
1331
1354
|
const markdown = extractAssistantText(message);
|
|
1332
1355
|
if (!markdown) continue;
|
|
1356
|
+
const thinking = extractAssistantThinking(message);
|
|
1333
1357
|
history.push({
|
|
1334
1358
|
id: typeof (entry as { id?: unknown }).id === "string" ? (entry as { id: string }).id : randomUUID(),
|
|
1335
1359
|
markdown,
|
|
1360
|
+
thinking,
|
|
1336
1361
|
timestamp: parseEntryTimestamp((entry as { timestamp?: unknown }).timestamp),
|
|
1337
1362
|
kind: inferStudioResponseKind(markdown),
|
|
1338
1363
|
prompt: lastUserPrompt,
|
|
@@ -2140,6 +2165,7 @@ ${cssVarsBlock}
|
|
|
2140
2165
|
border-radius: 8px;
|
|
2141
2166
|
background: var(--editor-bg);
|
|
2142
2167
|
overflow: hidden;
|
|
2168
|
+
overscroll-behavior: none;
|
|
2143
2169
|
}
|
|
2144
2170
|
|
|
2145
2171
|
.editor-highlight {
|
|
@@ -2152,7 +2178,9 @@ ${cssVarsBlock}
|
|
|
2152
2178
|
overflow: auto;
|
|
2153
2179
|
pointer-events: none;
|
|
2154
2180
|
white-space: pre-wrap;
|
|
2155
|
-
word-break:
|
|
2181
|
+
word-break: normal;
|
|
2182
|
+
overflow-wrap: break-word;
|
|
2183
|
+
overscroll-behavior: none;
|
|
2156
2184
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
2157
2185
|
font-size: 13px;
|
|
2158
2186
|
line-height: 1.45;
|
|
@@ -2172,6 +2200,7 @@ ${cssVarsBlock}
|
|
|
2172
2200
|
background: transparent;
|
|
2173
2201
|
resize: none;
|
|
2174
2202
|
outline: none;
|
|
2203
|
+
overscroll-behavior: none;
|
|
2175
2204
|
}
|
|
2176
2205
|
|
|
2177
2206
|
#sourceText.highlight-active {
|
|
@@ -2215,7 +2244,7 @@ ${cssVarsBlock}
|
|
|
2215
2244
|
|
|
2216
2245
|
.hl-code-com {
|
|
2217
2246
|
color: var(--syntax-comment);
|
|
2218
|
-
font-style:
|
|
2247
|
+
font-style: normal;
|
|
2219
2248
|
}
|
|
2220
2249
|
|
|
2221
2250
|
.hl-code-var,
|
|
@@ -2244,7 +2273,7 @@ ${cssVarsBlock}
|
|
|
2244
2273
|
|
|
2245
2274
|
.hl-quote {
|
|
2246
2275
|
color: var(--md-quote);
|
|
2247
|
-
font-style:
|
|
2276
|
+
font-style: normal;
|
|
2248
2277
|
}
|
|
2249
2278
|
|
|
2250
2279
|
.hl-link {
|
|
@@ -2259,9 +2288,10 @@ ${cssVarsBlock}
|
|
|
2259
2288
|
.hl-annotation {
|
|
2260
2289
|
color: var(--accent);
|
|
2261
2290
|
background: var(--accent-soft);
|
|
2262
|
-
border:
|
|
2291
|
+
border: 0;
|
|
2263
2292
|
border-radius: 4px;
|
|
2264
|
-
padding: 0
|
|
2293
|
+
padding: 0;
|
|
2294
|
+
box-shadow: inset 0 0 0 1px var(--marker-border);
|
|
2265
2295
|
}
|
|
2266
2296
|
|
|
2267
2297
|
.hl-annotation-muted {
|
|
@@ -2889,6 +2919,7 @@ ${cssVarsBlock}
|
|
|
2889
2919
|
<option value="markdown">Response (Raw)</option>
|
|
2890
2920
|
<option value="preview" selected>Response (Preview)</option>
|
|
2891
2921
|
<option value="editor-preview">Editor (Preview)</option>
|
|
2922
|
+
<option value="thinking">Thinking (Raw)</option>
|
|
2892
2923
|
</select>
|
|
2893
2924
|
</div>
|
|
2894
2925
|
<div class="section-header-actions">
|
|
@@ -3043,11 +3074,13 @@ ${cssVarsBlock}
|
|
|
3043
3074
|
let followLatest = true;
|
|
3044
3075
|
let queuedLatestResponse = null;
|
|
3045
3076
|
let latestResponseMarkdown = "";
|
|
3077
|
+
let latestResponseThinking = "";
|
|
3046
3078
|
let latestResponseTimestamp = 0;
|
|
3047
3079
|
let latestResponseKind = "annotation";
|
|
3048
3080
|
let latestResponseIsStructuredCritique = false;
|
|
3049
3081
|
let latestResponseHasContent = false;
|
|
3050
3082
|
let latestResponseNormalized = "";
|
|
3083
|
+
let latestResponseThinkingNormalized = "";
|
|
3051
3084
|
let latestCritiqueNotes = "";
|
|
3052
3085
|
let latestCritiqueNotesNormalized = "";
|
|
3053
3086
|
let responseHistory = [];
|
|
@@ -3149,6 +3182,7 @@ ${cssVarsBlock}
|
|
|
3149
3182
|
let editorHighlightRenderRaf = null;
|
|
3150
3183
|
let annotationsEnabled = true;
|
|
3151
3184
|
const ANNOTATION_MARKER_REGEX = /\\[an:\\s*([^\\]\\n]+?)\\]/gi;
|
|
3185
|
+
const EMPTY_OVERLAY_LINE = "\\u200b";
|
|
3152
3186
|
const MERMAID_CDN_URL = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
|
3153
3187
|
const MERMAID_CONFIG = ${JSON.stringify(mermaidConfig)};
|
|
3154
3188
|
const MERMAID_UNAVAILABLE_MESSAGE = "Mermaid renderer unavailable. Showing mermaid blocks as code.";
|
|
@@ -3672,10 +3706,14 @@ ${cssVarsBlock}
|
|
|
3672
3706
|
const prompt = typeof item.prompt === "string"
|
|
3673
3707
|
? item.prompt
|
|
3674
3708
|
: (item.prompt == null ? null : String(item.prompt));
|
|
3709
|
+
const thinking = typeof item.thinking === "string"
|
|
3710
|
+
? item.thinking
|
|
3711
|
+
: (item.thinking == null ? null : String(item.thinking));
|
|
3675
3712
|
|
|
3676
3713
|
return {
|
|
3677
3714
|
id,
|
|
3678
3715
|
markdown,
|
|
3716
|
+
thinking,
|
|
3679
3717
|
timestamp,
|
|
3680
3718
|
kind: normalizeHistoryKind(item.kind),
|
|
3681
3719
|
prompt,
|
|
@@ -3690,11 +3728,13 @@ ${cssVarsBlock}
|
|
|
3690
3728
|
|
|
3691
3729
|
function clearActiveResponseView() {
|
|
3692
3730
|
latestResponseMarkdown = "";
|
|
3731
|
+
latestResponseThinking = "";
|
|
3693
3732
|
latestResponseKind = "annotation";
|
|
3694
3733
|
latestResponseTimestamp = 0;
|
|
3695
3734
|
latestResponseIsStructuredCritique = false;
|
|
3696
3735
|
latestResponseHasContent = false;
|
|
3697
3736
|
latestResponseNormalized = "";
|
|
3737
|
+
latestResponseThinkingNormalized = "";
|
|
3698
3738
|
latestCritiqueNotes = "";
|
|
3699
3739
|
latestCritiqueNotesNormalized = "";
|
|
3700
3740
|
refreshResponseUi();
|
|
@@ -3731,7 +3771,7 @@ ${cssVarsBlock}
|
|
|
3731
3771
|
clearActiveResponseView();
|
|
3732
3772
|
return false;
|
|
3733
3773
|
}
|
|
3734
|
-
handleIncomingResponse(item.markdown, item.kind, item.timestamp);
|
|
3774
|
+
handleIncomingResponse(item.markdown, item.kind, item.timestamp, item.thinking);
|
|
3735
3775
|
return true;
|
|
3736
3776
|
}
|
|
3737
3777
|
|
|
@@ -3814,6 +3854,26 @@ ${cssVarsBlock}
|
|
|
3814
3854
|
}
|
|
3815
3855
|
|
|
3816
3856
|
const hasResponse = Boolean(latestResponseMarkdown && latestResponseMarkdown.trim());
|
|
3857
|
+
const hasThinking = Boolean(latestResponseThinking && latestResponseThinking.trim());
|
|
3858
|
+
if (rightView === "thinking") {
|
|
3859
|
+
if (!hasResponse && !hasThinking) {
|
|
3860
|
+
referenceBadgeEl.textContent = "Thinking: none";
|
|
3861
|
+
return;
|
|
3862
|
+
}
|
|
3863
|
+
|
|
3864
|
+
const time = formatReferenceTime(latestResponseTimestamp);
|
|
3865
|
+
const total = Array.isArray(responseHistory) ? responseHistory.length : 0;
|
|
3866
|
+
const selected = total > 0 && responseHistoryIndex >= 0 && responseHistoryIndex < total
|
|
3867
|
+
? responseHistoryIndex + 1
|
|
3868
|
+
: 0;
|
|
3869
|
+
const historyPrefix = total > 0 ? "Response history " + selected + "/" + total + " · " : "";
|
|
3870
|
+
const thinkingLabel = hasThinking ? "assistant thinking" : "assistant thinking unavailable";
|
|
3871
|
+
referenceBadgeEl.textContent = time
|
|
3872
|
+
? historyPrefix + thinkingLabel + " · " + time
|
|
3873
|
+
: historyPrefix + thinkingLabel;
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
|
|
3817
3877
|
if (!hasResponse) {
|
|
3818
3878
|
referenceBadgeEl.textContent = "Latest response: none";
|
|
3819
3879
|
return;
|
|
@@ -3864,8 +3924,13 @@ ${cssVarsBlock}
|
|
|
3864
3924
|
function updateSyncBadge(normalizedEditorText) {
|
|
3865
3925
|
if (!syncBadgeEl) return;
|
|
3866
3926
|
|
|
3867
|
-
|
|
3868
|
-
|
|
3927
|
+
const showingThinking = rightView === "thinking";
|
|
3928
|
+
const hasComparableContent = showingThinking
|
|
3929
|
+
? Boolean(latestResponseThinking && latestResponseThinking.trim())
|
|
3930
|
+
: latestResponseHasContent;
|
|
3931
|
+
|
|
3932
|
+
if (!hasComparableContent) {
|
|
3933
|
+
syncBadgeEl.textContent = showingThinking ? "No thinking loaded" : "No response loaded";
|
|
3869
3934
|
syncBadgeEl.classList.remove("sync", "edited");
|
|
3870
3935
|
return;
|
|
3871
3936
|
}
|
|
@@ -3873,13 +3938,14 @@ ${cssVarsBlock}
|
|
|
3873
3938
|
const normalizedEditor = typeof normalizedEditorText === "string"
|
|
3874
3939
|
? normalizedEditorText
|
|
3875
3940
|
: normalizeForCompare(sourceTextEl.value);
|
|
3876
|
-
const
|
|
3941
|
+
const targetNormalized = showingThinking ? latestResponseThinkingNormalized : latestResponseNormalized;
|
|
3942
|
+
const inSync = normalizedEditor === targetNormalized;
|
|
3877
3943
|
if (inSync) {
|
|
3878
|
-
syncBadgeEl.textContent = "In sync with response";
|
|
3944
|
+
syncBadgeEl.textContent = showingThinking ? "In sync with thinking" : "In sync with response";
|
|
3879
3945
|
syncBadgeEl.classList.add("sync");
|
|
3880
3946
|
syncBadgeEl.classList.remove("edited");
|
|
3881
3947
|
} else {
|
|
3882
|
-
syncBadgeEl.textContent = "Out of sync with response";
|
|
3948
|
+
syncBadgeEl.textContent = showingThinking ? "Out of sync with thinking" : "Out of sync with response";
|
|
3883
3949
|
syncBadgeEl.classList.add("edited");
|
|
3884
3950
|
syncBadgeEl.classList.remove("sync");
|
|
3885
3951
|
}
|
|
@@ -4411,6 +4477,15 @@ ${cssVarsBlock}
|
|
|
4411
4477
|
return;
|
|
4412
4478
|
}
|
|
4413
4479
|
|
|
4480
|
+
if (rightView === "thinking") {
|
|
4481
|
+
const thinking = latestResponseThinking;
|
|
4482
|
+
finishPreviewRender(critiqueViewEl);
|
|
4483
|
+
critiqueViewEl.innerHTML = thinking && thinking.trim()
|
|
4484
|
+
? buildPlainMarkdownHtml(thinking)
|
|
4485
|
+
: "<pre class='plain-markdown'>No thinking available for this response.</pre>";
|
|
4486
|
+
return;
|
|
4487
|
+
}
|
|
4488
|
+
|
|
4414
4489
|
const markdown = latestResponseMarkdown;
|
|
4415
4490
|
if (!markdown || !markdown.trim()) {
|
|
4416
4491
|
finishPreviewRender(critiqueViewEl);
|
|
@@ -4446,36 +4521,56 @@ ${cssVarsBlock}
|
|
|
4446
4521
|
|
|
4447
4522
|
function updateResultActionButtons(normalizedEditorText) {
|
|
4448
4523
|
const hasResponse = latestResponseHasContent;
|
|
4524
|
+
const hasThinking = Boolean(latestResponseThinking && latestResponseThinking.trim());
|
|
4449
4525
|
const normalizedEditor = typeof normalizedEditorText === "string"
|
|
4450
4526
|
? normalizedEditorText
|
|
4451
4527
|
: normalizeForCompare(sourceTextEl.value);
|
|
4452
4528
|
const responseLoaded = hasResponse && normalizedEditor === latestResponseNormalized;
|
|
4529
|
+
const thinkingLoaded = hasThinking && normalizedEditor === latestResponseThinkingNormalized;
|
|
4453
4530
|
const isCritiqueResponse = hasResponse && latestResponseIsStructuredCritique;
|
|
4531
|
+
const showingThinking = rightView === "thinking";
|
|
4454
4532
|
|
|
4455
4533
|
const critiqueNotes = isCritiqueResponse ? latestCritiqueNotes : "";
|
|
4456
4534
|
const critiqueNotesLoaded = Boolean(critiqueNotes) && normalizedEditor === latestCritiqueNotesNormalized;
|
|
4457
4535
|
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4536
|
+
if (showingThinking) {
|
|
4537
|
+
loadResponseBtn.hidden = false;
|
|
4538
|
+
loadCritiqueNotesBtn.hidden = true;
|
|
4539
|
+
loadCritiqueFullBtn.hidden = true;
|
|
4540
|
+
|
|
4541
|
+
loadResponseBtn.disabled = uiBusy || !hasThinking || thinkingLoaded;
|
|
4542
|
+
loadResponseBtn.textContent = !hasThinking
|
|
4543
|
+
? "Thinking unavailable"
|
|
4544
|
+
: (thinkingLoaded ? "Thinking already in editor" : "Load thinking into editor");
|
|
4545
|
+
|
|
4546
|
+
copyResponseBtn.disabled = uiBusy || !hasThinking;
|
|
4547
|
+
copyResponseBtn.textContent = "Copy thinking text";
|
|
4548
|
+
} else {
|
|
4549
|
+
loadResponseBtn.hidden = isCritiqueResponse;
|
|
4550
|
+
loadCritiqueNotesBtn.hidden = !isCritiqueResponse;
|
|
4551
|
+
loadCritiqueFullBtn.hidden = !isCritiqueResponse;
|
|
4461
4552
|
|
|
4462
|
-
|
|
4463
|
-
|
|
4553
|
+
loadResponseBtn.disabled = uiBusy || !hasResponse || responseLoaded || isCritiqueResponse;
|
|
4554
|
+
loadResponseBtn.textContent = responseLoaded ? "Response already in editor" : "Load response into editor";
|
|
4464
4555
|
|
|
4465
|
-
|
|
4466
|
-
|
|
4556
|
+
loadCritiqueNotesBtn.disabled = uiBusy || !isCritiqueResponse || !critiqueNotes || critiqueNotesLoaded;
|
|
4557
|
+
loadCritiqueNotesBtn.textContent = critiqueNotesLoaded ? "Critique notes already in editor" : "Load critique notes into editor";
|
|
4467
4558
|
|
|
4468
|
-
|
|
4469
|
-
|
|
4559
|
+
loadCritiqueFullBtn.disabled = uiBusy || !isCritiqueResponse || responseLoaded;
|
|
4560
|
+
loadCritiqueFullBtn.textContent = responseLoaded ? "Full critique already in editor" : "Load full critique into editor";
|
|
4470
4561
|
|
|
4471
|
-
|
|
4562
|
+
copyResponseBtn.disabled = uiBusy || !hasResponse;
|
|
4563
|
+
copyResponseBtn.textContent = "Copy response text";
|
|
4564
|
+
}
|
|
4472
4565
|
|
|
4473
4566
|
const rightPaneShowsPreview = rightView === "preview" || rightView === "editor-preview";
|
|
4474
4567
|
const exportText = rightView === "editor-preview" ? prepareEditorTextForPreview(sourceTextEl.value) : latestResponseMarkdown;
|
|
4475
4568
|
const canExportPdf = rightPaneShowsPreview && Boolean(String(exportText || "").trim());
|
|
4476
4569
|
if (exportPdfBtn) {
|
|
4477
4570
|
exportPdfBtn.disabled = uiBusy || pdfExportInProgress || !canExportPdf;
|
|
4478
|
-
if (rightView === "
|
|
4571
|
+
if (rightView === "thinking") {
|
|
4572
|
+
exportPdfBtn.title = "Thinking view does not support PDF export yet.";
|
|
4573
|
+
} else if (rightView === "markdown") {
|
|
4479
4574
|
exportPdfBtn.title = "Switch right pane to Response (Preview) or Editor (Preview) to export PDF.";
|
|
4480
4575
|
} else if (!canExportPdf) {
|
|
4481
4576
|
exportPdfBtn.title = "Nothing to export yet.";
|
|
@@ -4653,7 +4748,11 @@ ${cssVarsBlock}
|
|
|
4653
4748
|
}
|
|
4654
4749
|
|
|
4655
4750
|
function setRightView(nextView) {
|
|
4656
|
-
rightView = nextView === "preview"
|
|
4751
|
+
rightView = nextView === "preview"
|
|
4752
|
+
? "preview"
|
|
4753
|
+
: (nextView === "editor-preview"
|
|
4754
|
+
? "editor-preview"
|
|
4755
|
+
: (nextView === "thinking" ? "thinking" : "markdown"));
|
|
4657
4756
|
rightViewSelect.value = rightView;
|
|
4658
4757
|
|
|
4659
4758
|
if (rightView !== "editor-preview" && responseEditorPreviewTimer) {
|
|
@@ -4980,7 +5079,12 @@ ${cssVarsBlock}
|
|
|
4980
5079
|
}
|
|
4981
5080
|
|
|
4982
5081
|
if (inFence) {
|
|
4983
|
-
out.push(line.length > 0 ? highlightCodeLine(line, fenceLanguage) :
|
|
5082
|
+
out.push(line.length > 0 ? highlightCodeLine(line, fenceLanguage) : EMPTY_OVERLAY_LINE);
|
|
5083
|
+
continue;
|
|
5084
|
+
}
|
|
5085
|
+
|
|
5086
|
+
if (line.length === 0) {
|
|
5087
|
+
out.push(EMPTY_OVERLAY_LINE);
|
|
4984
5088
|
continue;
|
|
4985
5089
|
}
|
|
4986
5090
|
|
|
@@ -5019,7 +5123,7 @@ ${cssVarsBlock}
|
|
|
5019
5123
|
const out = [];
|
|
5020
5124
|
for (const line of lines) {
|
|
5021
5125
|
if (line.length === 0) {
|
|
5022
|
-
out.push(
|
|
5126
|
+
out.push(EMPTY_OVERLAY_LINE);
|
|
5023
5127
|
} else if (lang) {
|
|
5024
5128
|
out.push(highlightCodeLine(line, lang));
|
|
5025
5129
|
} else {
|
|
@@ -5330,18 +5434,20 @@ ${cssVarsBlock}
|
|
|
5330
5434
|
return lower.indexOf("## critiques") !== -1 && lower.indexOf("## document") !== -1;
|
|
5331
5435
|
}
|
|
5332
5436
|
|
|
5333
|
-
function handleIncomingResponse(markdown, kind, timestamp) {
|
|
5437
|
+
function handleIncomingResponse(markdown, kind, timestamp, thinking) {
|
|
5334
5438
|
const responseTimestamp =
|
|
5335
5439
|
typeof timestamp === "number" && Number.isFinite(timestamp) && timestamp > 0
|
|
5336
5440
|
? timestamp
|
|
5337
5441
|
: Date.now();
|
|
5338
5442
|
|
|
5339
5443
|
latestResponseMarkdown = markdown;
|
|
5444
|
+
latestResponseThinking = typeof thinking === "string" ? thinking : "";
|
|
5340
5445
|
latestResponseKind = kind === "critique" ? "critique" : "annotation";
|
|
5341
5446
|
latestResponseTimestamp = responseTimestamp;
|
|
5342
5447
|
latestResponseIsStructuredCritique = isStructuredCritique(markdown);
|
|
5343
5448
|
latestResponseHasContent = Boolean(markdown && markdown.trim());
|
|
5344
5449
|
latestResponseNormalized = normalizeForCompare(markdown);
|
|
5450
|
+
latestResponseThinkingNormalized = normalizeForCompare(latestResponseThinking);
|
|
5345
5451
|
|
|
5346
5452
|
if (latestResponseIsStructuredCritique) {
|
|
5347
5453
|
latestCritiqueNotes = buildCritiqueNotesMarkdown(markdown);
|
|
@@ -5357,7 +5463,7 @@ ${cssVarsBlock}
|
|
|
5357
5463
|
function applyLatestPayload(payload) {
|
|
5358
5464
|
if (!payload || typeof payload.markdown !== "string") return false;
|
|
5359
5465
|
const responseKind = payload.kind === "critique" ? "critique" : "annotation";
|
|
5360
|
-
handleIncomingResponse(payload.markdown, responseKind, payload.timestamp);
|
|
5466
|
+
handleIncomingResponse(payload.markdown, responseKind, payload.timestamp, payload.thinking);
|
|
5361
5467
|
return true;
|
|
5362
5468
|
}
|
|
5363
5469
|
|
|
@@ -5456,7 +5562,7 @@ ${cssVarsBlock}
|
|
|
5456
5562
|
message.lastResponse.kind === "critique"
|
|
5457
5563
|
? "critique"
|
|
5458
5564
|
: (isStructuredCritique(lastMarkdown) ? "critique" : "annotation");
|
|
5459
|
-
handleIncomingResponse(lastMarkdown, lastResponseKind, message.lastResponse.timestamp);
|
|
5565
|
+
handleIncomingResponse(lastMarkdown, lastResponseKind, message.lastResponse.timestamp, message.lastResponse.thinking);
|
|
5460
5566
|
}
|
|
5461
5567
|
|
|
5462
5568
|
if (pendingRequestId) {
|
|
@@ -5553,7 +5659,7 @@ ${cssVarsBlock}
|
|
|
5553
5659
|
}
|
|
5554
5660
|
|
|
5555
5661
|
if (!appliedFromHistory && typeof message.markdown === "string") {
|
|
5556
|
-
handleIncomingResponse(message.markdown, responseKind, message.timestamp);
|
|
5662
|
+
handleIncomingResponse(message.markdown, responseKind, message.timestamp, message.thinking);
|
|
5557
5663
|
}
|
|
5558
5664
|
|
|
5559
5665
|
if (responseKind === "critique") {
|
|
@@ -5582,6 +5688,7 @@ ${cssVarsBlock}
|
|
|
5582
5688
|
const payload = {
|
|
5583
5689
|
kind: message.kind === "critique" ? "critique" : "annotation",
|
|
5584
5690
|
markdown: message.markdown,
|
|
5691
|
+
thinking: typeof message.thinking === "string" ? message.thinking : null,
|
|
5585
5692
|
timestamp: message.timestamp,
|
|
5586
5693
|
};
|
|
5587
5694
|
|
|
@@ -6172,6 +6279,17 @@ ${cssVarsBlock}
|
|
|
6172
6279
|
});
|
|
6173
6280
|
|
|
6174
6281
|
loadResponseBtn.addEventListener("click", () => {
|
|
6282
|
+
if (rightView === "thinking") {
|
|
6283
|
+
if (!latestResponseThinking.trim()) {
|
|
6284
|
+
setStatus("No thinking available for the selected response.", "warning");
|
|
6285
|
+
return;
|
|
6286
|
+
}
|
|
6287
|
+
setEditorText(latestResponseThinking, { preserveScroll: false, preserveSelection: false });
|
|
6288
|
+
setSourceState({ source: "blank", label: "assistant thinking", path: null });
|
|
6289
|
+
setStatus("Loaded thinking into editor.", "success");
|
|
6290
|
+
return;
|
|
6291
|
+
}
|
|
6292
|
+
|
|
6175
6293
|
if (!latestResponseMarkdown.trim()) {
|
|
6176
6294
|
setStatus("No response available yet.", "warning");
|
|
6177
6295
|
return;
|
|
@@ -6210,14 +6328,15 @@ ${cssVarsBlock}
|
|
|
6210
6328
|
});
|
|
6211
6329
|
|
|
6212
6330
|
copyResponseBtn.addEventListener("click", async () => {
|
|
6213
|
-
|
|
6214
|
-
|
|
6331
|
+
const content = rightView === "thinking" ? latestResponseThinking : latestResponseMarkdown;
|
|
6332
|
+
if (!content.trim()) {
|
|
6333
|
+
setStatus(rightView === "thinking" ? "No thinking available yet." : "No response available yet.", "warning");
|
|
6215
6334
|
return;
|
|
6216
6335
|
}
|
|
6217
6336
|
|
|
6218
6337
|
try {
|
|
6219
|
-
await navigator.clipboard.writeText(
|
|
6220
|
-
setStatus("Copied response text.", "success");
|
|
6338
|
+
await navigator.clipboard.writeText(content);
|
|
6339
|
+
setStatus(rightView === "thinking" ? "Copied thinking text." : "Copied response text.", "success");
|
|
6221
6340
|
} catch (error) {
|
|
6222
6341
|
setStatus("Clipboard write failed.", "warning");
|
|
6223
6342
|
}
|
|
@@ -6635,6 +6754,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6635
6754
|
}
|
|
6636
6755
|
lastStudioResponse = {
|
|
6637
6756
|
markdown: latest.markdown,
|
|
6757
|
+
thinking: latest.thinking,
|
|
6638
6758
|
timestamp: latest.timestamp,
|
|
6639
6759
|
kind: latest.kind,
|
|
6640
6760
|
};
|
|
@@ -6897,6 +7017,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
6897
7017
|
type: "latest_response",
|
|
6898
7018
|
kind: lastStudioResponse.kind,
|
|
6899
7019
|
markdown: lastStudioResponse.markdown,
|
|
7020
|
+
thinking: lastStudioResponse.thinking,
|
|
6900
7021
|
timestamp: lastStudioResponse.timestamp,
|
|
6901
7022
|
responseHistory: studioResponseHistory,
|
|
6902
7023
|
});
|
|
@@ -7711,11 +7832,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
7711
7832
|
const stopReason = typeof message.stopReason === "string" ? message.stopReason : "";
|
|
7712
7833
|
const role = typeof message.role === "string" ? message.role : "";
|
|
7713
7834
|
const markdown = extractAssistantText(event.message);
|
|
7835
|
+
const thinking = extractAssistantThinking(event.message);
|
|
7714
7836
|
emitDebugEvent("message_end", {
|
|
7715
7837
|
role,
|
|
7716
7838
|
stopReason,
|
|
7717
7839
|
hasMarkdown: Boolean(markdown),
|
|
7718
7840
|
markdownLength: markdown ? markdown.length : 0,
|
|
7841
|
+
hasThinking: Boolean(thinking),
|
|
7842
|
+
thinkingLength: thinking ? thinking.length : 0,
|
|
7719
7843
|
activeRequestId: activeRequest?.id ?? null,
|
|
7720
7844
|
activeRequestKind: activeRequest?.kind ?? null,
|
|
7721
7845
|
});
|
|
@@ -7742,6 +7866,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
7742
7866
|
const fallbackHistoryItem: StudioResponseHistoryItem = {
|
|
7743
7867
|
id: randomUUID(),
|
|
7744
7868
|
markdown,
|
|
7869
|
+
thinking,
|
|
7745
7870
|
timestamp: Date.now(),
|
|
7746
7871
|
kind: inferStudioResponseKind(markdown),
|
|
7747
7872
|
prompt: fallbackPrompt,
|
|
@@ -7752,12 +7877,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
7752
7877
|
|
|
7753
7878
|
const latestItem = studioResponseHistory[studioResponseHistory.length - 1];
|
|
7754
7879
|
const responseTimestamp = latestItem?.timestamp ?? Date.now();
|
|
7880
|
+
const responseThinking = latestItem?.thinking ?? thinking ?? null;
|
|
7755
7881
|
|
|
7756
7882
|
if (activeRequest) {
|
|
7757
7883
|
const requestId = activeRequest.id;
|
|
7758
7884
|
const kind = activeRequest.kind;
|
|
7759
7885
|
lastStudioResponse = {
|
|
7760
7886
|
markdown,
|
|
7887
|
+
thinking: responseThinking,
|
|
7761
7888
|
timestamp: responseTimestamp,
|
|
7762
7889
|
kind,
|
|
7763
7890
|
};
|
|
@@ -7765,6 +7892,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
7765
7892
|
requestId,
|
|
7766
7893
|
kind,
|
|
7767
7894
|
markdownLength: markdown.length,
|
|
7895
|
+
thinkingLength: responseThinking ? responseThinking.length : 0,
|
|
7768
7896
|
stopReason,
|
|
7769
7897
|
});
|
|
7770
7898
|
broadcast({
|
|
@@ -7772,6 +7900,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
7772
7900
|
requestId,
|
|
7773
7901
|
kind,
|
|
7774
7902
|
markdown,
|
|
7903
|
+
thinking: lastStudioResponse.thinking,
|
|
7775
7904
|
timestamp: lastStudioResponse.timestamp,
|
|
7776
7905
|
responseHistory: studioResponseHistory,
|
|
7777
7906
|
});
|
|
@@ -7783,18 +7912,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
7783
7912
|
const inferredKind = inferStudioResponseKind(markdown);
|
|
7784
7913
|
lastStudioResponse = {
|
|
7785
7914
|
markdown,
|
|
7915
|
+
thinking: responseThinking,
|
|
7786
7916
|
timestamp: responseTimestamp,
|
|
7787
7917
|
kind: inferredKind,
|
|
7788
7918
|
};
|
|
7789
7919
|
emitDebugEvent("broadcast_latest_response", {
|
|
7790
7920
|
kind: inferredKind,
|
|
7791
7921
|
markdownLength: markdown.length,
|
|
7922
|
+
thinkingLength: responseThinking ? responseThinking.length : 0,
|
|
7792
7923
|
stopReason,
|
|
7793
7924
|
});
|
|
7794
7925
|
broadcast({
|
|
7795
7926
|
type: "latest_response",
|
|
7796
7927
|
kind: inferredKind,
|
|
7797
7928
|
markdown,
|
|
7929
|
+
thinking: lastStudioResponse.thinking,
|
|
7798
7930
|
timestamp: lastStudioResponse.timestamp,
|
|
7799
7931
|
responseHistory: studioResponseHistory,
|
|
7800
7932
|
});
|