pi-studio 0.5.57 → 0.5.59
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 +15 -0
- package/client/studio-client.js +573 -7
- package/client/studio.css +552 -1
- package/index.ts +139 -0
- package/package.json +1 -1
package/client/studio-client.js
CHANGED
|
@@ -133,6 +133,7 @@
|
|
|
133
133
|
const reviewNotesEmptyStateEl = document.getElementById("reviewNotesEmptyState");
|
|
134
134
|
const reviewNotesAddBtn = document.getElementById("reviewNotesAddBtn");
|
|
135
135
|
const reviewNotesInlineAllBtn = document.getElementById("reviewNotesInlineAllBtn");
|
|
136
|
+
const reviewNotesDeleteAllBtn = document.getElementById("reviewNotesDeleteAllBtn");
|
|
136
137
|
const reviewNotesCloseBtn = document.getElementById("reviewNotesCloseBtn");
|
|
137
138
|
const reviewNotesDoneBtn = document.getElementById("reviewNotesDoneBtn");
|
|
138
139
|
|
|
@@ -459,16 +460,92 @@
|
|
|
459
460
|
return "working";
|
|
460
461
|
}
|
|
461
462
|
|
|
463
|
+
async function writeTextToClipboard(text) {
|
|
464
|
+
const content = String(text || "");
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
await fetchStudioJson("/clipboard", {
|
|
468
|
+
method: "POST",
|
|
469
|
+
body: JSON.stringify({ text: content }),
|
|
470
|
+
});
|
|
471
|
+
return true;
|
|
472
|
+
} catch {
|
|
473
|
+
// Fall back to browser clipboard APIs. The server-side clipboard path
|
|
474
|
+
// is most reliable for local Studio, but may be unavailable over SSH
|
|
475
|
+
// or on systems without a clipboard command.
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Prefer a copy-event payload first. It runs synchronously inside the
|
|
479
|
+
// user's click gesture and avoids browser quirks where copying a hidden
|
|
480
|
+
// textarea reports success but leaves the system clipboard unchanged.
|
|
481
|
+
if (document.execCommand && typeof document.addEventListener === "function") {
|
|
482
|
+
let handled = false;
|
|
483
|
+
const handleCopy = (event) => {
|
|
484
|
+
if (!event || !event.clipboardData) return;
|
|
485
|
+
event.clipboardData.setData("text/plain", content);
|
|
486
|
+
event.preventDefault();
|
|
487
|
+
handled = true;
|
|
488
|
+
};
|
|
489
|
+
try {
|
|
490
|
+
document.addEventListener("copy", handleCopy, true);
|
|
491
|
+
const ok = document.execCommand("copy");
|
|
492
|
+
if (ok && handled) return true;
|
|
493
|
+
} catch {
|
|
494
|
+
// Fall through to the other clipboard paths.
|
|
495
|
+
} finally {
|
|
496
|
+
document.removeEventListener("copy", handleCopy, true);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
|
|
501
|
+
try {
|
|
502
|
+
await navigator.clipboard.writeText(content);
|
|
503
|
+
return true;
|
|
504
|
+
} catch {
|
|
505
|
+
// Fall through to the selection-based legacy path.
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const textarea = document.createElement("textarea");
|
|
510
|
+
textarea.value = content;
|
|
511
|
+
textarea.setAttribute("readonly", "");
|
|
512
|
+
textarea.style.position = "fixed";
|
|
513
|
+
textarea.style.top = "0";
|
|
514
|
+
textarea.style.left = "0";
|
|
515
|
+
textarea.style.width = "1px";
|
|
516
|
+
textarea.style.height = "1px";
|
|
517
|
+
textarea.style.opacity = "0";
|
|
518
|
+
document.body.appendChild(textarea);
|
|
519
|
+
const activeEl = document.activeElement;
|
|
520
|
+
textarea.focus();
|
|
521
|
+
textarea.select();
|
|
522
|
+
textarea.setSelectionRange(0, textarea.value.length);
|
|
523
|
+
let ok = false;
|
|
524
|
+
try {
|
|
525
|
+
ok = document.execCommand && document.execCommand("copy");
|
|
526
|
+
} catch {
|
|
527
|
+
ok = false;
|
|
528
|
+
}
|
|
529
|
+
textarea.remove();
|
|
530
|
+
if (activeEl && typeof activeEl.focus === "function") {
|
|
531
|
+
try {
|
|
532
|
+
activeEl.focus();
|
|
533
|
+
} catch {
|
|
534
|
+
// Ignore focus restore failures.
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return Boolean(ok);
|
|
538
|
+
}
|
|
539
|
+
|
|
462
540
|
async function copyVisibleWorkingToClipboard() {
|
|
463
541
|
const content = buildVisibleWorkingText();
|
|
464
542
|
if (!content.trim()) {
|
|
465
543
|
setStatus("No visible working details to copy yet.", "warning");
|
|
466
544
|
return;
|
|
467
545
|
}
|
|
468
|
-
|
|
469
|
-
await navigator.clipboard.writeText(content);
|
|
546
|
+
if (await writeTextToClipboard(content)) {
|
|
470
547
|
setStatus("Copied visible working text.", "success");
|
|
471
|
-
}
|
|
548
|
+
} else {
|
|
472
549
|
setStatus("Clipboard write failed.", "warning");
|
|
473
550
|
}
|
|
474
551
|
}
|
|
@@ -567,6 +644,12 @@
|
|
|
567
644
|
let lineNumbersEnabled = false;
|
|
568
645
|
let lineNumbersRenderRaf = null;
|
|
569
646
|
let annotationsEnabled = true;
|
|
647
|
+
const STUDIO_UI_REFRESH_STORAGE_KEY = "piStudio.uiRefresh";
|
|
648
|
+
const studioUiRefreshEnabled = readStudioUiRefreshEnabled();
|
|
649
|
+
let studioUiRefreshUi = null;
|
|
650
|
+
if (studioUiRefreshEnabled && document.body) {
|
|
651
|
+
document.body.classList.add("studio-ui-refresh");
|
|
652
|
+
}
|
|
570
653
|
let scratchpadText = "";
|
|
571
654
|
let scratchpadReturnFocusEl = null;
|
|
572
655
|
let scratchpadPersistTimer = null;
|
|
@@ -585,6 +668,333 @@
|
|
|
585
668
|
let suppressedEditorSelectionEnd = null;
|
|
586
669
|
const previewJumpHighlightState = new WeakMap();
|
|
587
670
|
const PREVIEW_ANNOTATION_PLACEHOLDER_PREFIX = "PISTUDIOANNOT";
|
|
671
|
+
|
|
672
|
+
function readStudioUiRefreshEnabled() {
|
|
673
|
+
const normalize = (value) => String(value == null ? "" : value).trim().toLowerCase();
|
|
674
|
+
const queryValue = initialQueryParams.has("uiRefresh")
|
|
675
|
+
? initialQueryParams.get("uiRefresh")
|
|
676
|
+
: (initialQueryParams.has("studioUiRefresh") ? initialQueryParams.get("studioUiRefresh") : null);
|
|
677
|
+
const isTruthy = (value) => ["1", "true", "yes", "on", "v2", "refresh"].indexOf(normalize(value)) !== -1;
|
|
678
|
+
const isFalsey = (value) => ["0", "false", "no", "off"].indexOf(normalize(value)) !== -1;
|
|
679
|
+
if (queryValue !== null) {
|
|
680
|
+
const enabled = isTruthy(queryValue) || (!isFalsey(queryValue) && normalize(queryValue) !== "");
|
|
681
|
+
try {
|
|
682
|
+
if (enabled) window.localStorage && window.localStorage.setItem(STUDIO_UI_REFRESH_STORAGE_KEY, "1");
|
|
683
|
+
else window.localStorage && window.localStorage.removeItem(STUDIO_UI_REFRESH_STORAGE_KEY);
|
|
684
|
+
} catch {}
|
|
685
|
+
return enabled;
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
return Boolean(window.localStorage && window.localStorage.getItem(STUDIO_UI_REFRESH_STORAGE_KEY) === "1");
|
|
689
|
+
} catch {
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function makeStudioUiRefreshElement(tagName, className, text) {
|
|
695
|
+
const element = document.createElement(tagName);
|
|
696
|
+
if (className) element.className = className;
|
|
697
|
+
if (typeof text === "string") element.textContent = text;
|
|
698
|
+
return element;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function makeStudioUiRefreshSeparator() {
|
|
702
|
+
return makeStudioUiRefreshElement("span", "studio-refresh-sep");
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function makeStudioUiRefreshIcon(kind) {
|
|
706
|
+
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
707
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
708
|
+
svg.setAttribute("aria-hidden", "true");
|
|
709
|
+
svg.classList.add("studio-refresh-icon");
|
|
710
|
+
const paths = kind === "focus-exit"
|
|
711
|
+
? ["M4 4l6 6", "M10 4v6H4", "M20 20l-6-6", "M14 20v-6h6"]
|
|
712
|
+
: ["M14 4h6v6", "M20 4l-6 6", "M10 20H4v-6", "M4 20l6-6"];
|
|
713
|
+
for (const d of paths) {
|
|
714
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
715
|
+
path.setAttribute("d", d);
|
|
716
|
+
svg.appendChild(path);
|
|
717
|
+
}
|
|
718
|
+
return svg;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function setStudioUiRefreshFocusButtonIcon(buttonEl, isFocusedPane) {
|
|
722
|
+
if (!buttonEl || !studioUiRefreshEnabled) return;
|
|
723
|
+
buttonEl.replaceChildren(makeStudioUiRefreshIcon(isFocusedPane ? "focus-exit" : "focus"));
|
|
724
|
+
buttonEl.setAttribute("aria-label", isFocusedPane ? "Exit focus" : "Focus pane");
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function appendStudioUiRefreshMenuSection(menuEl, heading, controls) {
|
|
728
|
+
const sectionEl = makeStudioUiRefreshElement("div", "studio-refresh-menu-section");
|
|
729
|
+
if (heading) {
|
|
730
|
+
sectionEl.appendChild(makeStudioUiRefreshElement("div", "studio-refresh-menu-heading", heading));
|
|
731
|
+
}
|
|
732
|
+
for (const control of controls) {
|
|
733
|
+
if (!control) continue;
|
|
734
|
+
const itemEl = makeStudioUiRefreshElement("div", "studio-refresh-menu-item");
|
|
735
|
+
itemEl.appendChild(control);
|
|
736
|
+
sectionEl.appendChild(itemEl);
|
|
737
|
+
}
|
|
738
|
+
menuEl.appendChild(sectionEl);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function getStudioUiRefreshSelectSummary(selectEl, prefix) {
|
|
742
|
+
if (!selectEl) return "";
|
|
743
|
+
const option = selectEl.options && selectEl.selectedIndex >= 0 ? selectEl.options[selectEl.selectedIndex] : null;
|
|
744
|
+
let label = option ? String(option.textContent || option.label || option.value || "") : String(selectEl.value || "");
|
|
745
|
+
if (prefix) label = label.replace(new RegExp("^" + prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*:?\\s*", "i"), "");
|
|
746
|
+
return label.trim();
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function setStudioUiRefreshButtonText(buttonEl, text) {
|
|
750
|
+
if (!buttonEl) return;
|
|
751
|
+
buttonEl.textContent = text;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
function getStudioUiRefreshAnnotationHeaderEnabled() {
|
|
755
|
+
try {
|
|
756
|
+
return Boolean(stripAnnotationHeader(sourceTextEl.value).hadHeader);
|
|
757
|
+
} catch {
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function syncStudioUiRefreshSummaries() {
|
|
763
|
+
if (!studioUiRefreshUi) return;
|
|
764
|
+
if (studioUiRefreshUi.annotationsButton) {
|
|
765
|
+
const inlineLabel = annotationsEnabled ? "Inline on" : "Inline hidden";
|
|
766
|
+
if (isEditorOnlyMode) {
|
|
767
|
+
setStudioUiRefreshButtonText(studioUiRefreshUi.annotationsButton, "Annotations: " + inlineLabel);
|
|
768
|
+
} else {
|
|
769
|
+
const headerLabel = getStudioUiRefreshAnnotationHeaderEnabled() ? "Header on" : "Header off";
|
|
770
|
+
setStudioUiRefreshButtonText(studioUiRefreshUi.annotationsButton, "Annotations: " + inlineLabel + " · " + headerLabel);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (studioUiRefreshUi.viewButton) {
|
|
774
|
+
const syntaxLabel = editorHighlightEnabled
|
|
775
|
+
? (getStudioUiRefreshSelectSummary(highlightSelect, "Syntax highlight") || editorLanguage || "Markdown")
|
|
776
|
+
: "Off";
|
|
777
|
+
const lineLabel = lineNumbersEnabled ? "Lines on" : "Lines off";
|
|
778
|
+
setStudioUiRefreshButtonText(studioUiRefreshUi.viewButton, "View: " + syntaxLabel + " · " + lineLabel);
|
|
779
|
+
}
|
|
780
|
+
syncStudioUiRefreshReviewTrigger();
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function closeStudioUiRefreshMenus() {
|
|
784
|
+
if (!studioUiRefreshUi || !studioUiRefreshUi.menus) return;
|
|
785
|
+
for (const item of studioUiRefreshUi.menus) {
|
|
786
|
+
item.menu.hidden = true;
|
|
787
|
+
item.button.classList.remove("is-open");
|
|
788
|
+
item.button.setAttribute("aria-expanded", "false");
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function toggleStudioUiRefreshMenu(name) {
|
|
793
|
+
if (!studioUiRefreshUi || !studioUiRefreshUi.menus) return;
|
|
794
|
+
let willOpen = false;
|
|
795
|
+
for (const item of studioUiRefreshUi.menus) {
|
|
796
|
+
if (item.name === name) willOpen = item.menu.hidden;
|
|
797
|
+
}
|
|
798
|
+
for (const item of studioUiRefreshUi.menus) {
|
|
799
|
+
const isOpen = willOpen && item.name === name;
|
|
800
|
+
item.menu.hidden = !isOpen;
|
|
801
|
+
item.button.classList.toggle("is-open", isOpen);
|
|
802
|
+
item.button.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
function syncStudioUiRefreshReviewTrigger() {
|
|
807
|
+
if (!studioUiRefreshUi || !studioUiRefreshUi.reviewButton) return;
|
|
808
|
+
const critiqueIsStop = getAbortablePendingKind() === "critique";
|
|
809
|
+
const reviewButton = studioUiRefreshUi.reviewButton;
|
|
810
|
+
reviewButton.textContent = critiqueIsStop ? "Stop critique" : "Review";
|
|
811
|
+
reviewButton.classList.toggle("request-stop-active", critiqueIsStop);
|
|
812
|
+
reviewButton.disabled = critiqueIsStop ? Boolean(critiqueBtn && critiqueBtn.disabled) : false;
|
|
813
|
+
reviewButton.title = critiqueIsStop
|
|
814
|
+
? "Stop the running critique request. Shortcut: Esc."
|
|
815
|
+
: "Open review actions and settings.";
|
|
816
|
+
if (critiqueIsStop) {
|
|
817
|
+
closeStudioUiRefreshMenus();
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function makeStudioUiRefreshMenu(buttonEl, name, menuClassName) {
|
|
822
|
+
const anchorEl = makeStudioUiRefreshElement("span", "studio-refresh-menu-anchor " + (menuClassName || ""));
|
|
823
|
+
const menuEl = makeStudioUiRefreshElement("div", "studio-refresh-menu");
|
|
824
|
+
menuEl.hidden = true;
|
|
825
|
+
buttonEl.type = "button";
|
|
826
|
+
buttonEl.classList.add("studio-refresh-chip");
|
|
827
|
+
buttonEl.setAttribute("aria-haspopup", "menu");
|
|
828
|
+
buttonEl.setAttribute("aria-expanded", "false");
|
|
829
|
+
buttonEl.addEventListener("click", (event) => {
|
|
830
|
+
event.stopPropagation();
|
|
831
|
+
if (name === "review" && getAbortablePendingKind() === "critique") {
|
|
832
|
+
requestCancelForPendingRequest("critique");
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
toggleStudioUiRefreshMenu(name);
|
|
836
|
+
});
|
|
837
|
+
anchorEl.appendChild(buttonEl);
|
|
838
|
+
anchorEl.appendChild(menuEl);
|
|
839
|
+
return { name, anchor: anchorEl, button: buttonEl, menu: menuEl };
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function setStudioUiRefreshPreference(enabled) {
|
|
843
|
+
try {
|
|
844
|
+
if (enabled) window.localStorage && window.localStorage.setItem(STUDIO_UI_REFRESH_STORAGE_KEY, "1");
|
|
845
|
+
else window.localStorage && window.localStorage.removeItem(STUDIO_UI_REFRESH_STORAGE_KEY);
|
|
846
|
+
} catch {}
|
|
847
|
+
try {
|
|
848
|
+
const url = new URL(window.location.href);
|
|
849
|
+
url.searchParams.set("uiRefresh", enabled ? "1" : "0");
|
|
850
|
+
window.location.assign(url.toString());
|
|
851
|
+
} catch {
|
|
852
|
+
window.location.reload();
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function setupStudioUiRefreshToggleButton() {
|
|
857
|
+
if (!footerMetaEl || document.getElementById("studioUiRefreshToggleBtn")) return;
|
|
858
|
+
const button = makeStudioUiRefreshElement("button", "footer-compact-btn studio-ui-refresh-toggle", studioUiRefreshEnabled ? "UI: Refresh" : "UI: Classic");
|
|
859
|
+
button.id = "studioUiRefreshToggleBtn";
|
|
860
|
+
button.type = "button";
|
|
861
|
+
button.title = studioUiRefreshEnabled
|
|
862
|
+
? "Switch Studio to the classic layout."
|
|
863
|
+
: "Switch Studio to the refreshed layout.";
|
|
864
|
+
button.addEventListener("click", () => {
|
|
865
|
+
setStudioUiRefreshPreference(!studioUiRefreshEnabled);
|
|
866
|
+
});
|
|
867
|
+
if (compactBtn && compactBtn.parentNode === footerMetaEl) {
|
|
868
|
+
compactBtn.insertAdjacentElement("afterend", button);
|
|
869
|
+
} else {
|
|
870
|
+
footerMetaEl.appendChild(button);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function setupStudioUiRefreshPrototype() {
|
|
875
|
+
if (!studioUiRefreshEnabled || studioUiRefreshUi) return;
|
|
876
|
+
const leftHeaderEl = document.getElementById("leftSectionHeader");
|
|
877
|
+
const sourceMetaEl = leftPaneEl ? leftPaneEl.querySelector(".source-meta") : null;
|
|
878
|
+
if (!leftHeaderEl || !sourceMetaEl || !copyDraftBtn) return;
|
|
879
|
+
|
|
880
|
+
let reviewMenu = null;
|
|
881
|
+
if (!isEditorOnlyMode && critiqueBtn && lensSelect) {
|
|
882
|
+
const reviewButton = makeStudioUiRefreshElement("button", "studio-refresh-tool-tab studio-refresh-review-btn", "Review");
|
|
883
|
+
reviewMenu = makeStudioUiRefreshMenu(reviewButton, "review", "studio-refresh-review-anchor");
|
|
884
|
+
appendStudioUiRefreshMenuSection(reviewMenu.menu, "Action", [critiqueBtn]);
|
|
885
|
+
appendStudioUiRefreshMenuSection(reviewMenu.menu, "Setting", [lensSelect]);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
const headerTopEl = makeStudioUiRefreshElement("div", "studio-refresh-header-top");
|
|
889
|
+
const titleGroupEl = makeStudioUiRefreshElement("div", "studio-refresh-title-group");
|
|
890
|
+
if (leftFocusBtn) {
|
|
891
|
+
setStudioUiRefreshFocusButtonIcon(leftFocusBtn, false);
|
|
892
|
+
titleGroupEl.appendChild(leftFocusBtn);
|
|
893
|
+
}
|
|
894
|
+
titleGroupEl.appendChild(makeStudioUiRefreshSeparator());
|
|
895
|
+
if (isEditorOnlyMode) {
|
|
896
|
+
titleGroupEl.appendChild(makeStudioUiRefreshElement("span", "studio-refresh-static-title", "Editor (Raw)"));
|
|
897
|
+
} else if (editorViewSelect) {
|
|
898
|
+
titleGroupEl.appendChild(editorViewSelect);
|
|
899
|
+
}
|
|
900
|
+
headerTopEl.appendChild(titleGroupEl);
|
|
901
|
+
const headerToolsEl = makeStudioUiRefreshElement("div", "studio-refresh-pane-tools");
|
|
902
|
+
if (reviewNotesBtn) headerToolsEl.appendChild(reviewNotesBtn);
|
|
903
|
+
if (outlineBtn) headerToolsEl.appendChild(outlineBtn);
|
|
904
|
+
if (scratchpadBtn) headerToolsEl.appendChild(scratchpadBtn);
|
|
905
|
+
if (reviewMenu) headerToolsEl.appendChild(reviewMenu.anchor);
|
|
906
|
+
headerTopEl.appendChild(headerToolsEl);
|
|
907
|
+
|
|
908
|
+
const headerUtilityEl = makeStudioUiRefreshElement("div", "studio-refresh-header-utility");
|
|
909
|
+
const utilityLeftEl = makeStudioUiRefreshElement("div", "studio-refresh-utility-left");
|
|
910
|
+
if (sourceBadgeEl) utilityLeftEl.appendChild(sourceBadgeEl);
|
|
911
|
+
if (sourceBadgeEl && (resourceDirBtn || resourceDirLabel || resourceDirInputWrap || syncBadgeEl)) {
|
|
912
|
+
utilityLeftEl.appendChild(makeStudioUiRefreshSeparator());
|
|
913
|
+
}
|
|
914
|
+
if (resourceDirBtn) utilityLeftEl.appendChild(resourceDirBtn);
|
|
915
|
+
if (resourceDirLabel) utilityLeftEl.appendChild(resourceDirLabel);
|
|
916
|
+
if (resourceDirInputWrap) utilityLeftEl.appendChild(resourceDirInputWrap);
|
|
917
|
+
if (syncBadgeEl) utilityLeftEl.appendChild(syncBadgeEl);
|
|
918
|
+
headerUtilityEl.appendChild(utilityLeftEl);
|
|
919
|
+
leftHeaderEl.replaceChildren(headerTopEl, headerUtilityEl);
|
|
920
|
+
|
|
921
|
+
const rightHeaderEl = document.getElementById("rightSectionHeader");
|
|
922
|
+
if (rightHeaderEl && rightViewSelect) {
|
|
923
|
+
const rightIdentityEl = makeStudioUiRefreshElement("div", "studio-refresh-pane-identity studio-refresh-pane-identity-right");
|
|
924
|
+
const rightTitleGroupEl = makeStudioUiRefreshElement("div", "studio-refresh-title-group");
|
|
925
|
+
if (rightFocusBtn) {
|
|
926
|
+
setStudioUiRefreshFocusButtonIcon(rightFocusBtn, false);
|
|
927
|
+
rightTitleGroupEl.appendChild(rightFocusBtn);
|
|
928
|
+
rightTitleGroupEl.appendChild(makeStudioUiRefreshSeparator());
|
|
929
|
+
}
|
|
930
|
+
if (isEditorOnlyMode) {
|
|
931
|
+
rightTitleGroupEl.appendChild(makeStudioUiRefreshElement("span", "studio-refresh-static-title", "Editor (Preview)"));
|
|
932
|
+
} else {
|
|
933
|
+
rightTitleGroupEl.appendChild(rightViewSelect);
|
|
934
|
+
}
|
|
935
|
+
rightIdentityEl.appendChild(rightTitleGroupEl);
|
|
936
|
+
const rightToolsEl = makeStudioUiRefreshElement("div", "studio-refresh-pane-tools");
|
|
937
|
+
if (exportPdfBtn) rightToolsEl.appendChild(exportPdfBtn);
|
|
938
|
+
rightHeaderEl.replaceChildren(rightIdentityEl, rightToolsEl);
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const toolbarEl = makeStudioUiRefreshElement("div", "studio-refresh-toolbar");
|
|
942
|
+
const toolbarMainEl = makeStudioUiRefreshElement("div", "studio-refresh-toolbar-main");
|
|
943
|
+
const actionsEl = makeStudioUiRefreshElement("div", "studio-refresh-toolbar-actions");
|
|
944
|
+
const actionLineOneEl = makeStudioUiRefreshElement("div", "studio-refresh-action-line");
|
|
945
|
+
if (!isEditorOnlyMode && sendRunBtn) actionLineOneEl.appendChild(sendRunBtn);
|
|
946
|
+
if (!isEditorOnlyMode && queueSteerBtn) actionLineOneEl.appendChild(queueSteerBtn);
|
|
947
|
+
const actionLineTwoEl = makeStudioUiRefreshElement("div", "studio-refresh-action-line");
|
|
948
|
+
actionLineTwoEl.appendChild(copyDraftBtn);
|
|
949
|
+
if (!isEditorOnlyMode && sendEditorBtn) actionLineTwoEl.appendChild(sendEditorBtn);
|
|
950
|
+
if (actionLineOneEl.childNodes.length > 0) actionsEl.appendChild(actionLineOneEl);
|
|
951
|
+
actionsEl.appendChild(actionLineTwoEl);
|
|
952
|
+
|
|
953
|
+
const stateEl = makeStudioUiRefreshElement("div", "studio-refresh-toolbar-state");
|
|
954
|
+
const annotationsButton = makeStudioUiRefreshElement("button", "", "Annotations");
|
|
955
|
+
const annotationsMenu = makeStudioUiRefreshMenu(annotationsButton, "annotations", "studio-refresh-annotations-anchor");
|
|
956
|
+
appendStudioUiRefreshMenuSection(annotationsMenu.menu, "Display", isEditorOnlyMode ? [annotationModeSelect] : [annotationModeSelect, insertHeaderBtn]);
|
|
957
|
+
appendStudioUiRefreshMenuSection(annotationsMenu.menu, "Actions", [stripAnnotationsBtn, saveAnnotatedBtn]);
|
|
958
|
+
const viewButton = makeStudioUiRefreshElement("button", "", "View");
|
|
959
|
+
const viewMenu = makeStudioUiRefreshMenu(viewButton, "view", "studio-refresh-view-anchor");
|
|
960
|
+
appendStudioUiRefreshMenuSection(viewMenu.menu, "Display", [highlightSelect, lineNumbersSelect]);
|
|
961
|
+
stateEl.appendChild(annotationsMenu.anchor);
|
|
962
|
+
stateEl.appendChild(viewMenu.anchor);
|
|
963
|
+
|
|
964
|
+
toolbarMainEl.appendChild(actionsEl);
|
|
965
|
+
toolbarMainEl.appendChild(stateEl);
|
|
966
|
+
toolbarEl.appendChild(toolbarMainEl);
|
|
967
|
+
sourceMetaEl.replaceChildren(toolbarEl);
|
|
968
|
+
|
|
969
|
+
studioUiRefreshUi = {
|
|
970
|
+
annotationsButton,
|
|
971
|
+
viewButton,
|
|
972
|
+
reviewButton: reviewMenu ? reviewMenu.button : null,
|
|
973
|
+
menus: [annotationsMenu, viewMenu].concat(reviewMenu ? [reviewMenu] : []),
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
document.addEventListener("click", (event) => {
|
|
977
|
+
const target = event.target;
|
|
978
|
+
if (target instanceof Element && target.closest(".studio-refresh-menu-anchor")) return;
|
|
979
|
+
closeStudioUiRefreshMenus();
|
|
980
|
+
});
|
|
981
|
+
document.addEventListener("keydown", (event) => {
|
|
982
|
+
if (event.key === "Escape") closeStudioUiRefreshMenus();
|
|
983
|
+
});
|
|
984
|
+
toolbarEl.addEventListener("change", () => {
|
|
985
|
+
window.setTimeout(syncStudioUiRefreshSummaries, 0);
|
|
986
|
+
});
|
|
987
|
+
toolbarEl.addEventListener("click", (event) => {
|
|
988
|
+
const target = event.target;
|
|
989
|
+
if (target instanceof Element && target.closest(".studio-refresh-menu")) {
|
|
990
|
+
window.setTimeout(syncStudioUiRefreshSummaries, 0);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
syncStudioUiRefreshSummaries();
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
setupStudioUiRefreshToggleButton();
|
|
997
|
+
setupStudioUiRefreshPrototype();
|
|
588
998
|
const annotationHelpers = globalThis.PiStudioAnnotationHelpers;
|
|
589
999
|
if (!annotationHelpers || typeof annotationHelpers.collectInlineAnnotationMarkers !== "function") {
|
|
590
1000
|
throw new Error("Studio annotation helpers failed to load.");
|
|
@@ -1224,7 +1634,7 @@
|
|
|
1224
1634
|
|
|
1225
1635
|
function updateSourceBadge() {
|
|
1226
1636
|
const label = sourceState && sourceState.label ? sourceState.label : "blank";
|
|
1227
|
-
sourceBadgeEl.textContent = "Editor origin: " + label;
|
|
1637
|
+
sourceBadgeEl.textContent = (studioUiRefreshEnabled ? "Origin: " : "Editor origin: ") + label;
|
|
1228
1638
|
const descriptor = getCurrentStudioDocumentDescriptor();
|
|
1229
1639
|
if (sourceBadgeEl) {
|
|
1230
1640
|
sourceBadgeEl.title = descriptor.fileBacked
|
|
@@ -1285,6 +1695,9 @@
|
|
|
1285
1695
|
btn.classList.toggle("is-active", isFocusedPane);
|
|
1286
1696
|
btn.setAttribute("aria-pressed", isFocusedPane ? "true" : "false");
|
|
1287
1697
|
btn.textContent = isFocusedPane ? "Exit focus" : "Focus pane";
|
|
1698
|
+
if (studioUiRefreshEnabled) {
|
|
1699
|
+
setStudioUiRefreshFocusButtonIcon(btn, isFocusedPane);
|
|
1700
|
+
}
|
|
1288
1701
|
btn.title = isFocusedPane
|
|
1289
1702
|
? "Return to the two-pane layout. Shortcut: F10 or Cmd/Ctrl+Esc."
|
|
1290
1703
|
: "Show only the " + paneName + " pane. Shortcut: F10 or Cmd/Ctrl+Esc.";
|
|
@@ -2837,6 +3250,107 @@
|
|
|
2837
3250
|
}
|
|
2838
3251
|
}
|
|
2839
3252
|
|
|
3253
|
+
function normalizeCopyableBlockText(text) {
|
|
3254
|
+
return String(text || "").replace(/\r\n/g, "\n").replace(/\u200b/g, "");
|
|
3255
|
+
}
|
|
3256
|
+
|
|
3257
|
+
function getCopyablePreviewBlockText(blockEl) {
|
|
3258
|
+
if (!blockEl || typeof blockEl.querySelectorAll !== "function") return "";
|
|
3259
|
+
if (blockEl.classList && blockEl.classList.contains("preview-code-lines")) {
|
|
3260
|
+
return normalizeCopyableBlockText(
|
|
3261
|
+
Array.from(blockEl.querySelectorAll(".preview-code-line-content"))
|
|
3262
|
+
.map((lineEl) => lineEl && typeof lineEl.textContent === "string" ? lineEl.textContent : "")
|
|
3263
|
+
.join("\n"),
|
|
3264
|
+
);
|
|
3265
|
+
}
|
|
3266
|
+
|
|
3267
|
+
const codeEl = typeof blockEl.querySelector === "function"
|
|
3268
|
+
? blockEl.querySelector("pre code, code")
|
|
3269
|
+
: null;
|
|
3270
|
+
if (codeEl && typeof codeEl.textContent === "string") {
|
|
3271
|
+
return normalizeCopyableBlockText(codeEl.textContent);
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
const clone = typeof blockEl.cloneNode === "function" ? blockEl.cloneNode(true) : null;
|
|
3275
|
+
if (clone && typeof clone.querySelectorAll === "function") {
|
|
3276
|
+
Array.from(clone.querySelectorAll(".studio-copy-block-btn")).forEach((buttonEl) => {
|
|
3277
|
+
if (buttonEl && buttonEl.parentNode) buttonEl.parentNode.removeChild(buttonEl);
|
|
3278
|
+
});
|
|
3279
|
+
return normalizeCopyableBlockText(clone.textContent || "");
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
return normalizeCopyableBlockText(blockEl.textContent || "");
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3285
|
+
async function handleCopyPreviewBlockButtonClick(event) {
|
|
3286
|
+
const target = event && event.target;
|
|
3287
|
+
const copyBtn = target instanceof Element ? target.closest(".studio-copy-block-btn") : null;
|
|
3288
|
+
if (!copyBtn) return;
|
|
3289
|
+
event.preventDefault();
|
|
3290
|
+
event.stopPropagation();
|
|
3291
|
+
if (typeof event.stopImmediatePropagation === "function") {
|
|
3292
|
+
event.stopImmediatePropagation();
|
|
3293
|
+
}
|
|
3294
|
+
|
|
3295
|
+
const blockEl = copyBtn.closest(".studio-copyable-block");
|
|
3296
|
+
if (!blockEl) {
|
|
3297
|
+
setStatus("Could not find the block to copy.", "warning");
|
|
3298
|
+
return;
|
|
3299
|
+
}
|
|
3300
|
+
|
|
3301
|
+
const text = getCopyablePreviewBlockText(blockEl);
|
|
3302
|
+
if (!text.trim()) {
|
|
3303
|
+
setStatus("Nothing to copy from this block.", "warning");
|
|
3304
|
+
return;
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
if (copyBtn.dataset && copyBtn.dataset.studioCopyBusy === "1") return;
|
|
3308
|
+
if (copyBtn.dataset) copyBtn.dataset.studioCopyBusy = "1";
|
|
3309
|
+
const ok = await writeTextToClipboard(text);
|
|
3310
|
+
if (ok) {
|
|
3311
|
+
setStatus("Copied block to clipboard.", "success");
|
|
3312
|
+
} else {
|
|
3313
|
+
setStatus("Clipboard write failed.", "warning");
|
|
3314
|
+
}
|
|
3315
|
+
if (copyBtn.dataset) {
|
|
3316
|
+
window.setTimeout(() => {
|
|
3317
|
+
if (copyBtn.dataset) copyBtn.dataset.studioCopyBusy = "0";
|
|
3318
|
+
}, 150);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
function decorateCopyablePreviewBlocks(targetEl) {
|
|
3323
|
+
if (!targetEl || typeof targetEl.querySelectorAll !== "function") return;
|
|
3324
|
+
const blocks = Array.from(targetEl.querySelectorAll("div.sourceCode, pre, .preview-code-lines"));
|
|
3325
|
+
blocks.forEach((blockEl) => {
|
|
3326
|
+
if (!blockEl || !(blockEl instanceof Element)) return;
|
|
3327
|
+
if (blockEl.dataset && blockEl.dataset.studioCopyDecorated === "1") return;
|
|
3328
|
+
if (blockEl.matches && blockEl.matches("pre") && blockEl.closest("div.sourceCode")) return;
|
|
3329
|
+
if (blockEl.closest && blockEl.closest("button, .studio-copy-block-btn")) return;
|
|
3330
|
+
|
|
3331
|
+
const initialText = getCopyablePreviewBlockText(blockEl);
|
|
3332
|
+
if (!initialText.trim()) return;
|
|
3333
|
+
|
|
3334
|
+
blockEl.classList.add("studio-copyable-block");
|
|
3335
|
+
if (blockEl.dataset) blockEl.dataset.studioCopyDecorated = "1";
|
|
3336
|
+
|
|
3337
|
+
const copyBtn = document.createElement("button");
|
|
3338
|
+
copyBtn.type = "button";
|
|
3339
|
+
copyBtn.className = "studio-copy-block-btn";
|
|
3340
|
+
copyBtn.textContent = "Copy";
|
|
3341
|
+
copyBtn.title = "Copy this block to the clipboard.";
|
|
3342
|
+
copyBtn.setAttribute("aria-label", "Copy this block to the clipboard");
|
|
3343
|
+
copyBtn.addEventListener("pointerdown", (event) => {
|
|
3344
|
+
event.stopPropagation();
|
|
3345
|
+
});
|
|
3346
|
+
copyBtn.addEventListener("mousedown", (event) => {
|
|
3347
|
+
event.stopPropagation();
|
|
3348
|
+
});
|
|
3349
|
+
|
|
3350
|
+
blockEl.appendChild(copyBtn);
|
|
3351
|
+
});
|
|
3352
|
+
}
|
|
3353
|
+
|
|
2840
3354
|
async function applyRenderedMarkdown(targetEl, markdown, pane, nonce) {
|
|
2841
3355
|
const previewPrepared = annotationsEnabled
|
|
2842
3356
|
? prepareMarkdownForPandocPreview(markdown)
|
|
@@ -2879,6 +3393,7 @@
|
|
|
2879
3393
|
if (shouldDecoratePreviewComments) {
|
|
2880
3394
|
decorateRenderedEditorPreviewComments(targetEl, sourceTextEl.value || "");
|
|
2881
3395
|
}
|
|
3396
|
+
decorateCopyablePreviewBlocks(targetEl);
|
|
2882
3397
|
|
|
2883
3398
|
// Warn if relative images are present but unlikely to resolve (non-file-backed content)
|
|
2884
3399
|
if (!sourceState.path && !(resourceDirInput && resourceDirInput.value.trim())) {
|
|
@@ -3670,6 +4185,7 @@
|
|
|
3670
4185
|
if (lineNumbersSelect) {
|
|
3671
4186
|
lineNumbersSelect.value = lineNumbersEnabled ? "on" : "off";
|
|
3672
4187
|
}
|
|
4188
|
+
syncStudioUiRefreshSummaries();
|
|
3673
4189
|
updateLineNumberGutterVisibility();
|
|
3674
4190
|
scheduleEditorLineNumberRender();
|
|
3675
4191
|
if (editorHighlightEnabled && editorView === "markdown") {
|
|
@@ -4281,6 +4797,7 @@
|
|
|
4281
4797
|
targetEl.innerHTML = buildCodePreviewHtmlWithCommentBlocks(text, editorLanguage || "");
|
|
4282
4798
|
ensurePreviewSelectionActions(targetEl);
|
|
4283
4799
|
updatePreviewCommentBlocksForElement(targetEl);
|
|
4800
|
+
decorateCopyablePreviewBlocks(targetEl);
|
|
4284
4801
|
if (pane === "response") {
|
|
4285
4802
|
applyPendingResponseScrollReset();
|
|
4286
4803
|
scheduleResponsePaneRepaintNudge();
|
|
@@ -4357,6 +4874,7 @@
|
|
|
4357
4874
|
if (stripAnnotationsBtn) {
|
|
4358
4875
|
stripAnnotationsBtn.disabled = uiBusy || !hasAnnotationMarkers(sourceTextEl.value);
|
|
4359
4876
|
}
|
|
4877
|
+
syncStudioUiRefreshSummaries();
|
|
4360
4878
|
}
|
|
4361
4879
|
|
|
4362
4880
|
function scheduleEditorMetaUpdate() {
|
|
@@ -8066,6 +8584,12 @@
|
|
|
8066
8584
|
? "Inline annotations derived from all non-empty comments are currently on. Click to remove them."
|
|
8067
8585
|
: "Inline annotations derived from all non-empty comments are currently off. Click to add them.";
|
|
8068
8586
|
}
|
|
8587
|
+
if (reviewNotesDeleteAllBtn) {
|
|
8588
|
+
reviewNotesDeleteAllBtn.disabled = uiBusy || !hasNotes;
|
|
8589
|
+
reviewNotesDeleteAllBtn.title = hasNotes
|
|
8590
|
+
? "Delete all local comments for this document or draft. Existing inline [an: ...] annotations in the editor text are left unchanged."
|
|
8591
|
+
: "No local comments to delete.";
|
|
8592
|
+
}
|
|
8069
8593
|
if (reviewNotesDoneBtn) {
|
|
8070
8594
|
reviewNotesDoneBtn.disabled = !isOpen;
|
|
8071
8595
|
}
|
|
@@ -8417,6 +8941,21 @@
|
|
|
8417
8941
|
setStatus("Deleted local comment.", "success");
|
|
8418
8942
|
}
|
|
8419
8943
|
|
|
8944
|
+
function deleteAllReviewNotes() {
|
|
8945
|
+
if (!reviewNotes.length) {
|
|
8946
|
+
setStatus("No local comments to delete.", "warning");
|
|
8947
|
+
return;
|
|
8948
|
+
}
|
|
8949
|
+
const count = reviewNotes.length;
|
|
8950
|
+
const confirmed = window.confirm(
|
|
8951
|
+
"Delete all " + count + " local comment" + (count === 1 ? "" : "s") + " for this document?\n\n"
|
|
8952
|
+
+ "Existing inline [an: ...] annotations in the editor text will not be removed.",
|
|
8953
|
+
);
|
|
8954
|
+
if (!confirmed) return;
|
|
8955
|
+
setReviewNotes([]);
|
|
8956
|
+
setStatus("Deleted all local comments.", "success");
|
|
8957
|
+
}
|
|
8958
|
+
|
|
8420
8959
|
function convertReviewNoteToAnnotation(noteId) {
|
|
8421
8960
|
if (uiBusy) {
|
|
8422
8961
|
setStatus("Wait until the current Studio action finishes before toggling inline annotation state.", "warning");
|
|
@@ -8669,11 +9208,13 @@
|
|
|
8669
9208
|
if (!highlightSelect) return;
|
|
8670
9209
|
if (!editorHighlightEnabled) {
|
|
8671
9210
|
highlightSelect.value = "off";
|
|
9211
|
+
syncStudioUiRefreshSummaries();
|
|
8672
9212
|
return;
|
|
8673
9213
|
}
|
|
8674
9214
|
highlightSelect.value = (editorLanguage && SUPPORTED_LANGUAGES.indexOf(editorLanguage) !== -1)
|
|
8675
9215
|
? editorLanguage
|
|
8676
9216
|
: "markdown";
|
|
9217
|
+
syncStudioUiRefreshSummaries();
|
|
8677
9218
|
}
|
|
8678
9219
|
|
|
8679
9220
|
function setEditorHighlightEnabled(enabled) {
|
|
@@ -8776,6 +9317,7 @@
|
|
|
8776
9317
|
critiqueBtn.disabled = true;
|
|
8777
9318
|
critiqueBtn.title = "Critique is unavailable in editor-only mode.";
|
|
8778
9319
|
}
|
|
9320
|
+
syncStudioUiRefreshReviewTrigger();
|
|
8779
9321
|
return;
|
|
8780
9322
|
}
|
|
8781
9323
|
|
|
@@ -8813,6 +9355,7 @@
|
|
|
8813
9355
|
? "Critique text as-is (includes [an: ...] markers)."
|
|
8814
9356
|
: "Critique text with [an: ...] markers stripped."));
|
|
8815
9357
|
}
|
|
9358
|
+
syncStudioUiRefreshReviewTrigger();
|
|
8816
9359
|
}
|
|
8817
9360
|
|
|
8818
9361
|
function updateAnnotationModeUi() {
|
|
@@ -8823,6 +9366,7 @@
|
|
|
8823
9366
|
: "Inline annotations Hide: keep markers in the editor, hide them in preview, and strip before Run/Critique.";
|
|
8824
9367
|
}
|
|
8825
9368
|
|
|
9369
|
+
syncStudioUiRefreshSummaries();
|
|
8826
9370
|
syncRunAndCritiqueButtons();
|
|
8827
9371
|
}
|
|
8828
9372
|
|
|
@@ -9708,10 +10252,12 @@
|
|
|
9708
10252
|
if (hasHeader) {
|
|
9709
10253
|
insertHeaderBtn.textContent = "Annotation header: On";
|
|
9710
10254
|
insertHeaderBtn.title = "Remove annotated-reply protocol header while keeping body text.";
|
|
10255
|
+
syncStudioUiRefreshSummaries();
|
|
9711
10256
|
return;
|
|
9712
10257
|
}
|
|
9713
10258
|
insertHeaderBtn.textContent = "Annotation header: Off";
|
|
9714
10259
|
insertHeaderBtn.title = "Insert annotated-reply protocol header (source metadata, [an: ...] syntax hint, precedence note, and end marker).";
|
|
10260
|
+
syncStudioUiRefreshSummaries();
|
|
9715
10261
|
}
|
|
9716
10262
|
|
|
9717
10263
|
function toggleAnnotatedReplyHeader() {
|
|
@@ -10078,7 +10624,7 @@
|
|
|
10078
10624
|
}
|
|
10079
10625
|
|
|
10080
10626
|
try {
|
|
10081
|
-
await
|
|
10627
|
+
await writeTextToClipboard(content);
|
|
10082
10628
|
setStatus("Copied response text.", "success");
|
|
10083
10629
|
} catch (error) {
|
|
10084
10630
|
setStatus("Clipboard write failed.", "warning");
|
|
@@ -10302,7 +10848,7 @@
|
|
|
10302
10848
|
}
|
|
10303
10849
|
|
|
10304
10850
|
try {
|
|
10305
|
-
await
|
|
10851
|
+
await writeTextToClipboard(content);
|
|
10306
10852
|
setStatus("Copied editor text.", "success");
|
|
10307
10853
|
} catch (error) {
|
|
10308
10854
|
setStatus("Clipboard write failed.", "warning");
|
|
@@ -10386,6 +10932,12 @@
|
|
|
10386
10932
|
});
|
|
10387
10933
|
}
|
|
10388
10934
|
|
|
10935
|
+
if (reviewNotesDeleteAllBtn) {
|
|
10936
|
+
reviewNotesDeleteAllBtn.addEventListener("click", () => {
|
|
10937
|
+
deleteAllReviewNotes();
|
|
10938
|
+
});
|
|
10939
|
+
}
|
|
10940
|
+
|
|
10389
10941
|
if (reviewNoteGutterContentEl) {
|
|
10390
10942
|
reviewNoteGutterContentEl.addEventListener("click", (event) => {
|
|
10391
10943
|
const target = event.target;
|
|
@@ -10397,6 +10949,20 @@
|
|
|
10397
10949
|
});
|
|
10398
10950
|
}
|
|
10399
10951
|
|
|
10952
|
+
document.addEventListener("click", (event) => {
|
|
10953
|
+
const target = event.target;
|
|
10954
|
+
const copyBtn = target instanceof Element ? target.closest(".studio-copy-block-btn") : null;
|
|
10955
|
+
if (!copyBtn) return;
|
|
10956
|
+
void handleCopyPreviewBlockButtonClick(event);
|
|
10957
|
+
}, true);
|
|
10958
|
+
|
|
10959
|
+
document.addEventListener("pointerup", (event) => {
|
|
10960
|
+
const target = event.target;
|
|
10961
|
+
const copyBtn = target instanceof Element ? target.closest(".studio-copy-block-btn") : null;
|
|
10962
|
+
if (!copyBtn) return;
|
|
10963
|
+
void handleCopyPreviewBlockButtonClick(event);
|
|
10964
|
+
}, true);
|
|
10965
|
+
|
|
10400
10966
|
function handlePreviewCommentActionMouseDown(event) {
|
|
10401
10967
|
const target = event.target;
|
|
10402
10968
|
const actionBtn = target instanceof Element ? target.closest(".preview-comment-add, .preview-comment-jump, .preview-comment-summary") : null;
|
|
@@ -10483,7 +11049,7 @@
|
|
|
10483
11049
|
}
|
|
10484
11050
|
|
|
10485
11051
|
try {
|
|
10486
|
-
await
|
|
11052
|
+
await writeTextToClipboard(String(scratchpadText || ""));
|
|
10487
11053
|
setStatus("Copied scratchpad text.", "success");
|
|
10488
11054
|
} catch (error) {
|
|
10489
11055
|
setStatus("Clipboard write failed.", "warning");
|