pi-studio 0.4.0 → 0.4.1
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 +7 -0
- package/index.ts +66 -23
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `pi-studio` are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.4.1] — 2026-03-03
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- Editor input keeps preview refreshes immediate (no added typing debounce) while keeping editor syntax highlighting immediate in Raw view.
|
|
9
|
+
- Response/sync state checks now reuse cached normalized response data and critique-note extracts instead of recomputing on each keystroke.
|
|
10
|
+
- Editor action/sync UI updates are now coalesced with `requestAnimationFrame` during typing.
|
|
11
|
+
|
|
5
12
|
## [0.3.0] — 2026-03-02
|
|
6
13
|
|
|
7
14
|
### Added
|
package/index.ts
CHANGED
|
@@ -2261,6 +2261,10 @@ ${cssVarsBlock}
|
|
|
2261
2261
|
let latestResponseTimestamp = 0;
|
|
2262
2262
|
let latestResponseKind = "annotation";
|
|
2263
2263
|
let latestResponseIsStructuredCritique = false;
|
|
2264
|
+
let latestResponseHasContent = false;
|
|
2265
|
+
let latestResponseNormalized = "";
|
|
2266
|
+
let latestCritiqueNotes = "";
|
|
2267
|
+
let latestCritiqueNotesNormalized = "";
|
|
2264
2268
|
let uiBusy = false;
|
|
2265
2269
|
let sourceState = {
|
|
2266
2270
|
source: initialSourceState.source,
|
|
@@ -2312,10 +2316,12 @@ ${cssVarsBlock}
|
|
|
2312
2316
|
var SUPPORTED_LANGUAGES = Object.keys(LANG_EXT_MAP);
|
|
2313
2317
|
const RESPONSE_HIGHLIGHT_MAX_CHARS = 120_000;
|
|
2314
2318
|
const RESPONSE_HIGHLIGHT_STORAGE_KEY = "piStudio.responseHighlightEnabled";
|
|
2319
|
+
const PREVIEW_INPUT_DEBOUNCE_MS = 0;
|
|
2315
2320
|
let sourcePreviewRenderTimer = null;
|
|
2316
2321
|
let sourcePreviewRenderNonce = 0;
|
|
2317
2322
|
let responsePreviewRenderNonce = 0;
|
|
2318
2323
|
let responseEditorPreviewTimer = null;
|
|
2324
|
+
let editorMetaUpdateRaf = null;
|
|
2319
2325
|
let editorHighlightEnabled = false;
|
|
2320
2326
|
let editorLanguage = "markdown";
|
|
2321
2327
|
let responseHighlightEnabled = false;
|
|
@@ -2514,23 +2520,19 @@ ${cssVarsBlock}
|
|
|
2514
2520
|
return normalizeForCompare(a) === normalizeForCompare(b);
|
|
2515
2521
|
}
|
|
2516
2522
|
|
|
2517
|
-
function
|
|
2518
|
-
return latestResponseMarkdown;
|
|
2519
|
-
}
|
|
2520
|
-
|
|
2521
|
-
function updateSyncBadge() {
|
|
2523
|
+
function updateSyncBadge(normalizedEditorText) {
|
|
2522
2524
|
if (!syncBadgeEl) return;
|
|
2523
2525
|
|
|
2524
|
-
|
|
2525
|
-
const hasResponse = Boolean(response && response.trim());
|
|
2526
|
-
|
|
2527
|
-
if (!hasResponse) {
|
|
2526
|
+
if (!latestResponseHasContent) {
|
|
2528
2527
|
syncBadgeEl.textContent = "No response loaded";
|
|
2529
2528
|
syncBadgeEl.classList.remove("sync", "edited");
|
|
2530
2529
|
return;
|
|
2531
2530
|
}
|
|
2532
2531
|
|
|
2533
|
-
const
|
|
2532
|
+
const normalizedEditor = typeof normalizedEditorText === "string"
|
|
2533
|
+
? normalizedEditorText
|
|
2534
|
+
: normalizeForCompare(sourceTextEl.value);
|
|
2535
|
+
const inSync = normalizedEditor === latestResponseNormalized;
|
|
2534
2536
|
if (inSync) {
|
|
2535
2537
|
syncBadgeEl.textContent = "In sync with response";
|
|
2536
2538
|
syncBadgeEl.classList.add("sync");
|
|
@@ -2787,15 +2789,20 @@ ${cssVarsBlock}
|
|
|
2787
2789
|
}, delay);
|
|
2788
2790
|
}
|
|
2789
2791
|
|
|
2790
|
-
function renderSourcePreview() {
|
|
2792
|
+
function renderSourcePreview(options) {
|
|
2793
|
+
const previewDelayMs =
|
|
2794
|
+
options && typeof options.previewDelayMs === "number"
|
|
2795
|
+
? Math.max(0, options.previewDelayMs)
|
|
2796
|
+
: 0;
|
|
2797
|
+
|
|
2791
2798
|
if (editorView === "preview") {
|
|
2792
|
-
scheduleSourcePreviewRender(
|
|
2799
|
+
scheduleSourcePreviewRender(previewDelayMs);
|
|
2793
2800
|
}
|
|
2794
2801
|
if (editorHighlightEnabled && editorView === "markdown") {
|
|
2795
2802
|
scheduleEditorHighlightRender();
|
|
2796
2803
|
}
|
|
2797
2804
|
if (rightView === "editor-preview") {
|
|
2798
|
-
scheduleResponseEditorPreviewRender(
|
|
2805
|
+
scheduleResponseEditorPreviewRender(previewDelayMs);
|
|
2799
2806
|
}
|
|
2800
2807
|
}
|
|
2801
2808
|
|
|
@@ -2860,14 +2867,16 @@ ${cssVarsBlock}
|
|
|
2860
2867
|
critiqueViewEl.innerHTML = buildPlainMarkdownHtml(markdown);
|
|
2861
2868
|
}
|
|
2862
2869
|
|
|
2863
|
-
function updateResultActionButtons() {
|
|
2864
|
-
const
|
|
2865
|
-
const
|
|
2866
|
-
|
|
2870
|
+
function updateResultActionButtons(normalizedEditorText) {
|
|
2871
|
+
const hasResponse = latestResponseHasContent;
|
|
2872
|
+
const normalizedEditor = typeof normalizedEditorText === "string"
|
|
2873
|
+
? normalizedEditorText
|
|
2874
|
+
: normalizeForCompare(sourceTextEl.value);
|
|
2875
|
+
const responseLoaded = hasResponse && normalizedEditor === latestResponseNormalized;
|
|
2867
2876
|
const isCritiqueResponse = hasResponse && latestResponseIsStructuredCritique;
|
|
2868
2877
|
|
|
2869
|
-
const critiqueNotes = isCritiqueResponse ?
|
|
2870
|
-
const critiqueNotesLoaded = Boolean(critiqueNotes) &&
|
|
2878
|
+
const critiqueNotes = isCritiqueResponse ? latestCritiqueNotes : "";
|
|
2879
|
+
const critiqueNotesLoaded = Boolean(critiqueNotes) && normalizedEditor === latestCritiqueNotesNormalized;
|
|
2871
2880
|
|
|
2872
2881
|
loadResponseBtn.hidden = isCritiqueResponse;
|
|
2873
2882
|
loadCritiqueNotesBtn.hidden = !isCritiqueResponse;
|
|
@@ -2887,7 +2896,7 @@ ${cssVarsBlock}
|
|
|
2887
2896
|
pullLatestBtn.disabled = uiBusy || followLatest;
|
|
2888
2897
|
pullLatestBtn.textContent = queuedLatestResponse ? "Get latest response *" : "Get latest response";
|
|
2889
2898
|
|
|
2890
|
-
updateSyncBadge();
|
|
2899
|
+
updateSyncBadge(normalizedEditor);
|
|
2891
2900
|
}
|
|
2892
2901
|
|
|
2893
2902
|
function refreshResponseUi() {
|
|
@@ -3411,6 +3420,31 @@ ${cssVarsBlock}
|
|
|
3411
3420
|
sourceHighlightEl.scrollLeft = sourceTextEl.scrollLeft;
|
|
3412
3421
|
}
|
|
3413
3422
|
|
|
3423
|
+
function runEditorMetaUpdateNow() {
|
|
3424
|
+
const normalizedEditor = normalizeForCompare(sourceTextEl.value);
|
|
3425
|
+
updateResultActionButtons(normalizedEditor);
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
function scheduleEditorMetaUpdate() {
|
|
3429
|
+
if (editorMetaUpdateRaf !== null) {
|
|
3430
|
+
if (typeof window.cancelAnimationFrame === "function") {
|
|
3431
|
+
window.cancelAnimationFrame(editorMetaUpdateRaf);
|
|
3432
|
+
} else {
|
|
3433
|
+
window.clearTimeout(editorMetaUpdateRaf);
|
|
3434
|
+
}
|
|
3435
|
+
editorMetaUpdateRaf = null;
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
const schedule = typeof window.requestAnimationFrame === "function"
|
|
3439
|
+
? window.requestAnimationFrame.bind(window)
|
|
3440
|
+
: (cb) => window.setTimeout(cb, 16);
|
|
3441
|
+
|
|
3442
|
+
editorMetaUpdateRaf = schedule(() => {
|
|
3443
|
+
editorMetaUpdateRaf = null;
|
|
3444
|
+
runEditorMetaUpdateNow();
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3414
3448
|
function readStoredToggle(storageKey) {
|
|
3415
3449
|
if (!window.localStorage) return null;
|
|
3416
3450
|
try {
|
|
@@ -3597,9 +3631,18 @@ ${cssVarsBlock}
|
|
|
3597
3631
|
latestResponseKind = kind === "critique" ? "critique" : "annotation";
|
|
3598
3632
|
latestResponseTimestamp = responseTimestamp;
|
|
3599
3633
|
latestResponseIsStructuredCritique = isStructuredCritique(markdown);
|
|
3634
|
+
latestResponseHasContent = Boolean(markdown && markdown.trim());
|
|
3635
|
+
latestResponseNormalized = normalizeForCompare(markdown);
|
|
3636
|
+
|
|
3637
|
+
if (latestResponseIsStructuredCritique) {
|
|
3638
|
+
latestCritiqueNotes = buildCritiqueNotesMarkdown(markdown);
|
|
3639
|
+
latestCritiqueNotesNormalized = normalizeForCompare(latestCritiqueNotes);
|
|
3640
|
+
} else {
|
|
3641
|
+
latestCritiqueNotes = "";
|
|
3642
|
+
latestCritiqueNotesNormalized = "";
|
|
3643
|
+
}
|
|
3600
3644
|
|
|
3601
3645
|
refreshResponseUi();
|
|
3602
|
-
syncActionButtons();
|
|
3603
3646
|
}
|
|
3604
3647
|
|
|
3605
3648
|
function applyLatestPayload(payload) {
|
|
@@ -4016,8 +4059,8 @@ ${cssVarsBlock}
|
|
|
4016
4059
|
});
|
|
4017
4060
|
|
|
4018
4061
|
sourceTextEl.addEventListener("input", () => {
|
|
4019
|
-
renderSourcePreview();
|
|
4020
|
-
|
|
4062
|
+
renderSourcePreview({ previewDelayMs: PREVIEW_INPUT_DEBOUNCE_MS });
|
|
4063
|
+
scheduleEditorMetaUpdate();
|
|
4021
4064
|
});
|
|
4022
4065
|
|
|
4023
4066
|
sourceTextEl.addEventListener("scroll", () => {
|