pi-studio 0.9.62 → 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,12 @@ 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
+
7
13
  ## [0.9.62] — 2026-10-08
8
14
 
9
15
  ### Fixed
@@ -18001,19 +18001,27 @@
18001
18001
  }
18002
18002
 
18003
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;
18004
18007
  if (!editorHighlightEnabled || editorView !== "markdown" || editorWrapCompositionActive || event?.isComposing) return false;
18005
18008
  // WebKit can keep the previous soft-wrap layout after a native edit,
18006
18009
  // while the rebuilt overlay has already moved a word onto its prior line.
18007
- // Force both layouts without replacing text or touching native undo/IME.
18008
- const width = sourceTextEl.style.width;
18009
- const priority = sourceTextEl.style.getPropertyPriority("width");
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";
18010
18018
  const top = sourceTextEl.scrollTop, left = sourceTextEl.scrollLeft;
18011
18019
  try {
18012
- sourceTextEl.style.setProperty("width", "calc(" + (width || "100%") + " - 1px)", priority);
18020
+ style.setProperty("padding-right", "calc(" + computed + " + 1px)", "important");
18013
18021
  void sourceTextEl.offsetWidth;
18014
18022
  } finally {
18015
- if (width) sourceTextEl.style.setProperty("width", width, priority);
18016
- else sourceTextEl.style.removeProperty("width");
18023
+ if (padding) style.setProperty("padding-right", padding, priority);
18024
+ else style.removeProperty("padding-right");
18017
18025
  void sourceTextEl.offsetWidth;
18018
18026
  sourceTextEl.scrollTop = top; sourceTextEl.scrollLeft = left;
18019
18027
  }
@@ -18021,6 +18029,14 @@
18021
18029
  return true;
18022
18030
  }
18023
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
+
18024
18040
  function handleEditorHighlightShortcut(event) {
18025
18041
  if (!event || event.defaultPrevented || event.isComposing || editorWrapCompositionActive || event.target !== sourceTextEl
18026
18042
  || editorView !== "markdown" || sourceTextEl.disabled || sourceTextEl.readOnly || studioModalBlocksDraftAction()
@@ -24978,6 +24994,8 @@
24978
24994
  requestLatestResponse();
24979
24995
  });
24980
24996
 
24997
+ sourceTextEl.addEventListener("keydown", event => { if (!event.isComposing && event.keyCode !== 229) editorWrapCompositionActive = false; });
24998
+ sourceTextEl.addEventListener("blur", () => { editorWrapCompositionActive = false; });
24981
24999
  sourceTextEl.addEventListener("keydown", handleSourceTextTabKey);
24982
25000
  sourceTextEl.addEventListener("keydown", handleEditorHighlightShortcut);
24983
25001
  sourceTextEl.addEventListener("compositionstart", () => { editorWrapCompositionActive = true; });
@@ -24987,7 +25005,7 @@
24987
25005
  });
24988
25006
 
24989
25007
  sourceTextEl.addEventListener("input", event => {
24990
- repairEditorTextareaWrap(event);
25008
+ repairEditorTextareaWrap(event); scheduleEditorWrapPauseRepair();
24991
25009
  if (completionSuggestionState && sourceTextEl.value !== completionSuggestionState.baseText) {
24992
25010
  hideCompletionSuggestion();
24993
25011
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.9.62",
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",