pi-studio 0.9.56 → 0.9.57
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 +16 -0
- package/README.md +17 -3
- package/ROADMAP.md +16 -1
- package/client/studio-client.js +65 -7
- package/index.ts +235 -112
- package/package.json +1 -1
- package/shared/REPL_SESSION_RECORD_PROTOCOL.md +24 -1
- package/shared/repl-control-files.js +158 -0
- package/shared/repl-submission-display.js +227 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.9.57] — 2026-09-03
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Add deterministic raw-pane submission displays and alignment anchors for Studio browser and `studio_repl_send` submissions across Shell, Python/IPython, Julia, R, GHCi, and Clojure.
|
|
11
|
+
- Add a browser-stored **Pane echo** selector with privacy-conscious **Off** default, adaptive **Summary**, and bounded **Full** modes, plus per-tool `echoMode` and `PI_STUDIO_REPL_ECHO_MODE` support.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Keep ordinary panes quiet by default; opt-in Summary now shows short submissions in full, truncates longer ones after 6 lines or 600 source characters, and uses compact begin/completion anchors with a plain unanchored output divider instead of three metadata-heavy marker lines.
|
|
15
|
+
- Strip each request-specific header, source preview, divider, and footer from captured tool output and the Shared REPL Record while retaining the optional display in raw tmux history for human readability and future transcript alignment.
|
|
16
|
+
- Replace long per-request Studio loader paths with compact collision-resistant files under a private per-user `/tmp/pi-rc-<user-key>` root. Source files use mode `0600`; completed files are removed immediately, timeout/abort files remain only until the submission settles, and crash leftovers older than 24 hours are pruned on a later send.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Send GHCi completion signalling as a separate command so a submitted Haskell runtime error cannot prevent completion capture or leave the cross-client lease waiting until timeout.
|
|
20
|
+
- Persist a final clean-record error through the previously validated exact session identity when a Studio submission's tmux lifetime disappears before output capture, instead of leaving its sidecar entry stuck in `sending`.
|
|
21
|
+
- Strip the new compact loader path from browser-side optimistic capture as well as server capture, and stop later raw transcript refreshes from overwriting an authoritative finalized clean-record entry.
|
|
22
|
+
|
|
7
23
|
## [0.9.56] — 2026-09-02
|
|
8
24
|
|
|
9
25
|
### Added
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ _The video shows an earlier version of the Studio interface. The basic workflow
|
|
|
39
39
|
- Includes a right-pane **Changes** view for browsing the current git diff by file, previewing per-file diffs, opening changed files, loading the full diff into the editor, and copying the diff
|
|
40
40
|
- Includes a right-pane **Files** view rooted in folders allowed for the current Studio session, with an **Allow folder…** action and location selector; exact-file grants can still be opened independently without exposing their parent folders. Files supports sorting by name/modified time/size, opening folders or the current root in Finder/the system file manager, loading text/code/CSV/TSV documents into the editor, opening a read-only **Preview (follow)** tab whose disk content is authoritative, previewing PDFs/images, opening previews in new Studio tabs, converting DOCX/ODT documents to editable Markdown when Pandoc is available after confirmation, copying paths, setting the current folder as the Studio working directory, and revealing files in the file manager
|
|
41
41
|
- Imports detached text-file copies either through the browser's file picker or from a path on the computer running Pi, keeping the path option available in embedded and remote browser views; imported copies remain detached until saved or opened as file-backed documents
|
|
42
|
-
- Includes an optional tmux-backed **REPL** view for Shell, Python, IPython, Julia, R, GHCi, and Clojure sessions, with Raw/Literate send modes, `Cmd/Ctrl+Shift+Enter` **Send to REPL**, session start/stop/interrupt controls, a compact refresh-persistent **Shared REPL Record** of compatible-client submissions, a secondary raw tmux mirror, agent-facing `studio_repl_status` / `studio_repl_send` tools, and Markdown/PDF/HTML export
|
|
42
|
+
- Includes an optional tmux-backed **REPL** view for Shell, Python, IPython, Julia, R, GHCi, and Clojure sessions, with Raw/Literate send modes, `Cmd/Ctrl+Shift+Enter` **Send to REPL**, session start/stop/interrupt controls, a privacy-conscious Off/Summary/Full pane-echo selector, a compact refresh-persistent **Shared REPL Record** of compatible-client submissions, a secondary raw tmux mirror, agent-facing `studio_repl_status` / `studio_repl_send` tools, and Markdown/PDF/HTML export
|
|
43
43
|
- Includes a local persistent scratchpad for quick notes you want to keep out of the main editor until you're ready to copy or insert them, with a **Recent…** picker for recovering scratchpads saved under earlier file/draft identities
|
|
44
44
|
- Includes a docked **Outline** rail for navigating document structure in the current editor text, with clickable entries that jump in the raw editor and reveal matching preview locations when available
|
|
45
45
|
- Restores each browser tab’s editor workspace after refresh or cmux hidden-surface reconstruction, and provides an explicit **Reset editor** action when you want to discard the restored draft and return the tab to a fresh blank draft without changing responses or saved files
|
|
@@ -92,7 +92,7 @@ _The video shows an earlier version of the Studio interface. The basic workflow
|
|
|
92
92
|
| `studio_export_pdf` | Export direct Markdown/LaTeX, a local file, or the last model response to PDF. Defaults to writing a file without opening a viewer. |
|
|
93
93
|
| `studio_export_html` | Export direct Markdown/LaTeX, a local file, or the last model response to standalone HTML. Defaults to writing a file without opening a viewer. |
|
|
94
94
|
| `studio_repl_status` | Inspect tmux-backed REPL sessions, including shared clean-record identity, compatible-client entries, and raw-mirror details. |
|
|
95
|
-
| `studio_repl_send` | Send code to a visible REPL under the cross-client send lease and add its captured output to the shared clean record. |
|
|
95
|
+
| `studio_repl_send` | Send code to a visible REPL under the cross-client send lease, optionally choose its raw-pane `echoMode`, and add its captured output to the shared clean record. |
|
|
96
96
|
|
|
97
97
|
## Install
|
|
98
98
|
|
|
@@ -118,6 +118,20 @@ Compatible clients publish a versioned opaque record ID in tmux and keep the bou
|
|
|
118
118
|
|
|
119
119
|
The clean record covers only submissions and notes whose boundaries a compatible client knows. Text typed directly into an attached tmux pane remains in the secondary raw pane/history mirror and is not presented as reliably parsed code/output. **Export record** produces a canonical Markdown representation with origin, mode, status, runtime, timestamp, and this limitation; PDF and HTML exports are derived from that Markdown.
|
|
120
120
|
|
|
121
|
+
### Submission display and alignment anchors
|
|
122
|
+
|
|
123
|
+
Optional pane echo places submitted code after a compact begin anchor, followed by a plain `── output ──` divider and a completion anchor. The anchors contain a stable 12-character hash derived from the Shared REPL Record entry ID, so compatible clients can align known sends in future derived transcripts without exposing the entry ID itself. Studio strips the exact header, source preview, divider, and footer from captured tool output and the clean record; they remain visible only in the raw pane/history.
|
|
124
|
+
|
|
125
|
+
The REPL footer selector offers:
|
|
126
|
+
|
|
127
|
+
- **Off** (default): no optional submission display or anchors; the runtime's unavoidable temporary-file control command can still be echoed by the REPL.
|
|
128
|
+
- **Summary**: short submissions in full, truncating after 6 lines or 600 source characters, with compact begin/end anchors and a plain output divider.
|
|
129
|
+
- **Full**: up to 40 lines or 4,000 source characters with the same anchors and divider. Full mode writes source code into persistent raw terminal history, so use it only when that is acceptable.
|
|
130
|
+
|
|
131
|
+
The browser choice is stored locally for that Studio origin. `studio_repl_send` accepts the same per-call `echoMode`; `PI_STUDIO_REPL_ECHO_MODE=off|summary|full` changes the tool-send startup default from Off. Display text escapes terminal, line-separator, and bidirectional control characters. The anchors improve raw-history alignment but do not make direct pane activity without instrumentation authoritative or alter protocol-v1 entries.
|
|
132
|
+
|
|
133
|
+
Runtime wrappers use compact request-unique paths such as `/tmp/pi-rc-<user-key>/<token>.py` rather than a long Studio/session directory tree. The per-user root is current-user-owned mode `0700`, source files are mode `0600`, and files are removed after capture or by the timeout/abort watcher once execution settles. This keeps the unavoidable echoed loader command short without reverting to a collision-prone shared filename.
|
|
134
|
+
|
|
121
135
|
See [`shared/REPL_SESSION_RECORD_PROTOCOL.md`](./shared/REPL_SESSION_RECORD_PROTOCOL.md) for protocol, safety, retention, and compatibility details.
|
|
122
136
|
|
|
123
137
|
## Side questions and research context
|
|
@@ -199,7 +213,7 @@ Studio only passes icon-pack arguments when a diagram actually references `lucid
|
|
|
199
213
|
- Studio is designed as a complement to terminal pi, not a replacement.
|
|
200
214
|
- Installing pi-studio makes the optional `pi-studio-dark` and `pi-studio-light` themes available in pi's theme selector; it does not change your active theme.
|
|
201
215
|
- Editor/code font uses a best-effort terminal-monospace match when the current terminal config exposes it; set `PI_STUDIO_FONT_MONO` to force a specific CSS `font-family` stack. Use `PI_STUDIO_FONT_UI` or `PI_STUDIO_FONT_PROSE` to override the Studio UI or rendered-preview font stacks.
|
|
202
|
-
- The optional REPL view requires `tmux`. Studio can start and stop Studio-owned `pi-studio-repl-*` sessions and can mirror detected `pi-repl-*` sessions, but it will not stop external `pi-repl-*` sessions. Compatible clients synchronize their clean record automatically; direct pane interaction remains available only in the raw mirror. A send timeout or tool abort does not stop submitted runtime code, so Studio keeps that session's cross-client lease until the completion
|
|
216
|
+
- The optional REPL view requires `tmux`. Studio can start and stop Studio-owned `pi-studio-repl-*` sessions and can mirror detected `pi-repl-*` sessions, but it will not stop external `pi-repl-*` sessions. Compatible clients synchronize their clean record automatically; direct pane interaction remains available only in the raw mirror. A send timeout or tool abort does not stop submitted runtime code, so Studio keeps that session's cross-client lease until the runtime completion signal appears or the exact tmux session ends. The visible request-specific completion anchor is separate presentation metadata and stays in raw history when pane echo is enabled.
|
|
203
217
|
- Side-question web search requires `BRAVE_API_KEY`; without it, local and conversation context still work, and the web option is shown as unavailable. Side sub-sessions are deliberately read-only and can run independently while the main Pi agent is busy.
|
|
204
218
|
- Full preview/PDF quality depends on `pandoc` (and `xelatex` for PDF):
|
|
205
219
|
- `brew install pandoc`
|
package/ROADMAP.md
CHANGED
|
@@ -83,6 +83,20 @@ A coordinated interoperability release with `pi-repl` 0.4.0:
|
|
|
83
83
|
|
|
84
84
|
This release does not change Studio's single-buffer architecture or move buffer-first work forward from `0.10.0`.
|
|
85
85
|
|
|
86
|
+
## 0.9.57 — REPL submission display and alignment anchors (shipped 2026-09-03)
|
|
87
|
+
|
|
88
|
+
A coordinated presentation and future-alignment increment with `pi-repl` 0.4.1:
|
|
89
|
+
|
|
90
|
+
- keep optional pane echo Off by default, with an adaptive Summary that shows short submissions in full and a separately bounded Full choice;
|
|
91
|
+
- derive collision-resistant human-readable anchors from immutable clean-record entry IDs without exposing those IDs in the pane;
|
|
92
|
+
- retain compact begin/completion anchors and a plain unanchored output divider in raw tmux history while stripping the exact header, source preview, divider, and footer from captured output and protocol-v1 records;
|
|
93
|
+
- sanitize terminal controls, cap all visible source previews, and warn that Full mode persists bounded source code in raw history;
|
|
94
|
+
- cover Shell, Python/IPython, Julia, R, GHCi, and Clojure consistently across browser and tool sends, including error, timeout, abort, and exact-session-disappearance paths;
|
|
95
|
+
- replace verbose or fixed loader paths in both clients with compact request-unique files in a private per-user control root, retaining files through timed-out/aborted execution and cleaning them when the submission settles;
|
|
96
|
+
- treat markers only as presentation and alignment evidence, never as authority for silently promoting inferred direct activity into the clean record.
|
|
97
|
+
|
|
98
|
+
A separate Derived REPL Transcript can use these anchors later, but it remains outside protocol v1 and outside this release.
|
|
99
|
+
|
|
86
100
|
## 0.10.0 — Buffer-first editing
|
|
87
101
|
|
|
88
102
|
The first architectural `0.10` release should add:
|
|
@@ -107,4 +121,5 @@ Cross-environment checks should cover, where relevant:
|
|
|
107
121
|
- paths with spaces, Unicode, traversal attempts, and symlinks;
|
|
108
122
|
- supported and unavailable browser APIs;
|
|
109
123
|
- browser reconstruction, refresh recovery, and clean shutdown;
|
|
110
|
-
- for shared REPL work: bidirectional visibility, compatible-client send contention, output attribution, restart persistence, clear/import behavior, stale or malformed records, and direct attached-pane activity
|
|
124
|
+
- for shared REPL work: bidirectional visibility, compatible-client send contention, output attribution, restart persistence, clear/import behavior, stale or malformed records, and direct attached-pane activity;
|
|
125
|
+
- for REPL submission displays: Summary/Off/Full bounds, raw-versus-clean separation, stable cross-client anchors, compact private collision-resistant control files and cleanup, every supported runtime, runtime errors, timeout/abort lease retention, and exact-session disappearance.
|
package/client/studio-client.js
CHANGED
|
@@ -132,6 +132,7 @@
|
|
|
132
132
|
const queueSteerBtn = document.getElementById("queueSteerBtn");
|
|
133
133
|
const sendReplBtn = document.getElementById("sendReplBtn");
|
|
134
134
|
const replSendModeSelect = document.getElementById("replSendModeSelect");
|
|
135
|
+
const replEchoModeSelect = document.getElementById("replEchoModeSelect");
|
|
135
136
|
const copyDraftBtn = document.getElementById("copyDraftBtn");
|
|
136
137
|
const suggestCompletionBtn = document.getElementById("suggestCompletionBtn");
|
|
137
138
|
const suggestCompletionOptionsBtn = document.getElementById("suggestCompletionOptionsBtn");
|
|
@@ -595,6 +596,14 @@
|
|
|
595
596
|
return "raw";
|
|
596
597
|
}
|
|
597
598
|
})();
|
|
599
|
+
let replEchoMode = (() => {
|
|
600
|
+
try {
|
|
601
|
+
const stored = String((window.localStorage && window.localStorage.getItem("piStudio.replEchoMode.v2")) || "").trim().toLowerCase();
|
|
602
|
+
return stored === "summary" || stored === "full" ? stored : "off";
|
|
603
|
+
} catch {
|
|
604
|
+
return "off";
|
|
605
|
+
}
|
|
606
|
+
})();
|
|
598
607
|
function normalizeReplJournalEntry(entry) {
|
|
599
608
|
if (!entry || typeof entry !== "object") return null;
|
|
600
609
|
const hasCompatibleOrigin = entry.origin === "pi-repl" || entry.origin === "pi-studio";
|
|
@@ -1353,6 +1362,11 @@
|
|
|
1353
1362
|
return String(value || "").trim().toLowerCase() === "literate" ? "literate" : "raw";
|
|
1354
1363
|
}
|
|
1355
1364
|
|
|
1365
|
+
function normalizeReplEchoMode(value) {
|
|
1366
|
+
const normalized = String(value || "").trim().toLowerCase();
|
|
1367
|
+
return normalized === "summary" || normalized === "full" ? normalized : "off";
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1356
1370
|
function isMacShortcutPlatform() {
|
|
1357
1371
|
try {
|
|
1358
1372
|
const platform = String((navigator && navigator.platform) || "");
|
|
@@ -1394,6 +1408,28 @@
|
|
|
1394
1408
|
}
|
|
1395
1409
|
}
|
|
1396
1410
|
|
|
1411
|
+
function setReplEchoMode(mode) {
|
|
1412
|
+
replEchoMode = normalizeReplEchoMode(mode);
|
|
1413
|
+
if (replEchoModeSelect) replEchoModeSelect.value = replEchoMode;
|
|
1414
|
+
try {
|
|
1415
|
+
if (window.localStorage) window.localStorage.setItem("piStudio.replEchoMode.v2", replEchoMode);
|
|
1416
|
+
} catch {
|
|
1417
|
+
// Ignore storage failures.
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
function syncReplEchoModeSelect(visible) {
|
|
1422
|
+
if (!replEchoModeSelect) return;
|
|
1423
|
+
replEchoModeSelect.hidden = !visible;
|
|
1424
|
+
replEchoModeSelect.disabled = !visible || wsState === "Disconnected" || uiBusy || replBusy;
|
|
1425
|
+
replEchoModeSelect.value = replEchoMode;
|
|
1426
|
+
replEchoModeSelect.title = replEchoMode === "off"
|
|
1427
|
+
? "Do not add a submitted-code display or alignment anchors to the raw REPL pane."
|
|
1428
|
+
: (replEchoMode === "full"
|
|
1429
|
+
? "Display up to 40 lines or 4,000 characters of submitted code; source remains in raw terminal history."
|
|
1430
|
+
: "Display short submissions in full, truncating after 6 lines or 600 characters, with compact anchors and a plain output divider.");
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1397
1433
|
function setReplJournalCollapsed(collapsed) {
|
|
1398
1434
|
replJournalCollapsed = Boolean(collapsed);
|
|
1399
1435
|
try {
|
|
@@ -1983,14 +2019,15 @@
|
|
|
1983
2019
|
function stripStudioReplSubmissionEcho(delta) {
|
|
1984
2020
|
let value = String(delta || "").replace(/^\s+/, "");
|
|
1985
2021
|
// The raw mirror below remains raw; Studio record cards hide only the
|
|
1986
|
-
// temp-file wrapper used to submit multiline snippets safely.
|
|
1987
|
-
//
|
|
2022
|
+
// temp-file wrapper used to submit multiline snippets safely. Match
|
|
2023
|
+
// both legacy long Studio roots and compact private control roots.
|
|
1988
2024
|
const submissionEchoPatterns = [
|
|
1989
|
-
/^.*exec\(open\([\s\S]*?pi-studio-re[\s\S]*?globals\(\)\)\s*$/gm,
|
|
1990
|
-
/^.*include\([\s\S]*?pi-studio-re[\s\S]*?\.jl"\)\s*$/gm,
|
|
1991
|
-
/^.*source\([\s\S]*?pi-studio-re[\s\S]*?local\s*=\s*\.GlobalEnv\)\s*$/gm,
|
|
1992
|
-
/^.*:script\s+[\s\S]*?pi-studio-re[\s\S]*?\.ghci"?\s*$/gm,
|
|
1993
|
-
/^.*\(do\s+\(load-file\s+[\s\S]*?pi-studio-re[\s\S]*?:pi-studio\/silent\)\s*$/gm,
|
|
2025
|
+
/^.*exec\(open\([\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?globals\(\)\)\s*$/gm,
|
|
2026
|
+
/^.*include\([\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?\.jl"\)\s*$/gm,
|
|
2027
|
+
/^.*source\([\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?local\s*=\s*\.GlobalEnv\)\s*$/gm,
|
|
2028
|
+
/^.*:script\s+[\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?\.ghci"?\s*$/gm,
|
|
2029
|
+
/^.*\(do\s+\(load-file\s+[\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?:pi-studio\/silent\)\s*$/gm,
|
|
2030
|
+
/^.*\.\s+[\s\S]*?(?:pi-studio-re|pi-rc-)[\s\S]*?\.sh[\s\S]*?(?:done\.flag|[a-f0-9]{16}\.done).*$/gm,
|
|
1994
2031
|
];
|
|
1995
2032
|
for (const pattern of submissionEchoPatterns) {
|
|
1996
2033
|
value = value.replace(pattern, "");
|
|
@@ -2026,6 +2063,10 @@
|
|
|
2026
2063
|
if (entryIndex < 0) return false;
|
|
2027
2064
|
const entry = replJournalEntries[entryIndex];
|
|
2028
2065
|
if (entry.sessionName && sessionName && entry.sessionName !== sessionName) return false;
|
|
2066
|
+
if (!entry.legacyLocal && (entry.status === "captured" || entry.status === "timeout" || entry.status === "error")) {
|
|
2067
|
+
activeReplJournalEntryId = "";
|
|
2068
|
+
return false;
|
|
2069
|
+
}
|
|
2029
2070
|
const delta = cleanReplCapturedOutput(extractReplTranscriptDelta(entry.beforeTranscript, transcript), entry);
|
|
2030
2071
|
if (!delta.trim()) return false;
|
|
2031
2072
|
if (entry.output === delta && entry.status === "captured") return false;
|
|
@@ -2231,6 +2272,7 @@
|
|
|
2231
2272
|
requestId,
|
|
2232
2273
|
sessionName: session.sessionName,
|
|
2233
2274
|
text,
|
|
2275
|
+
echoMode: replEchoMode,
|
|
2234
2276
|
journalEntryId: journalEntry.id,
|
|
2235
2277
|
createdAt: journalEntry.createdAt,
|
|
2236
2278
|
label: journalEntry.label,
|
|
@@ -22342,6 +22384,7 @@
|
|
|
22342
22384
|
? "Literate send: Send to REPL uses the selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk."
|
|
22343
22385
|
: "Raw send: Send to REPL uses the selection, or full editor if no selection.";
|
|
22344
22386
|
}
|
|
22387
|
+
syncReplEchoModeSelect(showReplSend);
|
|
22345
22388
|
if (critiqueBtn) {
|
|
22346
22389
|
critiqueBtn.textContent = "Critique text";
|
|
22347
22390
|
critiqueBtn.classList.remove("request-stop-active");
|
|
@@ -22404,6 +22447,7 @@
|
|
|
22404
22447
|
? "Literate send: Send to REPL uses the selection, current fenced code chunk, or all matching chunks if the cursor is outside a chunk."
|
|
22405
22448
|
: "Raw send: Send to REPL uses the selection, or full editor if no selection.";
|
|
22406
22449
|
}
|
|
22450
|
+
syncReplEchoModeSelect(rightView === "repl");
|
|
22407
22451
|
|
|
22408
22452
|
if (critiqueBtn) {
|
|
22409
22453
|
critiqueBtn.textContent = critiqueIsStop ? "Stop" : "Critique text";
|
|
@@ -24960,6 +25004,19 @@
|
|
|
24960
25004
|
});
|
|
24961
25005
|
}
|
|
24962
25006
|
|
|
25007
|
+
if (replEchoModeSelect) {
|
|
25008
|
+
replEchoModeSelect.addEventListener("change", () => {
|
|
25009
|
+
setReplEchoMode(replEchoModeSelect.value);
|
|
25010
|
+
syncActionButtons();
|
|
25011
|
+
setStatus(replEchoMode === "full"
|
|
25012
|
+
? "Full pane echo enabled. Bounded submitted source code will remain in raw terminal history."
|
|
25013
|
+
: (replEchoMode === "off"
|
|
25014
|
+
? "Pane echo disabled; new sends will not add alignment anchors to raw terminal history."
|
|
25015
|
+
: "Summary pane echo enabled; short submissions will be shown with compact anchors and a plain output divider."),
|
|
25016
|
+
replEchoMode === "full" ? "warning" : "success");
|
|
25017
|
+
});
|
|
25018
|
+
}
|
|
25019
|
+
|
|
24963
25020
|
copyDraftBtn.addEventListener("click", async () => {
|
|
24964
25021
|
const content = sourceTextEl.value;
|
|
24965
25022
|
if (!content.trim()) {
|
|
@@ -25580,6 +25637,7 @@
|
|
|
25580
25637
|
const initialAnnotationsEnabled = storedAnnotationsEnabled ?? Boolean(annotationModeSelect ? annotationModeSelect.value !== "off" : true);
|
|
25581
25638
|
setAnnotationsEnabled(initialAnnotationsEnabled, { silent: true });
|
|
25582
25639
|
setReplSendMode(replSendMode);
|
|
25640
|
+
setReplEchoMode(replEchoMode);
|
|
25583
25641
|
|
|
25584
25642
|
const sessionWorkspaceState = readPersistedWorkspaceState();
|
|
25585
25643
|
const serverWorkspaceRecovery = await readServerWorkspaceRecoveryState();
|