pi-studio 0.9.61 → 0.9.63

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,20 @@ All notable changes to `pi-studio` are documented here.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.63] — 2026-10-08
8
+
9
+ ### Fixed
10
+ - The cursor fix in 0.9.62 didn't reach Studio's own editor, so in Safari and other WebKit browsers such as Muxy and cmux the cursor could still drift away from the text after an edit let a word move back up a line. The re-layout now works in Studio's layout.
11
+ - After a macOS autocorrect, that re-layout could switch itself off until you moved to another line. Typing, an ordinary edit or leaving the editor now turns it back on, and the editor also re-lays out once whenever you pause typing.
12
+
13
+ ## [0.9.62] — 2026-10-08
14
+
15
+ ### Fixed
16
+ - With syntax highlighting on, the cursor could end up several characters away from the text being typed, mostly in Safari and other WebKit browsers such as Muxy and cmux. It happened when an edit let a word move back up a line. The editor now re-lays out its text after each edit, so the cursor and text stay together. A line that is exactly full can still show a brief mismatch, which corrects itself on the next key.
17
+
18
+ ### Added
19
+ - `Ctrl+Shift+H` toggles syntax highlighting without leaving the editor.
20
+
7
21
  ## [0.9.61] — 2026-09-30
8
22
 
9
23
  ### Fixed
@@ -2587,6 +2587,7 @@
2587
2587
  let pendingResponseScrollReset = false;
2588
2588
  let editorMetaUpdateRaf = null;
2589
2589
  let editorHighlightEnabled = false;
2590
+ let editorWrapCompositionActive = false;
2590
2591
  let editorLanguage = "markdown";
2591
2592
  let responseHighlightEnabled = false;
2592
2593
  let completionSuggestionState = null;
@@ -17999,6 +18000,56 @@
17999
18000
  return EXT_TO_LANG[ext] || "";
18000
18001
  }
18001
18002
 
18003
+ function repairEditorTextareaWrap(event) {
18004
+ // A composition can start without a matching end (e.g. macOS autocorrect in WebKit);
18005
+ // an ordinary input event proves it is over, so a stale flag cannot disable the repair.
18006
+ if (event?.type === "input" && event.isComposing === false) editorWrapCompositionActive = false;
18007
+ if (!editorHighlightEnabled || editorView !== "markdown" || editorWrapCompositionActive || event?.isComposing) return false;
18008
+ // WebKit can keep the previous soft-wrap layout after a native edit,
18009
+ // while the rebuilt overlay has already moved a word onto its prior line.
18010
+ // Narrow the text by 1px for one forced layout, then restore it, without
18011
+ // replacing text or touching native undo/IME. Nudge the padding, not the
18012
+ // width: the editor is a flex item that grows back to full width, so a
18013
+ // width nudge never reached its text layout (checked in WebKit, 8 Oct).
18014
+ const style = sourceTextEl.style;
18015
+ const padding = style.getPropertyValue("padding-right");
18016
+ const priority = style.getPropertyPriority("padding-right");
18017
+ const computed = window.getComputedStyle(sourceTextEl).paddingRight || "0px";
18018
+ const top = sourceTextEl.scrollTop, left = sourceTextEl.scrollLeft;
18019
+ try {
18020
+ style.setProperty("padding-right", "calc(" + computed + " + 1px)", "important");
18021
+ void sourceTextEl.offsetWidth;
18022
+ } finally {
18023
+ if (padding) style.setProperty("padding-right", padding, priority);
18024
+ else style.removeProperty("padding-right");
18025
+ void sourceTextEl.offsetWidth;
18026
+ sourceTextEl.scrollTop = top; sourceTextEl.scrollLeft = left;
18027
+ }
18028
+ syncEditorHighlightScroll();
18029
+ return true;
18030
+ }
18031
+
18032
+ // Catch-all: lay the text out again once typing pauses, covering edits made
18033
+ // outside the ordinary input path (autocorrect, dictation, text replacement).
18034
+ let editorWrapPauseTimer = null;
18035
+ function scheduleEditorWrapPauseRepair() {
18036
+ if (editorWrapPauseTimer !== null) window.clearTimeout(editorWrapPauseTimer);
18037
+ editorWrapPauseTimer = window.setTimeout(() => { editorWrapPauseTimer = null; repairEditorTextareaWrap(); }, 300);
18038
+ }
18039
+
18040
+ function handleEditorHighlightShortcut(event) {
18041
+ if (!event || event.defaultPrevented || event.isComposing || editorWrapCompositionActive || event.target !== sourceTextEl
18042
+ || editorView !== "markdown" || sourceTextEl.disabled || sourceTextEl.readOnly || studioModalBlocksDraftAction()
18043
+ || !event.ctrlKey || !event.shiftKey || event.metaKey || event.altKey
18044
+ || !(event.code === "KeyH" || String(event.key || "").toLowerCase() === "h")) return false;
18045
+ event.preventDefault(); event.stopPropagation();
18046
+ if (!event.repeat) {
18047
+ setEditorHighlightEnabled(!editorHighlightEnabled);
18048
+ repairEditorTextareaWrap();
18049
+ }
18050
+ return true;
18051
+ }
18052
+
18002
18053
  function renderEditorHighlightNow() {
18003
18054
  if (!sourceHighlightEl) return;
18004
18055
  if (!editorHighlightEnabled || editorView !== "markdown") {
@@ -24943,9 +24994,18 @@
24943
24994
  requestLatestResponse();
24944
24995
  });
