pi-studio 0.2.6 → 0.3.0
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 +14 -0
- package/README.md +3 -2
- package/WORKFLOW.md +1 -1
- package/index.ts +70 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `pi-studio` are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.3.0] — 2026-03-02
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Editor Preview in response pane**: new `Right: Editor (Preview)` view mode renders editor text in the right pane with debounced live updates — enables Overleaf-style side-by-side source/rendered editing without a model round-trip.
|
|
9
|
+
- Code-language aware: Editor Preview renders syntax-highlighted code when a non-markdown language is selected.
|
|
10
|
+
- Response badge shows "Previewing: editor text" in editor-preview mode, with "· response updated HH:MM:SS" when a model response arrives in the background.
|
|
11
|
+
- Right pane section header updates to "Editor Preview" when in editor-preview mode.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- View toggle labels now use `Left: Source (Mode)` / `Right: Source (Mode)` format for unambiguous pane identification (e.g., `Left: Editor (Raw)`, `Right: Response (Preview)`, `Right: Editor (Preview)`).
|
|
15
|
+
- Sync badge wording: `Edited since response` → `Out of sync with response` (direction-neutral, accurate regardless of which side changed).
|
|
16
|
+
- Critique load buttons now include destination: `Load critique notes into editor` / `Load full critique into editor` (consistent with `Load response into editor`).
|
|
17
|
+
- Critique loaded-state labels updated: `Critique (full) already in editor` → `Full critique already in editor`.
|
|
18
|
+
|
|
5
19
|
## [0.2.4] — 2026-03-02
|
|
6
20
|
|
|
7
21
|
### Changed
|
package/README.md
CHANGED
|
@@ -50,9 +50,10 @@ Status: experimental alpha.
|
|
|
50
50
|
- **Critique editor text** requests structured critique (auto/writing/code focus)
|
|
51
51
|
- Response load helpers:
|
|
52
52
|
- non-critique: **Load response into editor**
|
|
53
|
-
- critique: **Load critique
|
|
53
|
+
- critique: **Load critique notes into editor** / **Load full critique into editor**
|
|
54
54
|
- File actions: **Save As…**, **Save file**, **Load file content**
|
|
55
|
-
- View toggles: `
|
|
55
|
+
- View toggles: `Left: Editor (Raw|Preview)`, `Right: Response (Raw|Preview) | Editor (Preview)`
|
|
56
|
+
- **Editor Preview in response pane**: side-by-side source/rendered view (Overleaf-style) — select `Right: Editor (Preview)` to render editor text in the right pane with live debounced updates
|
|
56
57
|
- Preview mode supports MathML equations and Mermaid fenced diagrams
|
|
57
58
|
- **Language-aware syntax highlighting** with selectable language mode:
|
|
58
59
|
- Markdown (default): headings, links, code fences, lists, quotes, inline code
|
package/WORKFLOW.md
CHANGED
|
@@ -75,7 +75,7 @@ Rules:
|
|
|
75
75
|
## Required UI elements
|
|
76
76
|
|
|
77
77
|
- Header actions: **Save As…**, **Save file** (file-backed), **Load file in editor**
|
|
78
|
-
- Header view toggles: `
|
|
78
|
+
- Header view toggles: `Left: Editor (Raw|Preview)`, `Right: Response (Raw|Preview) | Editor (Preview)`
|
|
79
79
|
- Preview mode uses server-side `pandoc` rendering (math-aware) with plain-markdown fallback when renderer is unavailable.
|
|
80
80
|
- Editor actions: **Insert annotation header**, **Run editor text**, **Critique editor text** (+ critique focus), **Send to pi editor**, **Copy editor text**
|
|
81
81
|
- Response actions include `Auto-update response: On|Off` + **Get latest response**
|
package/index.ts
CHANGED
|
@@ -1908,12 +1908,13 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
1908
1908
|
<h1><span class="app-logo" aria-hidden="true">π</span> Pi Studio <span class="app-subtitle">Feedback Workspace</span></h1>
|
|
1909
1909
|
<div class="controls">
|
|
1910
1910
|
<select id="editorViewSelect" aria-label="Editor view mode">
|
|
1911
|
-
<option value="markdown" selected>
|
|
1912
|
-
<option value="preview">
|
|
1911
|
+
<option value="markdown" selected>Left: Editor (Raw)</option>
|
|
1912
|
+
<option value="preview">Left: Editor (Preview)</option>
|
|
1913
1913
|
</select>
|
|
1914
1914
|
<select id="rightViewSelect" aria-label="Response view mode">
|
|
1915
|
-
<option value="markdown">
|
|
1916
|
-
<option value="preview" selected>
|
|
1915
|
+
<option value="markdown">Right: Response (Raw)</option>
|
|
1916
|
+
<option value="preview" selected>Right: Response (Preview)</option>
|
|
1917
|
+
<option value="editor-preview">Right: Editor (Preview)</option>
|
|
1917
1918
|
</select>
|
|
1918
1919
|
<button id="saveAsBtn" type="button" title="Save editor text to a new file path.">Save As…</button>
|
|
1919
1920
|
<button id="saveOverBtn" type="button" title="Overwrite current file with editor text." disabled>Save file</button>
|
|
@@ -1988,8 +1989,8 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
1988
1989
|
</select>
|
|
1989
1990
|
<button id="pullLatestBtn" type="button" title="Fetch the latest assistant response when auto-update is off.">Get latest response</button>
|
|
1990
1991
|
<button id="loadResponseBtn" type="button">Load response into editor</button>
|
|
1991
|
-
<button id="loadCritiqueNotesBtn" type="button" hidden>Load critique
|
|
1992
|
-
<button id="loadCritiqueFullBtn" type="button" hidden>Load critique
|
|
1992
|
+
<button id="loadCritiqueNotesBtn" type="button" hidden>Load critique notes into editor</button>
|
|
1993
|
+
<button id="loadCritiqueFullBtn" type="button" hidden>Load full critique into editor</button>
|
|
1993
1994
|
<button id="copyResponseBtn" type="button">Copy response text</button>
|
|
1994
1995
|
</div>
|
|
1995
1996
|
</div>
|
|
@@ -2098,6 +2099,7 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2098
2099
|
let sourcePreviewRenderTimer = null;
|
|
2099
2100
|
let sourcePreviewRenderNonce = 0;
|
|
2100
2101
|
let responsePreviewRenderNonce = 0;
|
|
2102
|
+
let responseEditorPreviewTimer = null;
|
|
2101
2103
|
let editorHighlightEnabled = false;
|
|
2102
2104
|
let editorLanguage = "markdown";
|
|
2103
2105
|
let responseHighlightEnabled = false;
|
|
@@ -2242,6 +2244,18 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2242
2244
|
function updateReferenceBadge() {
|
|
2243
2245
|
if (!referenceBadgeEl) return;
|
|
2244
2246
|
|
|
2247
|
+
if (rightView === "editor-preview") {
|
|
2248
|
+
const hasResponse = Boolean(latestResponseMarkdown && latestResponseMarkdown.trim());
|
|
2249
|
+
if (hasResponse) {
|
|
2250
|
+
const time = formatReferenceTime(latestResponseTimestamp);
|
|
2251
|
+
const suffix = time ? " · response updated " + time : " · response available";
|
|
2252
|
+
referenceBadgeEl.textContent = "Previewing: editor text" + suffix;
|
|
2253
|
+
} else {
|
|
2254
|
+
referenceBadgeEl.textContent = "Previewing: editor text";
|
|
2255
|
+
}
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2245
2259
|
const hasResponse = Boolean(latestResponseMarkdown && latestResponseMarkdown.trim());
|
|
2246
2260
|
if (!hasResponse) {
|
|
2247
2261
|
referenceBadgeEl.textContent = "Latest response: none";
|
|
@@ -2285,7 +2299,7 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2285
2299
|
syncBadgeEl.classList.add("sync");
|
|
2286
2300
|
syncBadgeEl.classList.remove("edited");
|
|
2287
2301
|
} else {
|
|
2288
|
-
syncBadgeEl.textContent = "
|
|
2302
|
+
syncBadgeEl.textContent = "Out of sync with response";
|
|
2289
2303
|
syncBadgeEl.classList.add("edited");
|
|
2290
2304
|
syncBadgeEl.classList.remove("sync");
|
|
2291
2305
|
}
|
|
@@ -2470,7 +2484,7 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2470
2484
|
if (pane === "source") {
|
|
2471
2485
|
if (nonce !== sourcePreviewRenderNonce || editorView !== "preview") return;
|
|
2472
2486
|
} else {
|
|
2473
|
-
if (nonce !== responsePreviewRenderNonce || rightView !== "preview") return;
|
|
2487
|
+
if (nonce !== responsePreviewRenderNonce || (rightView !== "preview" && rightView !== "editor-preview")) return;
|
|
2474
2488
|
}
|
|
2475
2489
|
|
|
2476
2490
|
targetEl.innerHTML = sanitizeRenderedHtml(renderedHtml, markdown);
|
|
@@ -2479,7 +2493,7 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2479
2493
|
if (pane === "source") {
|
|
2480
2494
|
if (nonce !== sourcePreviewRenderNonce || editorView !== "preview") return;
|
|
2481
2495
|
} else {
|
|
2482
|
-
if (nonce !== responsePreviewRenderNonce || rightView !== "preview") return;
|
|
2496
|
+
if (nonce !== responsePreviewRenderNonce || (rightView !== "preview" && rightView !== "editor-preview")) return;
|
|
2483
2497
|
}
|
|
2484
2498
|
|
|
2485
2499
|
const detail = error && error.message ? error.message : String(error || "unknown error");
|
|
@@ -2521,9 +2535,43 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2521
2535
|
if (editorHighlightEnabled && editorView === "markdown") {
|
|
2522
2536
|
scheduleEditorHighlightRender();
|
|
2523
2537
|
}
|
|
2538
|
+
if (rightView === "editor-preview") {
|
|
2539
|
+
scheduleResponseEditorPreviewRender(0);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2543
|
+
function scheduleResponseEditorPreviewRender(delayMs) {
|
|
2544
|
+
if (responseEditorPreviewTimer) {
|
|
2545
|
+
window.clearTimeout(responseEditorPreviewTimer);
|
|
2546
|
+
responseEditorPreviewTimer = null;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
if (rightView !== "editor-preview") return;
|
|
2550
|
+
|
|
2551
|
+
const delay = typeof delayMs === "number" ? Math.max(0, delayMs) : 180;
|
|
2552
|
+
responseEditorPreviewTimer = window.setTimeout(() => {
|
|
2553
|
+
responseEditorPreviewTimer = null;
|
|
2554
|
+
renderActiveResult();
|
|
2555
|
+
}, delay);
|
|
2524
2556
|
}
|
|
2525
2557
|
|
|
2526
2558
|
function renderActiveResult() {
|
|
2559
|
+
if (rightView === "editor-preview") {
|
|
2560
|
+
const editorText = sourceTextEl.value || "";
|
|
2561
|
+
if (!editorText.trim()) {
|
|
2562
|
+
critiqueViewEl.innerHTML = "<pre class='plain-markdown'>Editor is empty.</pre>";
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
if (editorLanguage && editorLanguage !== "markdown") {
|
|
2566
|
+
critiqueViewEl.innerHTML = "<div class='response-markdown-highlight' style='white-space:pre;font-family:var(--font-mono);font-size:13px;line-height:1.5;padding:16px;overflow:auto;'>" + highlightCode(editorText, editorLanguage) + "</div>";
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
const nonce = ++responsePreviewRenderNonce;
|
|
2570
|
+
critiqueViewEl.innerHTML = "<div class='preview-loading'>Rendering preview…</div>";
|
|
2571
|
+
void applyRenderedMarkdown(critiqueViewEl, editorText, "response", nonce);
|
|
2572
|
+
return;
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2527
2575
|
const markdown = latestResponseMarkdown;
|
|
2528
2576
|
if (!markdown || !markdown.trim()) {
|
|
2529
2577
|
critiqueViewEl.innerHTML = "<pre class='plain-markdown'>No response yet. Run editor text or critique editor text.</pre>";
|
|
@@ -2570,10 +2618,10 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2570
2618
|
loadResponseBtn.textContent = responseLoaded ? "Response already in editor" : "Load response into editor";
|
|
2571
2619
|
|
|
2572
2620
|
loadCritiqueNotesBtn.disabled = uiBusy || !isCritiqueResponse || !critiqueNotes || critiqueNotesLoaded;
|
|
2573
|
-
loadCritiqueNotesBtn.textContent = critiqueNotesLoaded ? "Critique notes already in editor" : "Load critique
|
|
2621
|
+
loadCritiqueNotesBtn.textContent = critiqueNotesLoaded ? "Critique notes already in editor" : "Load critique notes into editor";
|
|
2574
2622
|
|
|
2575
2623
|
loadCritiqueFullBtn.disabled = uiBusy || !isCritiqueResponse || responseLoaded;
|
|
2576
|
-
loadCritiqueFullBtn.textContent = responseLoaded ? "
|
|
2624
|
+
loadCritiqueFullBtn.textContent = responseLoaded ? "Full critique already in editor" : "Load full critique into editor";
|
|
2577
2625
|
|
|
2578
2626
|
copyResponseBtn.disabled = uiBusy || !hasResponse;
|
|
2579
2627
|
|
|
@@ -2589,7 +2637,7 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2589
2637
|
}
|
|
2590
2638
|
|
|
2591
2639
|
if (rightSectionHeaderEl) {
|
|
2592
|
-
rightSectionHeaderEl.textContent = "Response";
|
|
2640
|
+
rightSectionHeaderEl.textContent = rightView === "editor-preview" ? "Editor Preview" : "Response";
|
|
2593
2641
|
}
|
|
2594
2642
|
|
|
2595
2643
|
updateSourceBadge();
|
|
@@ -2672,9 +2720,15 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
2672
2720
|
}
|
|
2673
2721
|
|
|
2674
2722
|
function setRightView(nextView) {
|
|
2675
|
-
rightView = nextView === "preview" ? "preview" : "markdown";
|
|
2723
|
+
rightView = nextView === "preview" ? "preview" : (nextView === "editor-preview" ? "editor-preview" : "markdown");
|
|
2676
2724
|
rightViewSelect.value = rightView;
|
|
2677
|
-
|
|
2725
|
+
|
|
2726
|
+
if (rightView !== "editor-preview" && responseEditorPreviewTimer) {
|
|
2727
|
+
window.clearTimeout(responseEditorPreviewTimer);
|
|
2728
|
+
responseEditorPreviewTimer = null;
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
refreshResponseUi();
|
|
2678
2732
|
syncActionButtons();
|
|
2679
2733
|
}
|
|
2680
2734
|
|
|
@@ -3748,8 +3802,8 @@ function buildStudioHtml(initialDocument: InitialStudioDocument | null, theme?:
|
|
|
3748
3802
|
|
|
3749
3803
|
sourceTextEl.value = latestResponseMarkdown;
|
|
3750
3804
|
renderSourcePreview();
|
|
3751
|
-
setSourceState({ source: "blank", label: "critique
|
|
3752
|
-
setStatus("Loaded critique
|
|
3805
|
+
setSourceState({ source: "blank", label: "full critique", path: null });
|
|
3806
|
+
setStatus("Loaded full critique into editor.", "success");
|
|
3753
3807
|
});
|
|
3754
3808
|
|
|
3755
3809
|
copyResponseBtn.addEventListener("click", async () => {
|