pi-studio 0.5.36 → 0.5.37
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 +8 -0
- package/README.md +1 -0
- package/client/studio-client.js +222 -7
- package/client/studio.css +125 -0
- package/index.ts +23 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ All notable changes to `pi-studio` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.5.37] — 2026-03-29
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- Studio now includes a local persistent scratchpad for parking quick thoughts while you work. The scratchpad opens as an integrated modal, keeps its contents after closing, and provides copy / clear / insert-into-editor actions.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Scratchpad UI text and actions now make the persistence semantics explicit: closing keeps the current notes unless you actively clear them.
|
|
14
|
+
|
|
7
15
|
## [0.5.36] — 2026-03-28
|
|
8
16
|
|
|
9
17
|
### Changed
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ Extension for [pi](https://pi.dev) that opens a local two-pane browser workspace
|
|
|
16
16
|
|
|
17
17
|
- Opens a two-pane browser workspace: **Editor** (left) + **Response/Thinking/Editor Preview** (right)
|
|
18
18
|
- Runs editor text directly, or asks for structured critique (auto/writing/code focus)
|
|
19
|
+
- 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
|
|
19
20
|
- Browses response history (`Prev/Next/Last`) and loads either:
|
|
20
21
|
- response text
|
|
21
22
|
- critique notes/full critique
|
package/client/studio-client.js
CHANGED
|
@@ -88,6 +88,16 @@
|
|
|
88
88
|
const compactBtn = document.getElementById("compactBtn");
|
|
89
89
|
const leftFocusBtn = document.getElementById("leftFocusBtn");
|
|
90
90
|
const rightFocusBtn = document.getElementById("rightFocusBtn");
|
|
91
|
+
const scratchpadBtn = document.getElementById("scratchpadBtn");
|
|
92
|
+
const scratchpadOverlayEl = document.getElementById("scratchpadOverlay");
|
|
93
|
+
const scratchpadDialogEl = document.getElementById("scratchpadDialog");
|
|
94
|
+
const scratchpadTextEl = document.getElementById("scratchpadText");
|
|
95
|
+
const scratchpadMetaEl = document.getElementById("scratchpadMeta");
|
|
96
|
+
const scratchpadInsertBtn = document.getElementById("scratchpadInsertBtn");
|
|
97
|
+
const scratchpadCopyBtn = document.getElementById("scratchpadCopyBtn");
|
|
98
|
+
const scratchpadClearBtn = document.getElementById("scratchpadClearBtn");
|
|
99
|
+
const scratchpadCloseBtn = document.getElementById("scratchpadCloseBtn");
|
|
100
|
+
const scratchpadDoneBtn = document.getElementById("scratchpadDoneBtn");
|
|
91
101
|
|
|
92
102
|
const initialSourceState = {
|
|
93
103
|
source: (document.body && document.body.dataset && document.body.dataset.initialSource) || "blank",
|
|
@@ -227,6 +237,7 @@
|
|
|
227
237
|
const RESPONSE_HIGHLIGHT_MAX_CHARS = 120_000;
|
|
228
238
|
const RESPONSE_HIGHLIGHT_STORAGE_KEY = "piStudio.responseHighlightEnabled";
|
|
229
239
|
const ANNOTATION_MODE_STORAGE_KEY = "piStudio.annotationsEnabled";
|
|
240
|
+
const SCRATCHPAD_STORAGE_KEY = "piStudio.scratchpad";
|
|
230
241
|
const PREVIEW_INPUT_DEBOUNCE_MS = 0;
|
|
231
242
|
const PREVIEW_PENDING_BADGE_DELAY_MS = 220;
|
|
232
243
|
const previewPendingTimers = new WeakMap();
|
|
@@ -241,6 +252,8 @@
|
|
|
241
252
|
let responseHighlightEnabled = false;
|
|
242
253
|
let editorHighlightRenderRaf = null;
|
|
243
254
|
let annotationsEnabled = true;
|
|
255
|
+
let scratchpadText = "";
|
|
256
|
+
let scratchpadReturnFocusEl = null;
|
|
244
257
|
const PREVIEW_ANNOTATION_PLACEHOLDER_PREFIX = "PISTUDIOANNOT";
|
|
245
258
|
const annotationHelpers = globalThis.PiStudioAnnotationHelpers;
|
|
246
259
|
if (!annotationHelpers || typeof annotationHelpers.collectInlineAnnotationMarkers !== "function") {
|
|
@@ -864,6 +877,28 @@
|
|
|
864
877
|
if (!event || event.defaultPrevented) return;
|
|
865
878
|
|
|
866
879
|
const key = typeof event.key === "string" ? event.key : "";
|
|
880
|
+
const plainEscape = key === "Escape"
|
|
881
|
+
&& !event.metaKey
|
|
882
|
+
&& !event.ctrlKey
|
|
883
|
+
&& !event.altKey
|
|
884
|
+
&& !event.shiftKey;
|
|
885
|
+
const scratchpadOwnsEvent = Boolean(
|
|
886
|
+
scratchpadDialogEl
|
|
887
|
+
&& event.target
|
|
888
|
+
&& typeof scratchpadDialogEl.contains === "function"
|
|
889
|
+
&& scratchpadDialogEl.contains(event.target)
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
if (isScratchpadOpen() && plainEscape) {
|
|
893
|
+
event.preventDefault();
|
|
894
|
+
closeScratchpad();
|
|
895
|
+
return;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
if (scratchpadOwnsEvent) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
|
|
867
902
|
const isToggleShortcut =
|
|
868
903
|
(key === "Escape" && (event.metaKey || event.ctrlKey))
|
|
869
904
|
|| key === "F10";
|
|
@@ -874,13 +909,7 @@
|
|
|
874
909
|
return;
|
|
875
910
|
}
|
|
876
911
|
|
|
877
|
-
if (
|
|
878
|
-
key === "Escape"
|
|
879
|
-
&& !event.metaKey
|
|
880
|
-
&& !event.ctrlKey
|
|
881
|
-
&& !event.altKey
|
|
882
|
-
&& !event.shiftKey
|
|
883
|
-
) {
|
|
912
|
+
if (plainEscape) {
|
|
884
913
|
const activeKind = getAbortablePendingKind();
|
|
885
914
|
if (activeKind === "direct" || activeKind === "critique") {
|
|
886
915
|
event.preventDefault();
|
|
@@ -3209,6 +3238,126 @@
|
|
|
3209
3238
|
persistStoredToggle(ANNOTATION_MODE_STORAGE_KEY, enabled);
|
|
3210
3239
|
}
|
|
3211
3240
|
|
|
3241
|
+
function readStoredText(storageKey) {
|
|
3242
|
+
if (!window.localStorage) return null;
|
|
3243
|
+
try {
|
|
3244
|
+
const value = window.localStorage.getItem(storageKey);
|
|
3245
|
+
return typeof value === "string" ? value : null;
|
|
3246
|
+
} catch {
|
|
3247
|
+
return null;
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
function persistStoredText(storageKey, value) {
|
|
3252
|
+
if (!window.localStorage) return;
|
|
3253
|
+
try {
|
|
3254
|
+
window.localStorage.setItem(storageKey, String(value ?? ""));
|
|
3255
|
+
} catch {
|
|
3256
|
+
// ignore storage failures
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
|
|
3260
|
+
function isScratchpadOpen() {
|
|
3261
|
+
return Boolean(scratchpadOverlayEl && !scratchpadOverlayEl.hidden);
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
function readStoredScratchpadText() {
|
|
3265
|
+
return readStoredText(SCRATCHPAD_STORAGE_KEY);
|
|
3266
|
+
}
|
|
3267
|
+
|
|
3268
|
+
function persistScratchpadText(value) {
|
|
3269
|
+
persistStoredText(SCRATCHPAD_STORAGE_KEY, value);
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
function updateScratchpadUi() {
|
|
3273
|
+
const normalized = String(scratchpadText || "");
|
|
3274
|
+
const hasContent = Boolean(normalized.trim());
|
|
3275
|
+
if (scratchpadBtn) {
|
|
3276
|
+
scratchpadBtn.textContent = hasContent ? "Scratchpad •" : "Scratchpad";
|
|
3277
|
+
scratchpadBtn.classList.toggle("has-content", hasContent);
|
|
3278
|
+
scratchpadBtn.title = hasContent
|
|
3279
|
+
? "Open your local persistent scratchpad. Current notes persist after closing until you edit or clear them."
|
|
3280
|
+
: "Open a local persistent scratchpad for quick notes. Anything you type will persist after closing until you edit or clear it.";
|
|
3281
|
+
}
|
|
3282
|
+
if (scratchpadMetaEl) {
|
|
3283
|
+
scratchpadMetaEl.textContent = hasContent
|
|
3284
|
+
? "Saved locally · persists after close · " + normalized.length + " chars"
|
|
3285
|
+
: "Empty · local only";
|
|
3286
|
+
}
|
|
3287
|
+
if (scratchpadInsertBtn) scratchpadInsertBtn.disabled = !hasContent;
|
|
3288
|
+
if (scratchpadCopyBtn) scratchpadCopyBtn.disabled = !hasContent;
|
|
3289
|
+
if (scratchpadClearBtn) scratchpadClearBtn.disabled = !normalized.length;
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
function setScratchpadText(nextText, options) {
|
|
3293
|
+
scratchpadText = String(nextText || "");
|
|
3294
|
+
if (scratchpadTextEl && scratchpadTextEl.value !== scratchpadText) {
|
|
3295
|
+
scratchpadTextEl.value = scratchpadText;
|
|
3296
|
+
}
|
|
3297
|
+
if (!options || options.persist !== false) {
|
|
3298
|
+
persistScratchpadText(scratchpadText);
|
|
3299
|
+
}
|
|
3300
|
+
updateScratchpadUi();
|
|
3301
|
+
}
|
|
3302
|
+
|
|
3303
|
+
function closeScratchpad(options) {
|
|
3304
|
+
if (!scratchpadOverlayEl || scratchpadOverlayEl.hidden) return;
|
|
3305
|
+
scratchpadOverlayEl.hidden = true;
|
|
3306
|
+
document.body.classList.remove("scratchpad-open");
|
|
3307
|
+
const focusTarget = options && Object.prototype.hasOwnProperty.call(options, "focusTarget")
|
|
3308
|
+
? options.focusTarget
|
|
3309
|
+
: (scratchpadReturnFocusEl || scratchpadBtn || sourceTextEl);
|
|
3310
|
+
scratchpadReturnFocusEl = null;
|
|
3311
|
+
if (focusTarget && typeof focusTarget.focus === "function") {
|
|
3312
|
+
const schedule = typeof window.requestAnimationFrame === "function"
|
|
3313
|
+
? window.requestAnimationFrame.bind(window)
|
|
3314
|
+
: (cb) => window.setTimeout(cb, 16);
|
|
3315
|
+
schedule(() => focusTarget.focus());
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
|
|
3319
|
+
function openScratchpad() {
|
|
3320
|
+
if (!scratchpadOverlayEl) return;
|
|
3321
|
+
scratchpadReturnFocusEl = document.activeElement && document.activeElement !== document.body
|
|
3322
|
+
? document.activeElement
|
|
3323
|
+
: sourceTextEl;
|
|
3324
|
+
scratchpadOverlayEl.hidden = false;
|
|
3325
|
+
document.body.classList.add("scratchpad-open");
|
|
3326
|
+
if (scratchpadTextEl && typeof scratchpadTextEl.focus === "function") {
|
|
3327
|
+
const schedule = typeof window.requestAnimationFrame === "function"
|
|
3328
|
+
? window.requestAnimationFrame.bind(window)
|
|
3329
|
+
: (cb) => window.setTimeout(cb, 16);
|
|
3330
|
+
schedule(() => {
|
|
3331
|
+
scratchpadTextEl.focus();
|
|
3332
|
+
if (typeof scratchpadTextEl.selectionStart === "number") {
|
|
3333
|
+
const end = scratchpadTextEl.value.length;
|
|
3334
|
+
scratchpadTextEl.setSelectionRange(end, end);
|
|
3335
|
+
}
|
|
3336
|
+
});
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
function insertScratchpadIntoEditor() {
|
|
3341
|
+
const content = String(scratchpadText || "");
|
|
3342
|
+
if (!content.trim()) {
|
|
3343
|
+
setStatus("Scratchpad is empty.", "warning");
|
|
3344
|
+
return;
|
|
3345
|
+
}
|
|
3346
|
+
|
|
3347
|
+
const current = sourceTextEl.value || "";
|
|
3348
|
+
const start = typeof sourceTextEl.selectionStart === "number" ? sourceTextEl.selectionStart : current.length;
|
|
3349
|
+
const end = typeof sourceTextEl.selectionEnd === "number" ? sourceTextEl.selectionEnd : start;
|
|
3350
|
+
const safeStart = Math.max(0, Math.min(start, current.length));
|
|
3351
|
+
const safeEnd = Math.max(safeStart, Math.min(end, current.length));
|
|
3352
|
+
const next = current.slice(0, safeStart) + content + current.slice(safeEnd);
|
|
3353
|
+
setEditorText(next, { preserveScroll: false, preserveSelection: false });
|
|
3354
|
+
const caret = safeStart + content.length;
|
|
3355
|
+
sourceTextEl.setSelectionRange(caret, caret);
|
|
3356
|
+
setActivePane("left");
|
|
3357
|
+
closeScratchpad({ focusTarget: sourceTextEl });
|
|
3358
|
+
setStatus("Inserted scratchpad into editor.", "success");
|
|
3359
|
+
}
|
|
3360
|
+
|
|
3212
3361
|
function updateEditorHighlightState() {
|
|
3213
3362
|
const enabled = editorHighlightEnabled && editorView === "markdown";
|
|
3214
3363
|
|
|
@@ -4715,6 +4864,71 @@
|
|
|
4715
4864
|
}
|
|
4716
4865
|
});
|
|
4717
4866
|
|
|
4867
|
+
if (scratchpadBtn) {
|
|
4868
|
+
scratchpadBtn.addEventListener("click", () => {
|
|
4869
|
+
openScratchpad();
|
|
4870
|
+
});
|
|
4871
|
+
}
|
|
4872
|
+
|
|
4873
|
+
if (scratchpadCloseBtn) {
|
|
4874
|
+
scratchpadCloseBtn.addEventListener("click", () => {
|
|
4875
|
+
closeScratchpad();
|
|
4876
|
+
});
|
|
4877
|
+
}
|
|
4878
|
+
|
|
4879
|
+
if (scratchpadDoneBtn) {
|
|
4880
|
+
scratchpadDoneBtn.addEventListener("click", () => {
|
|
4881
|
+
closeScratchpad();
|
|
4882
|
+
});
|
|
4883
|
+
}
|
|
4884
|
+
|
|
4885
|
+
if (scratchpadOverlayEl) {
|
|
4886
|
+
scratchpadOverlayEl.addEventListener("click", (event) => {
|
|
4887
|
+
if (event.target === scratchpadOverlayEl) {
|
|
4888
|
+
closeScratchpad();
|
|
4889
|
+
}
|
|
4890
|
+
});
|
|
4891
|
+
}
|
|
4892
|
+
|
|
4893
|
+
if (scratchpadTextEl) {
|
|
4894
|
+
scratchpadTextEl.addEventListener("input", () => {
|
|
4895
|
+
setScratchpadText(scratchpadTextEl.value);
|
|
4896
|
+
});
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4899
|
+
if (scratchpadInsertBtn) {
|
|
4900
|
+
scratchpadInsertBtn.addEventListener("click", () => {
|
|
4901
|
+
insertScratchpadIntoEditor();
|
|
4902
|
+
});
|
|
4903
|
+
}
|
|
4904
|
+
|
|
4905
|
+
if (scratchpadCopyBtn) {
|
|
4906
|
+
scratchpadCopyBtn.addEventListener("click", async () => {
|
|
4907
|
+
if (!String(scratchpadText || "").trim()) {
|
|
4908
|
+
setStatus("Scratchpad is empty.", "warning");
|
|
4909
|
+
return;
|
|
4910
|
+
}
|
|
4911
|
+
|
|
4912
|
+
try {
|
|
4913
|
+
await navigator.clipboard.writeText(String(scratchpadText || ""));
|
|
4914
|
+
setStatus("Copied scratchpad text.", "success");
|
|
4915
|
+
} catch (error) {
|
|
4916
|
+
setStatus("Clipboard write failed.", "warning");
|
|
4917
|
+
}
|
|
4918
|
+
});
|
|
4919
|
+
}
|
|
4920
|
+
|
|
4921
|
+
if (scratchpadClearBtn) {
|
|
4922
|
+
scratchpadClearBtn.addEventListener("click", () => {
|
|
4923
|
+
if (!String(scratchpadText || "").length) return;
|
|
4924
|
+
const confirmed = window.confirm("Clear scratchpad text?");
|
|
4925
|
+
if (!confirmed) return;
|
|
4926
|
+
setScratchpadText("");
|
|
4927
|
+
if (scratchpadTextEl) scratchpadTextEl.focus();
|
|
4928
|
+
setStatus("Cleared scratchpad.", "success");
|
|
4929
|
+
});
|
|
4930
|
+
}
|
|
4931
|
+
|
|
4718
4932
|
if (saveAnnotatedBtn) {
|
|
4719
4933
|
saveAnnotatedBtn.addEventListener("click", () => {
|
|
4720
4934
|
const content = sourceTextEl.value;
|
|
@@ -4853,6 +5067,7 @@
|
|
|
4853
5067
|
refreshResponseUi();
|
|
4854
5068
|
updateAnnotatedReplyHeaderButton();
|
|
4855
5069
|
setActivePane("left");
|
|
5070
|
+
setScratchpadText(readStoredScratchpadText() || "", { persist: false });
|
|
4856
5071
|
|
|
4857
5072
|
const storedEditorHighlightEnabled = readStoredEditorHighlightEnabled();
|
|
4858
5073
|
const initialHighlightEnabled = storedEditorHighlightEnabled ?? Boolean(highlightSelect && highlightSelect.value === "on");
|
package/client/studio.css
CHANGED
|
@@ -220,6 +220,12 @@
|
|
|
220
220
|
filter: brightness(0.95);
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
#scratchpadBtn.has-content {
|
|
224
|
+
border-color: var(--accent);
|
|
225
|
+
color: var(--accent);
|
|
226
|
+
font-weight: 600;
|
|
227
|
+
}
|
|
228
|
+
|
|
223
229
|
.section-header select {
|
|
224
230
|
font-weight: 600;
|
|
225
231
|
font-size: 14px;
|
|
@@ -1339,6 +1345,125 @@
|
|
|
1339
1345
|
background: var(--panel);
|
|
1340
1346
|
}
|
|
1341
1347
|
|
|
1348
|
+
body.scratchpad-open {
|
|
1349
|
+
overflow: hidden;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
.scratchpad-overlay {
|
|
1353
|
+
position: fixed;
|
|
1354
|
+
inset: 0;
|
|
1355
|
+
z-index: 50;
|
|
1356
|
+
display: flex;
|
|
1357
|
+
align-items: center;
|
|
1358
|
+
justify-content: center;
|
|
1359
|
+
padding: 24px;
|
|
1360
|
+
background: rgba(0, 0, 0, 0.48);
|
|
1361
|
+
backdrop-filter: blur(2px);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
.scratchpad-overlay[hidden] {
|
|
1365
|
+
display: none !important;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
.scratchpad-dialog {
|
|
1369
|
+
width: min(860px, 100%);
|
|
1370
|
+
max-height: min(82vh, 900px);
|
|
1371
|
+
border: 1px solid var(--border);
|
|
1372
|
+
border-radius: 14px;
|
|
1373
|
+
background: var(--panel);
|
|
1374
|
+
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.28);
|
|
1375
|
+
display: flex;
|
|
1376
|
+
flex-direction: column;
|
|
1377
|
+
overflow: hidden;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
.scratchpad-header {
|
|
1381
|
+
display: flex;
|
|
1382
|
+
align-items: flex-start;
|
|
1383
|
+
justify-content: space-between;
|
|
1384
|
+
gap: 12px;
|
|
1385
|
+
padding: 16px 18px 12px;
|
|
1386
|
+
border-bottom: 1px solid var(--border-muted);
|
|
1387
|
+
background: var(--panel-2);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
.scratchpad-header > div {
|
|
1391
|
+
flex: 1 1 auto;
|
|
1392
|
+
min-width: 0;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
.scratchpad-header h2 {
|
|
1396
|
+
margin: 0;
|
|
1397
|
+
font-size: 17px;
|
|
1398
|
+
font-weight: 600;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
.scratchpad-description {
|
|
1402
|
+
margin: 6px 0 0;
|
|
1403
|
+
font-size: 12px;
|
|
1404
|
+
line-height: 1.45;
|
|
1405
|
+
color: var(--muted);
|
|
1406
|
+
max-width: none;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
.scratchpad-close-btn {
|
|
1410
|
+
padding: 6px 10px;
|
|
1411
|
+
line-height: 1;
|
|
1412
|
+
flex: 0 0 auto;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
.scratchpad-textarea {
|
|
1416
|
+
width: 100%;
|
|
1417
|
+
min-height: 280px;
|
|
1418
|
+
flex: 1 1 auto;
|
|
1419
|
+
border: 0;
|
|
1420
|
+
border-bottom: 1px solid var(--border-muted);
|
|
1421
|
+
border-radius: 0;
|
|
1422
|
+
margin: 0;
|
|
1423
|
+
padding: 16px 18px;
|
|
1424
|
+
background: var(--panel);
|
|
1425
|
+
color: var(--text);
|
|
1426
|
+
font-family: var(--font-mono);
|
|
1427
|
+
font-size: 13px;
|
|
1428
|
+
line-height: 1.55;
|
|
1429
|
+
resize: vertical;
|
|
1430
|
+
outline: none;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
.scratchpad-footer {
|
|
1434
|
+
display: flex;
|
|
1435
|
+
align-items: center;
|
|
1436
|
+
justify-content: space-between;
|
|
1437
|
+
gap: 12px;
|
|
1438
|
+
flex-wrap: wrap;
|
|
1439
|
+
padding: 12px 18px 16px;
|
|
1440
|
+
background: var(--panel);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
.scratchpad-meta {
|
|
1444
|
+
font-size: 12px;
|
|
1445
|
+
color: var(--muted);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
.scratchpad-actions {
|
|
1449
|
+
display: inline-flex;
|
|
1450
|
+
align-items: center;
|
|
1451
|
+
gap: 8px;
|
|
1452
|
+
flex-wrap: wrap;
|
|
1453
|
+
justify-content: flex-end;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
#scratchpadDoneBtn:not(:disabled) {
|
|
1457
|
+
background: var(--accent);
|
|
1458
|
+
border-color: var(--accent);
|
|
1459
|
+
color: var(--accent-contrast);
|
|
1460
|
+
font-weight: 600;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
#scratchpadDoneBtn:not(:disabled):hover {
|
|
1464
|
+
filter: brightness(0.95);
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1342
1467
|
#status.error { color: var(--error); }
|
|
1343
1468
|
#status.warning { color: var(--warn); }
|
|
1344
1469
|
#status.success { color: var(--ok); }
|
package/index.ts
CHANGED
|
@@ -5740,6 +5740,7 @@ ${cssVarsBlock}
|
|
|
5740
5740
|
</div>
|
|
5741
5741
|
<div class="section-header-actions">
|
|
5742
5742
|
<button id="leftFocusBtn" class="pane-focus-btn" type="button" title="Show only the editor pane. Shortcut: F10 or Cmd/Ctrl+Esc.">Focus pane</button>
|
|
5743
|
+
<button id="scratchpadBtn" type="button" title="Open a local persistent scratchpad for quick notes. Scratchpad text is never run, critiqued, or exported unless you explicitly insert it into the editor.">Scratchpad</button>
|
|
5743
5744
|
</div>
|
|
5744
5745
|
</div>
|
|
5745
5746
|
<div class="source-wrap">
|
|
@@ -5875,6 +5876,28 @@ ${cssVarsBlock}
|
|
|
5875
5876
|
<span class="shortcut-hint">Focus pane: F10 (or Cmd/Ctrl+Esc) to toggle · Run / queue steering: Cmd/Ctrl+Enter · Stop request: Esc</span>
|
|
5876
5877
|
</footer>
|
|
5877
5878
|
|
|
5879
|
+
<div id="scratchpadOverlay" class="scratchpad-overlay" hidden>
|
|
5880
|
+
<div id="scratchpadDialog" class="scratchpad-dialog" role="dialog" aria-modal="true" aria-labelledby="scratchpadTitle">
|
|
5881
|
+
<div class="scratchpad-header">
|
|
5882
|
+
<div>
|
|
5883
|
+
<h2 id="scratchpadTitle">Scratchpad</h2>
|
|
5884
|
+
<p class="scratchpad-description">Local persistent notes for thoughts you want to park while working. Closing the scratchpad does not clear it: notes persist locally until you edit or clear them. Scratchpad text is not run, critiqued, sent, or exported unless you explicitly insert it into the editor.</p>
|
|
5885
|
+
</div>
|
|
5886
|
+
<button id="scratchpadCloseBtn" type="button" class="scratchpad-close-btn" aria-label="Keep current scratchpad text and close scratchpad" title="Keep current scratchpad text and close scratchpad">✕</button>
|
|
5887
|
+
</div>
|
|
5888
|
+
<textarea id="scratchpadText" class="scratchpad-textarea" placeholder="Jot quick thoughts, TODOs, or prompt ideas here..."></textarea>
|
|
5889
|
+
<div class="scratchpad-footer">
|
|
5890
|
+
<span id="scratchpadMeta" class="scratchpad-meta">Empty · local only</span>
|
|
5891
|
+
<div class="scratchpad-actions">
|
|
5892
|
+
<button id="scratchpadInsertBtn" type="button" title="Insert the scratchpad text into the editor at the current selection, or append it if no editor selection is available.">Insert into editor</button>
|
|
5893
|
+
<button id="scratchpadCopyBtn" type="button" title="Copy scratchpad text to the clipboard.">Copy</button>
|
|
5894
|
+
<button id="scratchpadClearBtn" type="button" title="Clear scratchpad text.">Clear</button>
|
|
5895
|
+
<button id="scratchpadDoneBtn" type="button" title="Keep the current scratchpad text and close the scratchpad.">Keep and close</button>
|
|
5896
|
+
</div>
|
|
5897
|
+
</div>
|
|
5898
|
+
</div>
|
|
5899
|
+
</div>
|
|
5900
|
+
|
|
5878
5901
|
<!-- Defer sanitizer script so studio can boot/connect even if CDN is slow or blocked. -->
|
|
5879
5902
|
<script defer src="https://cdn.jsdelivr.net/npm/dompurify@3.2.6/dist/purify.min.js"></script>
|
|
5880
5903
|
<script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-studio",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.37",
|
|
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",
|