24945
24996
 
24997
+ sourceTextEl.addEventListener("keydown", event => { if (!event.isComposing && event.keyCode !== 229) editorWrapCompositionActive = false; });
24998
+ sourceTextEl.addEventListener("blur", () => { editorWrapCompositionActive = false; });
24946
24999
  sourceTextEl.addEventListener("keydown", handleSourceTextTabKey);
25000
+ sourceTextEl.addEventListener("keydown", handleEditorHighlightShortcut);
25001
+ sourceTextEl.addEventListener("compositionstart", () => { editorWrapCompositionActive = true; });
25002
+ sourceTextEl.addEventListener("compositionend", () => {
25003
+ editorWrapCompositionActive = false;
25004
+ repairEditorTextareaWrap();
25005
+ });
24947
25006
 
24948
- sourceTextEl.addEventListener("input", () => {
25007
+ sourceTextEl.addEventListener("input", event => {
25008
+ repairEditorTextareaWrap(event); scheduleEditorWrapPauseRepair();
24949
25009
  if (completionSuggestionState && sourceTextEl.value !== completionSuggestionState.baseText) {
24950
25010
  hideCompletionSuggestion();
24951
25011
  }
package/index.ts CHANGED
@@ -12515,7 +12515,7 @@ ${cssVarsBlock}
12515
12515
  <button id="showMeResponseBtn" type="button" hidden title="Explain the response displayed in the right pane using the smallest useful visual or structural representation.">Explain displayed response</button>
12516
12516
  <button id="askAsideBtn" type="button" title="Open a separate side-question thread without adding it to the main Pi conversation.">Side question</button>
12517
12517
  <button id="quizBtn" type="button" title="Open an active quiz for the current editor selection or document.">Quiz me</button>
12518
- <select id="highlightSelect" class="studio-flat-select" aria-label="Editor syntax highlighting">
12518
+ <select id="highlightSelect" class="studio-flat-select" aria-label="Editor syntax highlighting" aria-keyshortcuts="Control+Shift+H">
12519
12519
  <option value="off">Syntax highlight: Off</option>
12520
12520
  <option value="bash">Syntax highlight: Bash</option>
12521
12521
  <option value="c">Syntax highlight: C</option>
@@ -12806,6 +12806,7 @@ ${cssVarsBlock}
12806
12806
  <div><dt>Tab</dt><dd>Insert a visible completion suggestion; otherwise indent selected editor text</dd></div>
12807
12807
  <div><dt>Esc</dt><dd>Dismiss a visible completion suggestion, close overlays, exit pane focus, or stop an active request</dd></div>
12808
12808
  <div><dt>Shift+Tab</dt><dd>Unindent selected editor text</dd></div>
12809
+ <div><dt>Ctrl+Shift+H</dt><dd>Toggle editor syntax highlighting</dd></div>
12809
12810
  </dl>
12810
12811
  </section>
12811
12812
  <section class="shortcuts-group shortcuts-full-only">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.9.61",
3
+ "version": "0.9.63",
4
4
  "description": "Two-pane browser workspace for pi with prompt/response editing, annotations, critiques, active quiz, prompt/response history, live previews, and tmux-backed REPL/literate REPL workflows",
5
5
  "type": "module",
6
6
  "license": "MIT",