pi-studio 0.5.0 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/WORKFLOW.md +10 -0
- package/assets/screenshots/dark-workspace.png +0 -0
- package/assets/screenshots/light-workspace.png +0 -0
- package/index.ts +151 -77
- 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.2] — 2026-03-06
|
|
92
|
+
|
|
93
|
+
### Changed
|
|
94
|
+
- Refined left-pane action grouping into clearer workflow rows (run/copy/send/load, annotation tools, critique/highlight controls).
|
|
95
|
+
- Refined right-pane action grouping with consistent rows below response output:
|
|
96
|
+
- mode toggles
|
|
97
|
+
- history navigation (`Get latest`, `Prev`, `History`, `Next`)
|
|
98
|
+
- response load/copy actions
|
|
99
|
+
- Moved **Export right preview as PDF** to the right-pane section header (next to response view selector).
|
|
100
|
+
- Annotation header scaffold now includes precedence guidance:
|
|
101
|
+
- `precedence: later messages supersede these annotations unless user explicitly references them`
|
|
102
|
+
- Inserted annotation scaffold now includes a closing boundary marker:
|
|
103
|
+
- `--- end annotations ---`
|
|
104
|
+
- Removing annotation header now strips the boundary marker as well.
|
|
105
|
+
- Updated default README dark/light workspace screenshots to the latest UI.
|
|
106
|
+
- Moved `sample.diff` example into `assets/` with other sample files.
|
|
107
|
+
- Added escaping guidance for embedded browser script/template changes to `WORKFLOW.md`.
|
|
108
|
+
|
|
109
|
+
### Fixed
|
|
110
|
+
- Prevented Studio boot breakage caused by unescaped newline insertion in embedded script string updates.
|
|
111
|
+
|
|
91
112
|
## [0.5.0] — 2026-03-05
|
|
92
113
|
|
|
93
114
|
### Added
|
package/WORKFLOW.md
CHANGED
|
@@ -87,6 +87,16 @@ Rules:
|
|
|
87
87
|
|
|
88
88
|
---
|
|
89
89
|
|
|
90
|
+
## Escaping pitfalls (implementation note)
|
|
91
|
+
|
|
92
|
+
`index.ts` builds browser HTML as a TypeScript template string and embeds inline browser JavaScript. This creates multiple parse layers (TS string → HTML → JS), so incorrect escaping can break Studio boot (e.g. stuck at `Booting studio…`).
|
|
93
|
+
|
|
94
|
+
Rules of thumb:
|
|
95
|
+
- In embedded JS string literals authored from TS template context, use `\\n` (not `\n`) for runtime newlines.
|
|
96
|
+
- Escape regex backslashes for the embedding layer (`\\s`, `\\n`, `\\[`), so browser JS receives the intended regex.
|
|
97
|
+
- Prefer `JSON.stringify(value)` when injecting arbitrary text into inline script.
|
|
98
|
+
- After touching inline `<script>` sections in `index.ts`, do a `/studio` boot smoke test immediately.
|
|
99
|
+
|
|
90
100
|
## Acceptance criteria
|
|
91
101
|
|
|
92
102
|
1. `/studio --last` opens with editor loaded and no required mode selection.
|
|
Binary file
|
|
Binary file
|
package/index.ts
CHANGED
|
@@ -1952,6 +1952,31 @@ ${cssVarsBlock}
|
|
|
1952
1952
|
background: var(--panel-2);
|
|
1953
1953
|
font-weight: 600;
|
|
1954
1954
|
font-size: 14px;
|
|
1955
|
+
display: flex;
|
|
1956
|
+
align-items: center;
|
|
1957
|
+
justify-content: space-between;
|
|
1958
|
+
gap: 8px;
|
|
1959
|
+
flex-wrap: wrap;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
.section-header-main {
|
|
1963
|
+
display: inline-flex;
|
|
1964
|
+
align-items: center;
|
|
1965
|
+
min-width: 0;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
.section-header-actions {
|
|
1969
|
+
display: inline-flex;
|
|
1970
|
+
align-items: center;
|
|
1971
|
+
gap: 8px;
|
|
1972
|
+
flex-wrap: wrap;
|
|
1973
|
+
justify-content: flex-end;
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
.section-header-actions button {
|
|
1977
|
+
padding: 6px 9px;
|
|
1978
|
+
font-size: 12px;
|
|
1979
|
+
border-radius: 7px;
|
|
1955
1980
|
}
|
|
1956
1981
|
|
|
1957
1982
|
.section-header select {
|
|
@@ -2034,10 +2059,19 @@ ${cssVarsBlock}
|
|
|
2034
2059
|
}
|
|
2035
2060
|
|
|
2036
2061
|
.source-actions {
|
|
2062
|
+
display: flex;
|
|
2063
|
+
flex-direction: column;
|
|
2064
|
+
gap: 6px;
|
|
2065
|
+
align-items: stretch;
|
|
2066
|
+
width: 100%;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
.source-actions-row {
|
|
2037
2070
|
display: flex;
|
|
2038
2071
|
gap: 6px;
|
|
2039
2072
|
flex-wrap: wrap;
|
|
2040
2073
|
align-items: center;
|
|
2074
|
+
min-width: 0;
|
|
2041
2075
|
}
|
|
2042
2076
|
|
|
2043
2077
|
.source-actions button,
|
|
@@ -2585,11 +2619,29 @@ ${cssVarsBlock}
|
|
|
2585
2619
|
}
|
|
2586
2620
|
|
|
2587
2621
|
.response-actions {
|
|
2622
|
+
display: flex;
|
|
2623
|
+
flex-direction: column;
|
|
2624
|
+
align-items: stretch;
|
|
2625
|
+
gap: 8px;
|
|
2626
|
+
}
|
|
2627
|
+
|
|
2628
|
+
.response-actions-row {
|
|
2588
2629
|
display: flex;
|
|
2589
2630
|
align-items: center;
|
|
2590
|
-
justify-content: flex-start;
|
|
2591
2631
|
gap: 8px;
|
|
2592
2632
|
flex-wrap: wrap;
|
|
2633
|
+
min-width: 0;
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
.response-actions-row.history-row {
|
|
2637
|
+
flex-wrap: nowrap;
|
|
2638
|
+
overflow-x: auto;
|
|
2639
|
+
padding-bottom: 2px;
|
|
2640
|
+
scrollbar-width: thin;
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2643
|
+
.response-actions-row.history-row > * {
|
|
2644
|
+
flex: 0 0 auto;
|
|
2593
2645
|
}
|
|
2594
2646
|
|
|
2595
2647
|
footer {
|
|
@@ -2765,55 +2817,61 @@ ${cssVarsBlock}
|
|
|
2765
2817
|
<span id="syncBadge" class="source-badge sync-badge">No response loaded</span>
|
|
2766
2818
|
</div>
|
|
2767
2819
|
<div class="source-actions">
|
|
2768
|
-
<
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
<
|
|
2772
|
-
<
|
|
2773
|
-
</
|
|
2774
|
-
<
|
|
2775
|
-
<
|
|
2776
|
-
<
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
<
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
<
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
<
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2820
|
+
<div class="source-actions-row">
|
|
2821
|
+
<button id="sendRunBtn" type="button" title="Send editor text directly to the model as-is. Shortcut: Cmd/Ctrl+Enter when editor pane is active.">Run editor text</button>
|
|
2822
|
+
<button id="copyDraftBtn" type="button">Copy editor text</button>
|
|
2823
|
+
<button id="sendEditorBtn" type="button">Send to pi editor</button>
|
|
2824
|
+
<button id="getEditorBtn" type="button" title="Load the current terminal editor draft into Studio.">Load from pi editor</button>
|
|
2825
|
+
</div>
|
|
2826
|
+
<div class="source-actions-row">
|
|
2827
|
+
<button id="insertHeaderBtn" type="button" title="Insert annotated-reply protocol header (source metadata, [an: ...] syntax hint, precedence note, and end marker).">Insert annotated reply header</button>
|
|
2828
|
+
<select id="annotationModeSelect" aria-label="Annotation visibility mode" title="On: keep and send [an: ...] markers. Hidden: keep markers in editor, hide in preview, and strip before Run/Critique.">
|
|
2829
|
+
<option value="on" selected>Annotations: On</option>
|
|
2830
|
+
<option value="off">Annotations: Hidden</option>
|
|
2831
|
+
</select>
|
|
2832
|
+
<button id="stripAnnotationsBtn" type="button" title="Destructively remove all [an: ...] markers from editor text.">Strip annotations…</button>
|
|
2833
|
+
<button id="saveAnnotatedBtn" type="button" title="Save full editor content (including [an: ...] markers) as a .annotated.md file.">Save .annotated.md</button>
|
|
2834
|
+
</div>
|
|
2835
|
+
<div class="source-actions-row">
|
|
2836
|
+
<select id="lensSelect" aria-label="Critique focus">
|
|
2837
|
+
<option value="auto" selected>Critique focus: Auto</option>
|
|
2838
|
+
<option value="writing">Critique focus: Writing</option>
|
|
2839
|
+
<option value="code">Critique focus: Code</option>
|
|
2840
|
+
</select>
|
|
2841
|
+
<button id="critiqueBtn" type="button">Critique editor text</button>
|
|
2842
|
+
<select id="highlightSelect" aria-label="Editor syntax highlighting">
|
|
2843
|
+
<option value="off">Syntax highlight: Off</option>
|
|
2844
|
+
<option value="on" selected>Syntax highlight: On</option>
|
|
2845
|
+
</select>
|
|
2846
|
+
<select id="langSelect" aria-label="Highlight language">
|
|
2847
|
+
<option value="markdown" selected>Lang: Markdown</option>
|
|
2848
|
+
<option value="javascript">Lang: JavaScript</option>
|
|
2849
|
+
<option value="typescript">Lang: TypeScript</option>
|
|
2850
|
+
<option value="python">Lang: Python</option>
|
|
2851
|
+
<option value="bash">Lang: Bash</option>
|
|
2852
|
+
<option value="json">Lang: JSON</option>
|
|
2853
|
+
<option value="rust">Lang: Rust</option>
|
|
2854
|
+
<option value="c">Lang: C</option>
|
|
2855
|
+
<option value="cpp">Lang: C++</option>
|
|
2856
|
+
<option value="julia">Lang: Julia</option>
|
|
2857
|
+
<option value="fortran">Lang: Fortran</option>
|
|
2858
|
+
<option value="r">Lang: R</option>
|
|
2859
|
+
<option value="matlab">Lang: MATLAB</option>
|
|
2860
|
+
<option value="latex">Lang: LaTeX</option>
|
|
2861
|
+
<option value="diff">Lang: Diff</option>
|
|
2862
|
+
<option value="java">Lang: Java</option>
|
|
2863
|
+
<option value="go">Lang: Go</option>
|
|
2864
|
+
<option value="ruby">Lang: Ruby</option>
|
|
2865
|
+
<option value="swift">Lang: Swift</option>
|
|
2866
|
+
<option value="html">Lang: HTML</option>
|
|
2867
|
+
<option value="css">Lang: CSS</option>
|
|
2868
|
+
<option value="xml">Lang: XML</option>
|
|
2869
|
+
<option value="yaml">Lang: YAML</option>
|
|
2870
|
+
<option value="toml">Lang: TOML</option>
|
|
2871
|
+
<option value="lua">Lang: Lua</option>
|
|
2872
|
+
<option value="text">Lang: Plain Text</option>
|
|
2873
|
+
</select>
|
|
2874
|
+
</div>
|
|
2817
2875
|
</div>
|
|
2818
2876
|
</div>
|
|
2819
2877
|
<div id="sourceEditorWrap" class="editor-highlight-wrap">
|
|
@@ -2826,11 +2884,16 @@ ${cssVarsBlock}
|
|
|
2826
2884
|
|
|
2827
2885
|
<section id="rightPane">
|
|
2828
2886
|
<div id="rightSectionHeader" class="section-header">
|
|
2829
|
-
<
|
|
2830
|
-
<
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2887
|
+
<div class="section-header-main">
|
|
2888
|
+
<select id="rightViewSelect" aria-label="Response view mode">
|
|
2889
|
+
<option value="markdown">Response (Raw)</option>
|
|
2890
|
+
<option value="preview" selected>Response (Preview)</option>
|
|
2891
|
+
<option value="editor-preview">Editor (Preview)</option>
|
|
2892
|
+
</select>
|
|
2893
|
+
</div>
|
|
2894
|
+
<div class="section-header-actions">
|
|
2895
|
+
<button id="exportPdfBtn" type="button" title="Export the current right-pane preview as PDF via pandoc + xelatex.">Export right preview as PDF</button>
|
|
2896
|
+
</div>
|
|
2834
2897
|
</div>
|
|
2835
2898
|
<div class="reference-meta">
|
|
2836
2899
|
<span id="referenceBadge" class="source-badge">Latest response: none</span>
|
|
@@ -2838,24 +2901,29 @@ ${cssVarsBlock}
|
|
|
2838
2901
|
<div id="critiqueView" class="panel-scroll rendered-markdown"><pre class="plain-markdown">No response yet.</pre></div>
|
|
2839
2902
|
<div class="response-wrap">
|
|
2840
2903
|
<div id="responseActions" class="response-actions">
|
|
2841
|
-
<
|
|
2842
|
-
<
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
<
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
<
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
<
|
|
2858
|
-
|
|
2904
|
+
<div class="response-actions-row">
|
|
2905
|
+
<select id="followSelect" aria-label="Auto-update response">
|
|
2906
|
+
<option value="on" selected>Auto-update response: On</option>
|
|
2907
|
+
<option value="off">Auto-update response: Off</option>
|
|
2908
|
+
</select>
|
|
2909
|
+
<select id="responseHighlightSelect" aria-label="Response markdown highlighting">
|
|
2910
|
+
<option value="off">Syntax highlight: Off</option>
|
|
2911
|
+
<option value="on" selected>Syntax highlight: On</option>
|
|
2912
|
+
</select>
|
|
2913
|
+
</div>
|
|
2914
|
+
<div class="response-actions-row history-row">
|
|
2915
|
+
<button id="pullLatestBtn" type="button" title="Fetch the latest assistant response when auto-update is off.">Get latest response</button>
|
|
2916
|
+
<button id="historyPrevBtn" type="button" title="Show previous response in history.">◀ Prev response</button>
|
|
2917
|
+
<span id="historyIndexBadge" class="source-badge">History: 0/0</span>
|
|
2918
|
+
<button id="historyNextBtn" type="button" title="Show next response in history.">Next response ▶</button>
|
|
2919
|
+
</div>
|
|
2920
|
+
<div class="response-actions-row">
|
|
2921
|
+
<button id="loadHistoryPromptBtn" type="button" title="Load the prompt that generated the selected response into the editor.">Load response prompt into editor</button>
|
|
2922
|
+
<button id="loadResponseBtn" type="button">Load response into editor</button>
|
|
2923
|
+
<button id="loadCritiqueNotesBtn" type="button" hidden>Load critique notes into editor</button>
|
|
2924
|
+
<button id="loadCritiqueFullBtn" type="button" hidden>Load full critique into editor</button>
|
|
2925
|
+
<button id="copyResponseBtn" type="button">Copy response text</button>
|
|
2926
|
+
</div>
|
|
2859
2927
|
</div>
|
|
2860
2928
|
</div>
|
|
2861
2929
|
</section>
|
|
@@ -5751,10 +5819,15 @@ ${cssVarsBlock}
|
|
|
5751
5819
|
const sourceDescriptor = describeSourceForAnnotation();
|
|
5752
5820
|
let header = "annotated reply below:\\n";
|
|
5753
5821
|
header += "original source: " + sourceDescriptor + "\\n";
|
|
5754
|
-
header += "annotation syntax: [an: your note]\\n
|
|
5822
|
+
header += "annotation syntax: [an: your note]\\n";
|
|
5823
|
+
header += "precedence: later messages supersede these annotations unless user explicitly references them\\n\\n---\\n\\n";
|
|
5755
5824
|
return header;
|
|
5756
5825
|
}
|
|
5757
5826
|
|
|
5827
|
+
function stripAnnotationBoundaryMarker(text) {
|
|
5828
|
+
return String(text || "").replace(/\\n{0,2}--- end annotations ---\\s*$/i, "");
|
|
5829
|
+
}
|
|
5830
|
+
|
|
5758
5831
|
function stripAnnotationHeader(text) {
|
|
5759
5832
|
const normalized = String(text || "").replace(/\\r\\n/g, "\\n");
|
|
5760
5833
|
if (!normalized.toLowerCase().startsWith("annotated reply below:")) {
|
|
@@ -5773,7 +5846,7 @@ ${cssVarsBlock}
|
|
|
5773
5846
|
|
|
5774
5847
|
return {
|
|
5775
5848
|
hadHeader: true,
|
|
5776
|
-
body: normalized.slice(cursor),
|
|
5849
|
+
body: stripAnnotationBoundaryMarker(normalized.slice(cursor)),
|
|
5777
5850
|
};
|
|
5778
5851
|
}
|
|
5779
5852
|
|
|
@@ -5786,7 +5859,7 @@ ${cssVarsBlock}
|
|
|
5786
5859
|
return;
|
|
5787
5860
|
}
|
|
5788
5861
|
insertHeaderBtn.textContent = "Insert annotated reply header";
|
|
5789
|
-
insertHeaderBtn.title = "Insert annotated-reply protocol header (
|
|
5862
|
+
insertHeaderBtn.title = "Insert annotated-reply protocol header (source metadata, [an: ...] syntax hint, precedence note, and end marker).";
|
|
5790
5863
|
}
|
|
5791
5864
|
|
|
5792
5865
|
function toggleAnnotatedReplyHeader() {
|
|
@@ -5800,7 +5873,8 @@ ${cssVarsBlock}
|
|
|
5800
5873
|
return;
|
|
5801
5874
|
}
|
|
5802
5875
|
|
|
5803
|
-
const
|
|
5876
|
+
const cleanedBody = stripAnnotationBoundaryMarker(stripped.body);
|
|
5877
|
+
const updated = buildAnnotationHeader() + cleanedBody + "\\n\\n--- end annotations ---\\n\\n";
|
|
5804
5878
|
if (isTextEquivalent(sourceTextEl.value, updated)) {
|
|
5805
5879
|
setStatus("Annotated reply header already present.");
|
|
5806
5880
|
return;
|