pi-studio 0.9.39 → 0.9.40

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.40] — 2026-08-10
8
+
9
+ ### Fixed
10
+ - Prevented Studio-owned REPL sessions from inheriting terminal-local `PROMPT_COMMAND` hooks without their function definitions, avoiding repeated `__bp_precmd_invoke_cmd` / `__bp_interactive_mode` errors under cmux and Ghostty on macOS Bash 3.2.
11
+ - Show the reliable `Ctrl+Shift+Enter` shortcut on the **Send to REPL** button on macOS while continuing to accept `Cmd+Shift+Enter` where the browser or host does not reserve it.
12
+
7
13
  ## [0.9.39] — 2026-08-07
8
14
 
9
15
  ### Added
@@ -1210,11 +1210,21 @@
1210
1210
 
1211
1211
  function getStudioShortcutLabel(kind) {
1212
1212
  const mac = isMacShortcutPlatform();
1213
- if (kind === "repl-send") return mac ? "⇧⌘↵" : "Ctrl+Shift+Enter";
1213
+ if (kind === "repl-send") return mac ? "⌃⇧↵" : "Ctrl+Shift+Enter";
1214
1214
  if (kind === "run") return mac ? "⌘↵" : "Ctrl+Enter";
1215
1215
  return "";
1216
1216
  }
1217
1217
 
1218
+ function getStudioShortcutDescription(kind) {
1219
+ if (kind === "repl-send") {
1220
+ return isMacShortcutPlatform()
1221
+ ? "Ctrl+Shift+Enter (Cmd+Shift+Enter also works where available)"
1222
+ : "Ctrl+Shift+Enter";
1223
+ }
1224
+ if (kind === "run") return "Cmd/Ctrl+Enter";
1225
+ return "";
1226
+ }
1227
+
1218
1228
  function withStudioShortcutLabel(label, kind) {
1219
1229
  const shortcut = getStudioShortcutLabel(kind);
1220
1230
  return shortcut ? (label + " " + shortcut) : label;
@@ -19188,6 +19198,7 @@
19188
19198
  const canQueueSteering = studioRunChainActive && !critiqueIsStop;
19189
19199
  const hasReplSession = Boolean(getActiveReplSessionForCurrentRuntime());
19190
19200
  const showReplSend = rightView === "repl";
19201
+ const replSendShortcut = getStudioShortcutDescription("repl-send");
19191
19202
 
19192
19203
  if (isEditorOnlyMode) {
19193
19204
  if (sendRunBtn) {
@@ -19209,8 +19220,8 @@
19209
19220
  sendReplBtn.textContent = showReplSend ? withStudioShortcutLabel(replSendMode === "literate" ? "Send selection/chunks" : "Send to REPL", "repl-send") : "Send to REPL";
19210
19221
  sendReplBtn.title = hasReplSession
19211
19222
  ? (replSendMode === "literate"
19212
- ? "Literate send: selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk. Shortcut: Cmd/Ctrl+Shift+Enter."
19213
- : "Raw send: selection, or full editor if no selection. Shortcut: Cmd/Ctrl+Shift+Enter.")
19223
+ ? "Literate send: selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk. Shortcut: " + replSendShortcut + "."
19224
+ : "Raw send: selection, or full editor if no selection. Shortcut: " + replSendShortcut + ".")
19214
19225
  : "Start or select a REPL session in the right pane first.";
19215
19226
  const replActionLine = sendReplBtn.closest(".repl-action-line");
19216
19227
  if (replActionLine instanceof HTMLElement) replActionLine.hidden = !showReplSend;
@@ -19270,8 +19281,8 @@
19270
19281
  sendReplBtn.textContent = showReplSend ? withStudioShortcutLabel(replSendMode === "literate" ? "Send selection/chunks" : "Send to REPL", "repl-send") : "Send to REPL";
19271
19282
  sendReplBtn.title = hasReplSession
19272
19283
  ? (replSendMode === "literate"
19273
- ? "Literate send: selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk. Shortcut: Cmd/Ctrl+Shift+Enter."
19274
- : "Raw send: selection, or full editor if no selection. Shortcut: Cmd/Ctrl+Shift+Enter.")
19284
+ ? "Literate send: selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk. Shortcut: " + replSendShortcut + "."
19285
+ : "Raw send: selection, or full editor if no selection. Shortcut: " + replSendShortcut + ".")
19275
19286
  : "Start or select a REPL session in the right pane first.";
19276
19287
  const replActionLine = sendReplBtn.closest(".repl-action-line");
19277
19288
  if (replActionLine instanceof HTMLElement) replActionLine.hidden = !showReplSend;
package/index.ts CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  } from "./shared/studio-markdown-latex-literals.js";
30
30
  import { escapeStudioPdfLatexTextFragment } from "./shared/studio-pdf-escape.js";
31
31
  import { resolveStudioPdfResourceFile } from "./shared/studio-pdf-resource.js";
32
+ import { buildStudioReplTmuxStartArgs } from "./shared/studio-repl-tmux.js";
32
33
  import { buildStudioForwardingHint, buildStudioSshTunnelHint, isStudioSshSession as isSshSession } from "./shared/studio-ssh-hint.js";
33
34
  import { renderStudioAnnotationInlineHtml } from "./shared/studio-annotation-render.js";
34
35
  import {
@@ -9626,7 +9627,7 @@ function startStudioReplSession(runtime: StudioReplRuntime, cwd: string, options
9626
9627
  };
9627
9628
  }
9628
9629
  const command = getStudioReplRuntimeCommand(runtime, commandOverride);
9629
- const result = runStudioTmux(["new-session", "-d", "-s", sessionName, "-c", cwd || process.cwd(), command], { timeout: 5_000 });
9630
+ const result = runStudioTmux(buildStudioReplTmuxStartArgs(sessionName, cwd || process.cwd(), command), { timeout: 5_000 });
9630
9631
  if (!result.ok) return { ok: false, message: result.message || `Failed to start ${STUDIO_REPL_RUNTIME_LABELS[runtime]} REPL.` };
9631
9632
  return {
9632
9633
  ok: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-studio",
3
- "version": "0.9.39",
3
+ "version": "0.9.40",
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",
@@ -0,0 +1,28 @@
1
+ const STUDIO_REPL_PROMPT_COMMAND_ENVIRONMENT = "PROMPT_COMMAND=";
2
+
3
+ /**
4
+ * Build the tmux arguments for a Studio-owned REPL session.
5
+ *
6
+ * PROMPT_COMMAND contains shell-local function names on some terminals (notably
7
+ * cmux/Ghostty with macOS Bash 3.2). The variable may be exported to Pi while
8
+ * the corresponding functions cannot be inherited by a child shell, so start
9
+ * each detached REPL with a clean prompt-command environment.
10
+ *
11
+ * @param {string} sessionName
12
+ * @param {string} cwd
13
+ * @param {string} command
14
+ * @returns {string[]}
15
+ */
16
+ export function buildStudioReplTmuxStartArgs(sessionName, cwd, command) {
17
+ return [
18
+ "new-session",
19
+ "-d",
20
+ "-s",
21
+ sessionName,
22
+ "-c",
23
+ cwd,
24
+ "-e",
25
+ STUDIO_REPL_PROMPT_COMMAND_ENVIRONMENT,
26
+ command,
27
+ ];
28
+ }