pi-studio 0.6.4 → 0.6.6

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 CHANGED
@@ -4,6 +4,16 @@ All notable changes to `pi-studio` are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.6.6] — 2026-04-30
8
+
9
+ ### Changed
10
+ - Studio now opens the right pane on **Editor (Preview)** by default for file-backed and blank launches, while last-response launches still open on the response preview.
11
+
12
+ ## [0.6.5] — 2026-04-30
13
+
14
+ ### Changed
15
+ - Working view output blocks now show a compact 50-line preview by default with per-block **Show full** / **Collapse** controls for longer thinking, responses, and tool input/output.
16
+
7
17
  ## [0.6.4] — 2026-04-29
8
18
 
9
19
  ### Fixed
@@ -174,8 +174,13 @@
174
174
  let pendingKind = null;
175
175
  let stickyStudioKind = null;
176
176
  let initialDocumentApplied = false;
177
- let editorView = isEditorOnlyMode ? "markdown" : "markdown";
178
- let rightView = isEditorOnlyMode ? "editor-preview" : "preview";
177
+ function getInitialRightView(source) {
178
+ if (isEditorOnlyMode) return "editor-preview";
179
+ return String(source || "").trim() === "last-response" ? "preview" : "editor-preview";
180
+ }
181
+
182
+ let editorView = "markdown";
183
+ let rightView = getInitialRightView(initialSourceState.source);
179
184
  let followLatest = !isEditorOnlyMode;
180
185
  let queuedLatestResponse = null;
181
186
  let latestResponseMarkdown = "";
@@ -194,6 +199,9 @@
194
199
  let traceFilter = "all";
195
200
  let traceAutoScroll = true;
196
201
  let traceRenderRaf = null;
202
+ const traceExpandedOutputs = new Set();
203
+ const TRACE_OUTPUT_PREVIEW_MAX_LINES = 50;
204
+ const TRACE_OUTPUT_PREVIEW_MAX_CHARS = 8000;
197
205
  let studioRunChainActive = false;
198
206
  let queuedSteeringCount = 0;
199
207
  let agentBusyFromServer = false;
@@ -322,7 +330,11 @@
322
330
  }
323
331
 
324
332
  function replaceTraceState(nextState) {
333
+ const previousRunId = traceState && traceState.runId ? traceState.runId : null;
325
334
  traceState = normalizeTraceState(nextState);
335
+ if ((traceState.runId || null) !== previousRunId) {
336
+ traceExpandedOutputs.clear();
337
+ }
326
338
  renderTraceViewIfActive();
327
339
  }
328
340
 
@@ -2971,6 +2983,21 @@
2971
2983
  setTraceFilter(nextFilter);
2972
2984
  return;
2973
2985
  }
2986
+ const outputToggleBtn = target instanceof Element ? target.closest("[data-trace-output-key]") : null;
2987
+ if (outputToggleBtn) {
2988
+ event.preventDefault();
2989
+ const key = outputToggleBtn.getAttribute("data-trace-output-key") || "";
2990
+ if (key) {
2991
+ if (traceExpandedOutputs.has(key)) {
2992
+ traceExpandedOutputs.delete(key);
2993
+ } else {
2994
+ traceExpandedOutputs.add(key);
2995
+ }
2996
+ traceAutoScroll = false;
2997
+ renderTraceViewIfActive();
2998
+ }
2999
+ return;
3000
+ }
2974
3001
  const actionBtn = target instanceof Element ? target.closest("[data-trace-action]") : null;
2975
3002
  if (!actionBtn) return;
2976
3003
  event.preventDefault();
@@ -3607,6 +3634,65 @@
3607
3634
  return remaining < 56;
3608
3635
  }
3609
3636
 
3637
+ function formatTraceOutputSize(text) {
3638
+ const value = String(text || "");
3639
+ const chars = value.length;
3640
+ const lines = value ? value.split(/\n/).length : 0;
3641
+ const compactChars = chars >= 1000 ? ((chars / 1000).toFixed(chars >= 10_000 ? 0 : 1) + "k") : String(chars);
3642
+ return lines + " line" + (lines === 1 ? "" : "s") + ", " + compactChars + " chars";
3643
+ }
3644
+
3645
+ function getTraceOutputPreview(text) {
3646
+ const value = String(text || "");
3647
+ const lines = value.split(/\n/);
3648
+ let preview = value;
3649
+ let truncated = false;
3650
+ if (lines.length > TRACE_OUTPUT_PREVIEW_MAX_LINES) {
3651
+ preview = lines.slice(0, TRACE_OUTPUT_PREVIEW_MAX_LINES).join("\n");
3652
+ truncated = true;
3653
+ }
3654
+ if (preview.length > TRACE_OUTPUT_PREVIEW_MAX_CHARS) {
3655
+ preview = preview.slice(0, TRACE_OUTPUT_PREVIEW_MAX_CHARS);
3656
+ truncated = true;
3657
+ }
3658
+ if (!truncated && value.length <= TRACE_OUTPUT_PREVIEW_MAX_CHARS) {
3659
+ return { text: value, truncated: false, hiddenChars: 0, hiddenLines: 0 };
3660
+ }
3661
+ if (!truncated && value.length > TRACE_OUTPUT_PREVIEW_MAX_CHARS) {
3662
+ preview = value.slice(0, TRACE_OUTPUT_PREVIEW_MAX_CHARS);
3663
+ truncated = true;
3664
+ }
3665
+ const hiddenChars = Math.max(0, value.length - preview.length);
3666
+ const previewLineCount = preview ? preview.split(/\n/).length : 0;
3667
+ const hiddenLines = Math.max(0, lines.length - previewLineCount);
3668
+ return { text: preview, truncated: true, hiddenChars, hiddenLines };
3669
+ }
3670
+
3671
+ function renderTraceOutput(text, outputKey) {
3672
+ const value = String(text || "");
3673
+ const key = String(outputKey || "trace-output");
3674
+ const isExpanded = traceExpandedOutputs.has(key);
3675
+ const preview = getTraceOutputPreview(value);
3676
+ const visibleText = isExpanded || !preview.truncated ? value : preview.text;
3677
+ const body = "<pre class='plain-markdown trace-output'>" + escapeHtml(visibleText) + "</pre>";
3678
+ if (!preview.truncated) return body;
3679
+
3680
+ const hiddenParts = [];
3681
+ if (preview.hiddenLines > 0) hiddenParts.push(preview.hiddenLines + " more line" + (preview.hiddenLines === 1 ? "" : "s"));
3682
+ if (preview.hiddenChars > 0) hiddenParts.push(formatCompactNumber(preview.hiddenChars) + " chars hidden");
3683
+ const summary = isExpanded
3684
+ ? "Showing full output (" + formatTraceOutputSize(value) + ")."
3685
+ : "Output truncated — " + (hiddenParts.join(", ") || "more hidden") + ".";
3686
+ const buttonLabel = isExpanded ? "Collapse" : "Show full";
3687
+ return "<div class='trace-output-wrap" + (isExpanded ? " is-expanded" : " is-truncated") + "'>"
3688
+ + body
3689
+ + "<div class='trace-output-truncation'>"
3690
+ + "<span>" + escapeHtml(summary) + "</span>"
3691
+ + "<button type='button' class='trace-output-toggle' data-trace-output-key='" + escapeHtml(key) + "' aria-expanded='" + (isExpanded ? "true" : "false") + "'>" + escapeHtml(buttonLabel) + "</button>"
3692
+ + "</div>"
3693
+ + "</div>";
3694
+ }
3695
+
3610
3696
  function buildTracePanelHtml() {
3611
3697
  const state = traceState || createEmptyTraceState();
3612
3698
  const filter = normalizeTraceFilter(traceFilter);
@@ -3656,7 +3742,7 @@
3656
3742
  sections.push(
3657
3743
  "<div class='trace-section'>"
3658
3744
  + "<div class='trace-section-label'>Thinking</div>"
3659
- + "<pre class='plain-markdown trace-output'>" + escapeHtml(entry.thinking) + "</pre>"
3745
+ + renderTraceOutput(entry.thinking, entry.id + ":thinking")
3660
3746
  + "</div>"
3661
3747
  );
3662
3748
  }
@@ -3664,7 +3750,7 @@
3664
3750
  sections.push(
3665
3751
  "<div class='trace-section'>"
3666
3752
  + "<div class='trace-section-label'>Response</div>"
3667
- + "<pre class='plain-markdown trace-output'>" + escapeHtml(entry.text) + "</pre>"
3753
+ + renderTraceOutput(entry.text, entry.id + ":response")
3668
3754
  + "</div>"
3669
3755
  );
3670
3756
  }
@@ -3684,10 +3770,10 @@
3684
3770
 
3685
3771
  const title = entry.label || entry.toolName || "tool";
3686
3772
  const argsSummary = entry.argsSummary
3687
- ? "<div class='trace-section'><div class='trace-section-label'>Input</div><pre class='plain-markdown trace-output'>" + escapeHtml(entry.argsSummary) + "</pre></div>"
3773
+ ? "<div class='trace-section'><div class='trace-section-label'>Input</div>" + renderTraceOutput(entry.argsSummary, entry.id + ":input") + "</div>"
3688
3774
  : "";
3689
3775
  const output = entry.output
3690
- ? "<div class='trace-section'><div class='trace-section-label'>Output</div><pre class='plain-markdown trace-output'>" + escapeHtml(entry.output) + "</pre></div>"
3776
+ ? "<div class='trace-section'><div class='trace-section-label'>Output</div>" + renderTraceOutput(entry.output, entry.id + ":output") + "</div>"
3691
3777
  : "<div class='trace-empty-inline'>No output yet.</div>";
3692
3778
  const toolStatusLabel = entry.isError
3693
3779
  ? "Error"
package/client/studio.css CHANGED
@@ -1822,17 +1822,51 @@
1822
1822
  letter-spacing: 0.04em;
1823
1823
  }
1824
1824
 
1825
- .trace-output {
1826
- padding: 10px 11px;
1827
- border-radius: 8px;
1825
+ .trace-output-wrap {
1828
1826
  border: 1px solid var(--panel-border);
1827
+ border-radius: 8px;
1829
1828
  background: var(--panel);
1829
+ overflow: hidden;
1830
+ }
1831
+
1832
+ .trace-output {
1833
+ margin: 0;
1834
+ padding: 10px 11px;
1835
+ border: 0;
1836
+ border-radius: 0;
1837
+ background: transparent;
1830
1838
  overflow-x: auto;
1831
1839
  white-space: pre-wrap;
1832
1840
  overflow-wrap: anywhere;
1833
1841
  font-size: var(--studio-working-font-size);
1834
1842
  }
1835
1843
 
1844
+ .trace-section > .trace-output {
1845
+ border: 1px solid var(--panel-border);
1846
+ border-radius: 8px;
1847
+ background: var(--panel);
1848
+ }
1849
+
1850
+ .trace-output-truncation {
1851
+ display: flex;
1852
+ align-items: center;
1853
+ justify-content: space-between;
1854
+ gap: 8px;
1855
+ padding: 6px 8px;
1856
+ border-top: 1px solid var(--border-subtle);
1857
+ color: var(--studio-info-text, var(--muted));
1858
+ font-size: 11px;
1859
+ background: var(--panel-2);
1860
+ }
1861
+
1862
+ .trace-output-toggle {
1863
+ flex: 0 0 auto;
1864
+ padding: 3px 7px;
1865
+ border-radius: 999px;
1866
+ font-size: 11px;
1867
+ line-height: 1.2;
1868
+ }
1869
+
1836
1870
  .trace-empty,
1837
1871
  .trace-empty-inline {
1838
1872
  color: var(--muted);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.6.4",
3
+ "version": "0.6.6",
4
4
  "description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, prompt/response history, and live Markdown/LaTeX/code preview",
5
5
  "type": "module",
6
6
  "license": "MIT",