pi-studio 0.5.49 → 0.5.51
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 +23 -0
- package/README.md +1 -1
- package/client/studio-client.js +1023 -79
- package/client/studio.css +32 -22
- package/index.ts +4 -1
- package/package.json +1 -1
package/client/studio-client.js
CHANGED
|
@@ -53,7 +53,9 @@
|
|
|
53
53
|
const lineNumberGutterContentEl = document.getElementById("lineNumberGutterContent");
|
|
54
54
|
const lineNumberMeasureEl = document.getElementById("lineNumberMeasure");
|
|
55
55
|
const sourcePreviewEl = document.getElementById("sourcePreview");
|
|
56
|
+
const editorSelectionActionsEl = document.getElementById("editorSelectionActions");
|
|
56
57
|
const editorSelectionCommentBtn = document.getElementById("editorSelectionCommentBtn");
|
|
58
|
+
const editorSelectionJumpBtn = document.getElementById("editorSelectionJumpBtn");
|
|
57
59
|
const leftPaneEl = document.getElementById("leftPane");
|
|
58
60
|
const rightPaneEl = document.getElementById("rightPane");
|
|
59
61
|
const sourceBadgeEl = document.getElementById("sourceBadge");
|
|
@@ -1904,6 +1906,7 @@
|
|
|
1904
1906
|
|
|
1905
1907
|
fallbackTargets.forEach((entry) => {
|
|
1906
1908
|
entry.renderTarget.classList.add("studio-mathjax-fallback");
|
|
1909
|
+
entry.renderTarget.setAttribute("data-tex-source", entry.tex);
|
|
1907
1910
|
if (entry.displayMode) {
|
|
1908
1911
|
entry.renderTarget.classList.add("studio-mathjax-fallback-display");
|
|
1909
1912
|
entry.renderTarget.textContent = "\\[\n" + entry.tex + "\n\\]";
|
|
@@ -3858,10 +3861,6 @@
|
|
|
3858
3861
|
+ " data-review-note-line-end='" + String(lineNumber) + "'"
|
|
3859
3862
|
+ " data-preview-comment-kind='" + escapeHtml(kind) + "'"
|
|
3860
3863
|
+ ">"
|
|
3861
|
-
+ "<div class='preview-comment-controls'>"
|
|
3862
|
-
+ "<button type='button' class='preview-comment-summary' hidden></button>"
|
|
3863
|
-
+ "<button type='button' class='preview-comment-add'>Comment</button>"
|
|
3864
|
-
+ "</div>"
|
|
3865
3864
|
+ "<div class='preview-comment-block-content preview-code-line-content'>" + lineHtml + "</div>"
|
|
3866
3865
|
+ "</div>",
|
|
3867
3866
|
);
|
|
@@ -3877,6 +3876,7 @@
|
|
|
3877
3876
|
clearPreviewJumpHighlight(targetEl);
|
|
3878
3877
|
finishPreviewRender(targetEl);
|
|
3879
3878
|
targetEl.innerHTML = buildCodePreviewHtmlWithCommentBlocks(text, editorLanguage || "");
|
|
3879
|
+
ensurePreviewSelectionActions(targetEl);
|
|
3880
3880
|
updatePreviewCommentBlocksForElement(targetEl);
|
|
3881
3881
|
if (pane === "response") {
|
|
3882
3882
|
applyPendingResponseScrollReset();
|
|
@@ -4447,10 +4447,9 @@
|
|
|
4447
4447
|
}
|
|
4448
4448
|
|
|
4449
4449
|
function supportsPreviewCommentsForCurrentEditor() {
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
return editorLanguage === "markdown" || supportsCodePreviewCommentsForCurrentEditor();
|
|
4450
|
+
return editorLanguage === "markdown"
|
|
4451
|
+
|| editorLanguage === "latex"
|
|
4452
|
+
|| supportsCodePreviewCommentsForCurrentEditor();
|
|
4454
4453
|
}
|
|
4455
4454
|
|
|
4456
4455
|
function getPreviewCommentBlockKindLabel(kind) {
|
|
@@ -4458,6 +4457,8 @@
|
|
|
4458
4457
|
if (kind === "blockquote") return "quote block";
|
|
4459
4458
|
if (kind === "list") return "list";
|
|
4460
4459
|
if (kind === "math") return "equation";
|
|
4460
|
+
if (kind === "figure") return "figure";
|
|
4461
|
+
if (kind === "algorithm") return "algorithm block";
|
|
4461
4462
|
if (kind === "page-break") return "page break";
|
|
4462
4463
|
if (kind === "code") return "code block";
|
|
4463
4464
|
if (kind === "table") return "table";
|
|
@@ -4670,6 +4671,389 @@
|
|
|
4670
4671
|
bodyText: String(range.bodyText || ""),
|
|
4671
4672
|
};
|
|
4672
4673
|
}
|
|
4674
|
+
|
|
4675
|
+
const LATEX_PREVIEW_HEADING_COMMANDS = new Set([
|
|
4676
|
+
"part",
|
|
4677
|
+
"chapter",
|
|
4678
|
+
"section",
|
|
4679
|
+
"subsection",
|
|
4680
|
+
"subsubsection",
|
|
4681
|
+
"paragraph",
|
|
4682
|
+
"subparagraph",
|
|
4683
|
+
]);
|
|
4684
|
+
const LATEX_PREVIEW_VISIBLE_GROUP_COMMANDS = new Set([
|
|
4685
|
+
"part",
|
|
4686
|
+
"chapter",
|
|
4687
|
+
"section",
|
|
4688
|
+
"subsection",
|
|
4689
|
+
"subsubsection",
|
|
4690
|
+
"paragraph",
|
|
4691
|
+
"subparagraph",
|
|
4692
|
+
"title",
|
|
4693
|
+
"author",
|
|
4694
|
+
"caption",
|
|
4695
|
+
"text",
|
|
4696
|
+
"textbf",
|
|
4697
|
+
"textit",
|
|
4698
|
+
"emph",
|
|
4699
|
+
"underline",
|
|
4700
|
+
"texttt",
|
|
4701
|
+
"textrm",
|
|
4702
|
+
"textsf",
|
|
4703
|
+
"textsc",
|
|
4704
|
+
"mbox",
|
|
4705
|
+
"makebox",
|
|
4706
|
+
"framebox",
|
|
4707
|
+
"fbox",
|
|
4708
|
+
"url",
|
|
4709
|
+
"path",
|
|
4710
|
+
"nolinkurl",
|
|
4711
|
+
]);
|
|
4712
|
+
const LATEX_PREVIEW_SECOND_ARG_VISIBLE_COMMANDS = new Set([
|
|
4713
|
+
"href",
|
|
4714
|
+
"hyperref",
|
|
4715
|
+
]);
|
|
4716
|
+
const LATEX_PREVIEW_HIDDEN_COMMANDS = new Set([
|
|
4717
|
+
"label",
|
|
4718
|
+
"ref",
|
|
4719
|
+
"eqref",
|
|
4720
|
+
"autoref",
|
|
4721
|
+
"pageref",
|
|
4722
|
+
"cite",
|
|
4723
|
+
"citet",
|
|
4724
|
+
"citep",
|
|
4725
|
+
"citealt",
|
|
4726
|
+
"citeauthor",
|
|
4727
|
+
"nocite",
|
|
4728
|
+
"footnote",
|
|
4729
|
+
"marginpar",
|
|
4730
|
+
"index",
|
|
4731
|
+
"includegraphics",
|
|
4732
|
+
"addbibresource",
|
|
4733
|
+
]);
|
|
4734
|
+
const LATEX_PREVIEW_SKIPPED_ENV_NAMES = new Set([
|
|
4735
|
+
"document",
|
|
4736
|
+
"thebibliography",
|
|
4737
|
+
"itemize",
|
|
4738
|
+
"enumerate",
|
|
4739
|
+
"description",
|
|
4740
|
+
"figure",
|
|
4741
|
+
"figure*",
|
|
4742
|
+
"table",
|
|
4743
|
+
"table*",
|
|
4744
|
+
"tabular",
|
|
4745
|
+
"tabular*",
|
|
4746
|
+
"theorem",
|
|
4747
|
+
"lemma",
|
|
4748
|
+
"proposition",
|
|
4749
|
+
"corollary",
|
|
4750
|
+
"definition",
|
|
4751
|
+
"proof",
|
|
4752
|
+
"remark",
|
|
4753
|
+
"example",
|
|
4754
|
+
"verbatim",
|
|
4755
|
+
"lstlisting",
|
|
4756
|
+
"minted",
|
|
4757
|
+
"algorithm",
|
|
4758
|
+
"algorithm*",
|
|
4759
|
+
"algorithmic",
|
|
4760
|
+
]);
|
|
4761
|
+
const LATEX_PREVIEW_STRUCTURAL_ENV_KIND_BY_NAME = new Map([
|
|
4762
|
+
["figure", "figure"],
|
|
4763
|
+
["figure*", "figure"],
|
|
4764
|
+
["table", "table"],
|
|
4765
|
+
["table*", "table"],
|
|
4766
|
+
["algorithm", "algorithm"],
|
|
4767
|
+
["algorithm*", "algorithm"],
|
|
4768
|
+
]);
|
|
4769
|
+
|
|
4770
|
+
function stripLatexPreviewComments(text) {
|
|
4771
|
+
const source = String(text || "");
|
|
4772
|
+
let out = "";
|
|
4773
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
4774
|
+
const ch = source[index];
|
|
4775
|
+
if (ch === "%" && !isEscapedAt(source, index)) {
|
|
4776
|
+
while (index < source.length && source[index] !== "\n") index += 1;
|
|
4777
|
+
if (index < source.length && source[index] === "\n") {
|
|
4778
|
+
out += "\n";
|
|
4779
|
+
}
|
|
4780
|
+
continue;
|
|
4781
|
+
}
|
|
4782
|
+
out += ch;
|
|
4783
|
+
}
|
|
4784
|
+
return out;
|
|
4785
|
+
}
|
|
4786
|
+
|
|
4787
|
+
function skipLatexPreviewCommentSpace(source, startIndex) {
|
|
4788
|
+
let index = Math.max(0, Number(startIndex) || 0);
|
|
4789
|
+
while (index < source.length) {
|
|
4790
|
+
const ch = source[index];
|
|
4791
|
+
if (/\s/.test(ch)) {
|
|
4792
|
+
index += 1;
|
|
4793
|
+
continue;
|
|
4794
|
+
}
|
|
4795
|
+
if (ch === "%" && !isEscapedAt(source, index)) {
|
|
4796
|
+
while (index < source.length && source[index] !== "\n") index += 1;
|
|
4797
|
+
continue;
|
|
4798
|
+
}
|
|
4799
|
+
break;
|
|
4800
|
+
}
|
|
4801
|
+
return index;
|
|
4802
|
+
}
|
|
4803
|
+
|
|
4804
|
+
function readLatexHeadingChunk(chunkText) {
|
|
4805
|
+
const source = String(chunkText || "");
|
|
4806
|
+
let index = skipLatexPreviewCommentSpace(source, 0);
|
|
4807
|
+
const command = parseLatexCommandAt(source, index);
|
|
4808
|
+
const commandName = command && command.name
|
|
4809
|
+
? String(command.name || "").replace(/\*$/, "").toLowerCase()
|
|
4810
|
+
: "";
|
|
4811
|
+
if (!command || !LATEX_PREVIEW_HEADING_COMMANDS.has(commandName)) return null;
|
|
4812
|
+
index = skipLatexPreviewCommentSpace(source, command.end);
|
|
4813
|
+
if (source[index] === "[") {
|
|
4814
|
+
const optionalGroup = readBalancedLatexGroup(source, index, "[", "]");
|
|
4815
|
+
if (optionalGroup) {
|
|
4816
|
+
index = skipLatexPreviewCommentSpace(source, optionalGroup.end);
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
if (source[index] !== "{") return null;
|
|
4820
|
+
const titleGroup = readBalancedLatexGroup(source, index, "{", "}");
|
|
4821
|
+
if (!titleGroup) return null;
|
|
4822
|
+
index = skipLatexPreviewCommentSpace(source, titleGroup.end);
|
|
4823
|
+
while (index < source.length) {
|
|
4824
|
+
const trailingCommand = parseLatexCommandAt(source, index);
|
|
4825
|
+
const trailingName = trailingCommand && trailingCommand.name
|
|
4826
|
+
? String(trailingCommand.name || "").replace(/\*$/, "").toLowerCase()
|
|
4827
|
+
: "";
|
|
4828
|
+
if (!trailingCommand || !LATEX_PREVIEW_HIDDEN_COMMANDS.has(trailingName)) {
|
|
4829
|
+
break;
|
|
4830
|
+
}
|
|
4831
|
+
let nextIndex = skipLatexPreviewCommentSpace(source, trailingCommand.end);
|
|
4832
|
+
if (source[nextIndex] === "[") {
|
|
4833
|
+
const optionalGroup = readBalancedLatexGroup(source, nextIndex, "[", "]");
|
|
4834
|
+
if (optionalGroup) {
|
|
4835
|
+
nextIndex = skipLatexPreviewCommentSpace(source, optionalGroup.end);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
if (source[nextIndex] === "{") {
|
|
4839
|
+
const argGroup = readBalancedLatexGroup(source, nextIndex, "{", "}");
|
|
4840
|
+
if (argGroup) {
|
|
4841
|
+
nextIndex = skipLatexPreviewCommentSpace(source, argGroup.end);
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
index = nextIndex;
|
|
4845
|
+
}
|
|
4846
|
+
if (skipLatexPreviewCommentSpace(source, index) < source.length) return null;
|
|
4847
|
+
return {
|
|
4848
|
+
commandName,
|
|
4849
|
+
titleText: source.slice(titleGroup.contentStart, titleGroup.contentEnd),
|
|
4850
|
+
};
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
function extractLatexPreviewVisibleText(text) {
|
|
4854
|
+
const source = String(text || "");
|
|
4855
|
+
let out = "";
|
|
4856
|
+
let index = 0;
|
|
4857
|
+
|
|
4858
|
+
while (index < source.length) {
|
|
4859
|
+
const ch = source[index];
|
|
4860
|
+
if (ch === "%" && !isEscapedAt(source, index)) {
|
|
4861
|
+
while (index < source.length && source[index] !== "\n") index += 1;
|
|
4862
|
+
continue;
|
|
4863
|
+
}
|
|
4864
|
+
if (source.startsWith("$$", index)) {
|
|
4865
|
+
const close = source.indexOf("$$", index + 2);
|
|
4866
|
+
if (close >= 0) {
|
|
4867
|
+
out += " " + source.slice(index + 2, close) + " ";
|
|
4868
|
+
index = close + 2;
|
|
4869
|
+
continue;
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
if (ch === "$" && !isEscapedAt(source, index)) {
|
|
4873
|
+
const close = findClosingUnescapedSequence(source, index + 1, "$", true);
|
|
4874
|
+
if (close >= 0) {
|
|
4875
|
+
out += " " + source.slice(index + 1, close) + " ";
|
|
4876
|
+
index = close + 1;
|
|
4877
|
+
continue;
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4880
|
+
if (source.startsWith("\\(", index)) {
|
|
4881
|
+
const close = source.indexOf("\\)", index + 2);
|
|
4882
|
+
if (close >= 0) {
|
|
4883
|
+
out += " " + source.slice(index + 2, close) + " ";
|
|
4884
|
+
index = close + 2;
|
|
4885
|
+
continue;
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
if (source.startsWith("\\[", index)) {
|
|
4889
|
+
const close = source.indexOf("\\]", index + 2);
|
|
4890
|
+
if (close >= 0) {
|
|
4891
|
+
out += " " + source.slice(index + 2, close) + " ";
|
|
4892
|
+
index = close + 2;
|
|
4893
|
+
continue;
|
|
4894
|
+
}
|
|
4895
|
+
}
|
|
4896
|
+
if (source.startsWith("\\begin{", index)) {
|
|
4897
|
+
const envGroup = readBalancedLatexGroup(source, index + 6, "{", "}");
|
|
4898
|
+
const envName = envGroup ? source.slice(envGroup.contentStart, envGroup.contentEnd).trim() : "";
|
|
4899
|
+
if (envName && DISPLAY_MATH_ENV_NAMES.has(envName)) {
|
|
4900
|
+
const closeToken = "\\end{" + envName + "}";
|
|
4901
|
+
const close = source.indexOf(closeToken, envGroup.end);
|
|
4902
|
+
if (close >= 0) {
|
|
4903
|
+
out += " " + source.slice(envGroup.end, close) + " ";
|
|
4904
|
+
index = close + closeToken.length;
|
|
4905
|
+
continue;
|
|
4906
|
+
}
|
|
4907
|
+
}
|
|
4908
|
+
}
|
|
4909
|
+
if (source.startsWith("\\end{", index)) {
|
|
4910
|
+
const envGroup = readBalancedLatexGroup(source, index + 4, "{", "}");
|
|
4911
|
+
if (envGroup) {
|
|
4912
|
+
index = envGroup.end;
|
|
4913
|
+
continue;
|
|
4914
|
+
}
|
|
4915
|
+
}
|
|
4916
|
+
if (ch === "\\") {
|
|
4917
|
+
const command = parseLatexCommandAt(source, index);
|
|
4918
|
+
const commandName = command && command.name
|
|
4919
|
+
? String(command.name || "").replace(/\*$/, "").toLowerCase()
|
|
4920
|
+
: "";
|
|
4921
|
+
if (!command) {
|
|
4922
|
+
index += 1;
|
|
4923
|
+
continue;
|
|
4924
|
+
}
|
|
4925
|
+
if (commandName === "begin" || commandName === "end") {
|
|
4926
|
+
let nextIndex = skipLatexWhitespace(source, command.end);
|
|
4927
|
+
if (source[nextIndex] === "{") {
|
|
4928
|
+
const group = readBalancedLatexGroup(source, nextIndex, "{", "}");
|
|
4929
|
+
if (group) {
|
|
4930
|
+
index = group.end;
|
|
4931
|
+
continue;
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
if (commandName === "latex") {
|
|
4936
|
+
out += "LaTeX";
|
|
4937
|
+
index = command.end;
|
|
4938
|
+
continue;
|
|
4939
|
+
}
|
|
4940
|
+
if (commandName === "tex") {
|
|
4941
|
+
out += "TeX";
|
|
4942
|
+
index = command.end;
|
|
4943
|
+
continue;
|
|
4944
|
+
}
|
|
4945
|
+
if (commandName === "item") {
|
|
4946
|
+
out += " ";
|
|
4947
|
+
index = command.end;
|
|
4948
|
+
continue;
|
|
4949
|
+
}
|
|
4950
|
+
let nextIndex = skipLatexWhitespace(source, command.end);
|
|
4951
|
+
if (source[nextIndex] === "[") {
|
|
4952
|
+
const optionalGroup = readBalancedLatexGroup(source, nextIndex, "[", "]");
|
|
4953
|
+
if (optionalGroup) {
|
|
4954
|
+
nextIndex = skipLatexWhitespace(source, optionalGroup.end);
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4957
|
+
if (LATEX_PREVIEW_VISIBLE_GROUP_COMMANDS.has(commandName) && source[nextIndex] === "{") {
|
|
4958
|
+
const group = readBalancedLatexGroup(source, nextIndex, "{", "}");
|
|
4959
|
+
if (group) {
|
|
4960
|
+
out += " " + extractLatexPreviewVisibleText(source.slice(group.contentStart, group.contentEnd)) + " ";
|
|
4961
|
+
index = group.end;
|
|
4962
|
+
continue;
|
|
4963
|
+
}
|
|
4964
|
+
}
|
|
4965
|
+
if (LATEX_PREVIEW_SECOND_ARG_VISIBLE_COMMANDS.has(commandName) && source[nextIndex] === "{") {
|
|
4966
|
+
const firstGroup = readBalancedLatexGroup(source, nextIndex, "{", "}");
|
|
4967
|
+
if (firstGroup) {
|
|
4968
|
+
let secondIndex = skipLatexWhitespace(source, firstGroup.end);
|
|
4969
|
+
if (source[secondIndex] === "{") {
|
|
4970
|
+
const secondGroup = readBalancedLatexGroup(source, secondIndex, "{", "}");
|
|
4971
|
+
if (secondGroup) {
|
|
4972
|
+
out += " " + extractLatexPreviewVisibleText(source.slice(secondGroup.contentStart, secondGroup.contentEnd)) + " ";
|
|
4973
|
+
index = secondGroup.end;
|
|
4974
|
+
continue;
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4979
|
+
if (LATEX_PREVIEW_HIDDEN_COMMANDS.has(commandName)) {
|
|
4980
|
+
index = nextIndex;
|
|
4981
|
+
if (source[index] === "{") {
|
|
4982
|
+
const group = readBalancedLatexGroup(source, index, "{", "}");
|
|
4983
|
+
if (group) {
|
|
4984
|
+
index = group.end;
|
|
4985
|
+
continue;
|
|
4986
|
+
}
|
|
4987
|
+
}
|
|
4988
|
+
index = command.end;
|
|
4989
|
+
continue;
|
|
4990
|
+
}
|
|
4991
|
+
index = command.end;
|
|
4992
|
+
continue;
|
|
4993
|
+
}
|
|
4994
|
+
if (ch === "{" || ch === "}") {
|
|
4995
|
+
index += 1;
|
|
4996
|
+
continue;
|
|
4997
|
+
}
|
|
4998
|
+
if (ch === "~") {
|
|
4999
|
+
out += " ";
|
|
5000
|
+
index += 1;
|
|
5001
|
+
continue;
|
|
5002
|
+
}
|
|
5003
|
+
out += ch;
|
|
5004
|
+
index += 1;
|
|
5005
|
+
}
|
|
5006
|
+
|
|
5007
|
+
return normalizeVisiblePreviewText(out);
|
|
5008
|
+
}
|
|
5009
|
+
|
|
5010
|
+
function findLatexDocumentBodyRange(text) {
|
|
5011
|
+
const source = String(text || "");
|
|
5012
|
+
const beginMatch = source.match(/\\begin\{document\}/);
|
|
5013
|
+
if (!beginMatch || beginMatch.index == null) {
|
|
5014
|
+
return { start: 0, end: source.length };
|
|
5015
|
+
}
|
|
5016
|
+
const start = beginMatch.index + beginMatch[0].length;
|
|
5017
|
+
const endMatch = source.slice(start).match(/\\end\{document\}/);
|
|
5018
|
+
return {
|
|
5019
|
+
start,
|
|
5020
|
+
end: endMatch && endMatch.index != null ? (start + endMatch.index) : source.length,
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5023
|
+
|
|
5024
|
+
function normalizeLatexPreviewBlockText(blockText, kind) {
|
|
5025
|
+
const source = String(blockText || "");
|
|
5026
|
+
if (/\\(?:bibliography|printbibliography)\b/i.test(source)) {
|
|
5027
|
+
return kind === "heading" ? "References" : "references";
|
|
5028
|
+
}
|
|
5029
|
+
if (kind === "math") {
|
|
5030
|
+
const mathRange = getStandaloneDisplayMathRange(stripLatexPreviewComments(source));
|
|
5031
|
+
return mathRange ? normalizeVisiblePreviewText(mathRange.bodyText) : normalizeVisiblePreviewText(source);
|
|
5032
|
+
}
|
|
5033
|
+
if (kind === "heading") {
|
|
5034
|
+
const heading = readLatexHeadingChunk(stripLatexPreviewComments(source));
|
|
5035
|
+
return heading ? extractLatexPreviewVisibleText(heading.titleText) : extractLatexPreviewVisibleText(source);
|
|
5036
|
+
}
|
|
5037
|
+
return extractLatexPreviewVisibleText(source);
|
|
5038
|
+
}
|
|
5039
|
+
|
|
5040
|
+
function isLatexPreviewSkippableChunk(chunkText) {
|
|
5041
|
+
const source = stripLatexPreviewComments(chunkText).trim();
|
|
5042
|
+
if (!source) return true;
|
|
5043
|
+
const command = parseLatexCommandAt(source, 0);
|
|
5044
|
+
const commandName = command && command.name
|
|
5045
|
+
? String(command.name || "").replace(/\*$/, "").toLowerCase()
|
|
5046
|
+
: "";
|
|
5047
|
+
if (command && LATEX_PREVIEW_HIDDEN_COMMANDS.has(commandName)) return true;
|
|
5048
|
+
if (command && /^(?:documentclass|usepackage|newtheorem|title|author|date|maketitle|tableofcontents)$/i.test(commandName)) return true;
|
|
5049
|
+
if (source.startsWith("\\begin{")) {
|
|
5050
|
+
const envGroup = readBalancedLatexGroup(source, 6, "{", "}");
|
|
5051
|
+
const envName = envGroup ? source.slice(envGroup.contentStart, envGroup.contentEnd).trim().toLowerCase() : "";
|
|
5052
|
+
if (envName && LATEX_PREVIEW_SKIPPED_ENV_NAMES.has(envName)) return true;
|
|
5053
|
+
}
|
|
5054
|
+
return false;
|
|
5055
|
+
}
|
|
5056
|
+
|
|
4673
5057
|
function normalizePreviewComparableCharacter(character) {
|
|
4674
5058
|
switch (String(character || "")) {
|
|
4675
5059
|
case "\u2018":
|
|
@@ -5102,12 +5486,12 @@
|
|
|
5102
5486
|
|
|
5103
5487
|
function getPreviewMathSearchText(element) {
|
|
5104
5488
|
if (!element || !(element instanceof Element)) return null;
|
|
5489
|
+
const texSourceAttr = element.getAttribute("data-tex-source");
|
|
5490
|
+
if (texSourceAttr && texSourceAttr.trim()) {
|
|
5491
|
+
return texSourceAttr;
|
|
5492
|
+
}
|
|
5105
5493
|
const tag = element.tagName ? element.tagName.toUpperCase() : "";
|
|
5106
5494
|
if (tag === "MATH") {
|
|
5107
|
-
const texSource = element.getAttribute("data-tex-source");
|
|
5108
|
-
if (texSource && texSource.trim()) {
|
|
5109
|
-
return texSource;
|
|
5110
|
-
}
|
|
5111
5495
|
return typeof element.textContent === "string" ? element.textContent : "";
|
|
5112
5496
|
}
|
|
5113
5497
|
if (element.classList && element.classList.contains("math") && (element.classList.contains("inline") || element.classList.contains("display"))) {
|
|
@@ -5116,6 +5500,16 @@
|
|
|
5116
5500
|
element.classList.contains("display"),
|
|
5117
5501
|
);
|
|
5118
5502
|
}
|
|
5503
|
+
if (
|
|
5504
|
+
element.classList
|
|
5505
|
+
&& (element.classList.contains("studio-display-equation") || element.classList.contains("studio-display-equation-body"))
|
|
5506
|
+
&& typeof element.querySelector === "function"
|
|
5507
|
+
) {
|
|
5508
|
+
const innerMathEl = element.querySelector("[data-tex-source], math[display='block'], .studio-mathjax-fallback-display");
|
|
5509
|
+
if (innerMathEl && innerMathEl !== element) {
|
|
5510
|
+
return getPreviewMathSearchText(innerMathEl);
|
|
5511
|
+
}
|
|
5512
|
+
}
|
|
5119
5513
|
return null;
|
|
5120
5514
|
}
|
|
5121
5515
|
|
|
@@ -5179,8 +5573,23 @@
|
|
|
5179
5573
|
return bestIndex;
|
|
5180
5574
|
}
|
|
5181
5575
|
|
|
5576
|
+
function buildLiteralPreviewDisplayMap(text, rawOffsets) {
|
|
5577
|
+
const source = String(text || "");
|
|
5578
|
+
const rawMap = Array.isArray(rawOffsets) ? rawOffsets : [];
|
|
5579
|
+
const charStarts = [];
|
|
5580
|
+
const charEnds = [];
|
|
5581
|
+
for (let i = 0; i < source.length; i += 1) {
|
|
5582
|
+
charStarts.push(rawMap[i]);
|
|
5583
|
+
charEnds.push(rawMap[i] + 1);
|
|
5584
|
+
}
|
|
5585
|
+
return buildNormalizedPreviewDisplayMap(source, charStarts, charEnds);
|
|
5586
|
+
}
|
|
5587
|
+
|
|
5182
5588
|
function buildPreviewSelectionDisplayMap(blockText, kind) {
|
|
5183
5589
|
const body = buildPreviewSelectionSourceBody(blockText, kind);
|
|
5590
|
+
if (kind === "code-line" || kind === "diff-line" || kind === "text-line") {
|
|
5591
|
+
return buildLiteralPreviewDisplayMap(body.text, body.rawOffsets);
|
|
5592
|
+
}
|
|
5184
5593
|
const inlineMap = buildPreviewInlineDisplayMap(body.text, body.rawOffsets);
|
|
5185
5594
|
return buildNormalizedPreviewDisplayMap(inlineMap.text, inlineMap.charStarts, inlineMap.charEnds);
|
|
5186
5595
|
}
|
|
@@ -5197,6 +5606,7 @@
|
|
|
5197
5606
|
function getPreviewCommentSelectionKey(selection) {
|
|
5198
5607
|
if (!selection) return "";
|
|
5199
5608
|
return [
|
|
5609
|
+
String(selection.paneId || ""),
|
|
5200
5610
|
String(selection.blockKey || ""),
|
|
5201
5611
|
String(selection.selectionStart || 0),
|
|
5202
5612
|
String(selection.selectionEnd || 0),
|
|
@@ -5224,6 +5634,96 @@
|
|
|
5224
5634
|
: null;
|
|
5225
5635
|
}
|
|
5226
5636
|
|
|
5637
|
+
function getPreviewSelectionPaneIdForNode(node) {
|
|
5638
|
+
if (!node) return "";
|
|
5639
|
+
const element = node instanceof Element ? node : node.parentElement;
|
|
5640
|
+
const paneEl = element && typeof element.closest === "function"
|
|
5641
|
+
? element.closest("#sourcePreview, #critiqueView")
|
|
5642
|
+
: null;
|
|
5643
|
+
return paneEl && paneEl.id ? String(paneEl.id) : "";
|
|
5644
|
+
}
|
|
5645
|
+
|
|
5646
|
+
function getPreviewSelectionPaneElement(paneId) {
|
|
5647
|
+
if (paneId === "sourcePreview") return sourcePreviewEl;
|
|
5648
|
+
if (paneId === "critiqueView") return critiqueViewEl;
|
|
5649
|
+
return null;
|
|
5650
|
+
}
|
|
5651
|
+
|
|
5652
|
+
function getActivePreviewSelectionForPane(paneId) {
|
|
5653
|
+
if (!paneId) return null;
|
|
5654
|
+
return activePreviewCommentSelection && activePreviewCommentSelection.paneId === paneId
|
|
5655
|
+
? activePreviewCommentSelection
|
|
5656
|
+
: null;
|
|
5657
|
+
}
|
|
5658
|
+
|
|
5659
|
+
function ensurePreviewSelectionActions(targetEl) {
|
|
5660
|
+
if (!targetEl || typeof document.createElement !== "function") return null;
|
|
5661
|
+
const paneId = targetEl.id ? String(targetEl.id) : "";
|
|
5662
|
+
if (!paneId) return null;
|
|
5663
|
+
const existing = Array.from(targetEl.children || []).find((child) => child.classList && child.classList.contains("preview-selection-actions"));
|
|
5664
|
+
if (existing) {
|
|
5665
|
+
existing.dataset.previewPane = paneId;
|
|
5666
|
+
return existing;
|
|
5667
|
+
}
|
|
5668
|
+
|
|
5669
|
+
const actionsEl = document.createElement("div");
|
|
5670
|
+
actionsEl.className = "preview-selection-actions";
|
|
5671
|
+
actionsEl.dataset.previewPane = paneId;
|
|
5672
|
+
actionsEl.hidden = true;
|
|
5673
|
+
|
|
5674
|
+
const commentBtn = document.createElement("button");
|
|
5675
|
+
commentBtn.type = "button";
|
|
5676
|
+
commentBtn.className = "preview-comment-add";
|
|
5677
|
+
commentBtn.dataset.previewCommentAction = "comment";
|
|
5678
|
+
commentBtn.textContent = "Comment";
|
|
5679
|
+
commentBtn.hidden = true;
|
|
5680
|
+
actionsEl.appendChild(commentBtn);
|
|
5681
|
+
|
|
5682
|
+
const jumpBtn = document.createElement("button");
|
|
5683
|
+
jumpBtn.type = "button";
|
|
5684
|
+
jumpBtn.className = "preview-comment-jump";
|
|
5685
|
+
jumpBtn.dataset.previewCommentAction = "jump";
|
|
5686
|
+
jumpBtn.textContent = "Jump";
|
|
5687
|
+
jumpBtn.hidden = true;
|
|
5688
|
+
actionsEl.appendChild(jumpBtn);
|
|
5689
|
+
|
|
5690
|
+
targetEl.insertBefore(actionsEl, targetEl.firstChild || null);
|
|
5691
|
+
return actionsEl;
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5694
|
+
function updatePreviewSelectionActions(targetEl) {
|
|
5695
|
+
if (!targetEl) return;
|
|
5696
|
+
const actionsEl = ensurePreviewSelectionActions(targetEl);
|
|
5697
|
+
if (!actionsEl) return;
|
|
5698
|
+
const paneId = targetEl.id ? String(targetEl.id) : "";
|
|
5699
|
+
const selection = getActivePreviewSelectionForPane(paneId);
|
|
5700
|
+
const commentBtn = actionsEl.querySelector(".preview-comment-add");
|
|
5701
|
+
const jumpBtn = actionsEl.querySelector(".preview-comment-jump");
|
|
5702
|
+
if (!selection) {
|
|
5703
|
+
actionsEl.hidden = true;
|
|
5704
|
+
if (commentBtn) commentBtn.hidden = true;
|
|
5705
|
+
if (jumpBtn) jumpBtn.hidden = true;
|
|
5706
|
+
return;
|
|
5707
|
+
}
|
|
5708
|
+
const lineLabel = summarizeReviewNoteAnchor(selection).toLowerCase();
|
|
5709
|
+
const blockKindLabel = getPreviewCommentBlockKindLabel(selection.previewCommentKind || "paragraph");
|
|
5710
|
+
actionsEl.hidden = false;
|
|
5711
|
+
if (commentBtn) {
|
|
5712
|
+
commentBtn.hidden = false;
|
|
5713
|
+
commentBtn.dataset.previewCommentMode = "selection";
|
|
5714
|
+
commentBtn.dataset.previewPane = paneId;
|
|
5715
|
+
commentBtn.title = "Add a local comment from the current preview selection on this " + blockKindLabel + " (" + lineLabel + ").";
|
|
5716
|
+
commentBtn.setAttribute("aria-label", commentBtn.title || "Comment");
|
|
5717
|
+
}
|
|
5718
|
+
if (jumpBtn) {
|
|
5719
|
+
jumpBtn.hidden = false;
|
|
5720
|
+
jumpBtn.dataset.previewCommentMode = "selection";
|
|
5721
|
+
jumpBtn.dataset.previewPane = paneId;
|
|
5722
|
+
jumpBtn.title = "Jump to the current preview selection on this " + blockKindLabel + " in the raw editor (" + lineLabel + ").";
|
|
5723
|
+
jumpBtn.setAttribute("aria-label", jumpBtn.title || "Jump");
|
|
5724
|
+
}
|
|
5725
|
+
}
|
|
5726
|
+
|
|
5227
5727
|
function unwrapPreviewJumpHighlightElement(element) {
|
|
5228
5728
|
if (!element || !element.parentNode) return;
|
|
5229
5729
|
const parent = element.parentNode;
|
|
@@ -5277,8 +5777,9 @@
|
|
|
5277
5777
|
}
|
|
5278
5778
|
|
|
5279
5779
|
function scanSourcePreviewCommentBlocks(markdown) {
|
|
5280
|
-
if (editorLanguage
|
|
5281
|
-
return
|
|
5780
|
+
if (editorLanguage === "markdown") return scanMarkdownPreviewCommentBlocks(markdown);
|
|
5781
|
+
if (editorLanguage === "latex") return scanLatexPreviewCommentBlocks(markdown);
|
|
5782
|
+
return [];
|
|
5282
5783
|
}
|
|
5283
5784
|
|
|
5284
5785
|
function scanMarkdownPreviewCommentBlocks(markdown) {
|
|
@@ -5497,8 +5998,199 @@
|
|
|
5497
5998
|
return expandSourcePreviewCommentBlocksByDisplayMath(source, blocks);
|
|
5498
5999
|
}
|
|
5499
6000
|
|
|
6001
|
+
function scanLatexPreviewCommentBlocks(markdown) {
|
|
6002
|
+
const source = String(markdown || "").replace(/\r\n/g, "\n");
|
|
6003
|
+
if (!source) return [];
|
|
6004
|
+
const bodyRange = findLatexDocumentBodyRange(source);
|
|
6005
|
+
const bodyStart = Math.max(0, Math.min(bodyRange.start, source.length));
|
|
6006
|
+
const bodyEnd = Math.max(bodyStart, Math.min(bodyRange.end, source.length));
|
|
6007
|
+
const bodyText = source.slice(bodyStart, bodyEnd);
|
|
6008
|
+
const lines = bodyText.split("\n");
|
|
6009
|
+
const lineOffsets = [];
|
|
6010
|
+
let runningOffset = 0;
|
|
6011
|
+
for (const line of lines) {
|
|
6012
|
+
lineOffsets.push(runningOffset);
|
|
6013
|
+
runningOffset += line.length + 1;
|
|
6014
|
+
}
|
|
6015
|
+
|
|
6016
|
+
function getLine(index) {
|
|
6017
|
+
return index >= 0 && index < lines.length ? String(lines[index] || "") : "";
|
|
6018
|
+
}
|
|
6019
|
+
|
|
6020
|
+
function getStrippedLine(index) {
|
|
6021
|
+
return stripLatexPreviewComments(getLine(index)).trim();
|
|
6022
|
+
}
|
|
6023
|
+
|
|
6024
|
+
function isBlankLine(index) {
|
|
6025
|
+
return !getStrippedLine(index);
|
|
6026
|
+
}
|
|
6027
|
+
|
|
6028
|
+
function isBibliographyCommandLine(index) {
|
|
6029
|
+
return /^\\(?:bibliographystyle|bibliography|printbibliography)\b/i.test(getStrippedLine(index));
|
|
6030
|
+
}
|
|
6031
|
+
|
|
6032
|
+
function makeBlock(kind, startLineIndex, endLineIndex) {
|
|
6033
|
+
const safeStartLine = Math.max(0, Math.min(startLineIndex, Math.max(0, lines.length - 1)));
|
|
6034
|
+
const safeEndLine = Math.max(safeStartLine, Math.min(endLineIndex, Math.max(0, lines.length - 1)));
|
|
6035
|
+
const start = bodyStart + (lineOffsets[safeStartLine] || 0);
|
|
6036
|
+
const end = bodyStart + (lineOffsets[safeEndLine] || 0) + getLine(safeEndLine).length;
|
|
6037
|
+
return {
|
|
6038
|
+
kind,
|
|
6039
|
+
start,
|
|
6040
|
+
end,
|
|
6041
|
+
lineStart: getLineNumberAtOffset(source, start),
|
|
6042
|
+
lineEnd: getLineNumberAtOffset(source, Math.max(start, end - 1)),
|
|
6043
|
+
};
|
|
6044
|
+
}
|
|
6045
|
+
|
|
6046
|
+
function getChunkText(startLineIndex, endLineIndex) {
|
|
6047
|
+
return bodyText.slice(
|
|
6048
|
+
lineOffsets[startLineIndex] || 0,
|
|
6049
|
+
(lineOffsets[endLineIndex] || 0) + getLine(endLineIndex).length,
|
|
6050
|
+
);
|
|
6051
|
+
}
|
|
6052
|
+
|
|
6053
|
+
function getEnvironmentStartName(index) {
|
|
6054
|
+
const line = getStrippedLine(index);
|
|
6055
|
+
const match = line.match(/^\\begin\{([^}]+)\}/);
|
|
6056
|
+
return match ? String(match[1] || "").trim().toLowerCase() : "";
|
|
6057
|
+
}
|
|
6058
|
+
|
|
6059
|
+
function findEnvironmentEndLine(startLineIndex, envName) {
|
|
6060
|
+
const openToken = "\\begin{" + envName + "}";
|
|
6061
|
+
const closeToken = "\\end{" + envName + "}";
|
|
6062
|
+
let depth = 0;
|
|
6063
|
+
for (let lineIndex = startLineIndex; lineIndex < lines.length; lineIndex += 1) {
|
|
6064
|
+
const line = getStrippedLine(lineIndex);
|
|
6065
|
+
if (line.includes(openToken)) depth += 1;
|
|
6066
|
+
if (line.includes(closeToken)) {
|
|
6067
|
+
depth -= 1;
|
|
6068
|
+
if (depth <= 0) return lineIndex;
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
return startLineIndex;
|
|
6072
|
+
}
|
|
6073
|
+
|
|
6074
|
+
function isHeadingLine(index) {
|
|
6075
|
+
return Boolean(readLatexHeadingChunk(getLine(index)));
|
|
6076
|
+
}
|
|
6077
|
+
|
|
6078
|
+
function findBibliographyCommandEndLine(startLineIndex) {
|
|
6079
|
+
let endLineIndex = startLineIndex;
|
|
6080
|
+
for (let lineIndex = startLineIndex + 1; lineIndex < lines.length; lineIndex += 1) {
|
|
6081
|
+
if (!isBibliographyCommandLine(lineIndex)) break;
|
|
6082
|
+
endLineIndex = lineIndex;
|
|
6083
|
+
}
|
|
6084
|
+
return endLineIndex;
|
|
6085
|
+
}
|
|
6086
|
+
|
|
6087
|
+
function isMathStartLine(index) {
|
|
6088
|
+
const line = getStrippedLine(index);
|
|
6089
|
+
if (!line) return false;
|
|
6090
|
+
if (line.startsWith("$$") || line.startsWith("\\[")) return true;
|
|
6091
|
+
const envName = getEnvironmentStartName(index);
|
|
6092
|
+
return Boolean(envName && DISPLAY_MATH_ENV_NAMES.has(envName));
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6095
|
+
function findMathEndLine(startLineIndex) {
|
|
6096
|
+
for (let endLineIndex = startLineIndex; endLineIndex < lines.length; endLineIndex += 1) {
|
|
6097
|
+
const chunkText = getChunkText(startLineIndex, endLineIndex);
|
|
6098
|
+
if (getStandaloneDisplayMathRange(stripLatexPreviewComments(chunkText))) {
|
|
6099
|
+
return endLineIndex;
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
return startLineIndex;
|
|
6103
|
+
}
|
|
6104
|
+
|
|
6105
|
+
const blocks = [];
|
|
6106
|
+
let lineIndex = 0;
|
|
6107
|
+
while (lineIndex < lines.length) {
|
|
6108
|
+
if (isBlankLine(lineIndex)) {
|
|
6109
|
+
lineIndex += 1;
|
|
6110
|
+
continue;
|
|
6111
|
+
}
|
|
6112
|
+
|
|
6113
|
+
const strippedLine = getStrippedLine(lineIndex);
|
|
6114
|
+
const envName = getEnvironmentStartName(lineIndex);
|
|
6115
|
+
|
|
6116
|
+
if (isHeadingLine(lineIndex)) {
|
|
6117
|
+
blocks.push(makeBlock("heading", lineIndex, lineIndex));
|
|
6118
|
+
lineIndex += 1;
|
|
6119
|
+
continue;
|
|
6120
|
+
}
|
|
6121
|
+
|
|
6122
|
+
if (envName === "abstract" || envName === "keywords") {
|
|
6123
|
+
const endLineIndex = findEnvironmentEndLine(lineIndex, envName);
|
|
6124
|
+
const chunkText = getChunkText(lineIndex, endLineIndex);
|
|
6125
|
+
if (normalizeLatexPreviewBlockText(chunkText, "paragraph")) {
|
|
6126
|
+
blocks.push(makeBlock("paragraph", lineIndex, endLineIndex));
|
|
6127
|
+
}
|
|
6128
|
+
lineIndex = endLineIndex + 1;
|
|
6129
|
+
continue;
|
|
6130
|
+
}
|
|
6131
|
+
|
|
6132
|
+
if (envName && LATEX_PREVIEW_STRUCTURAL_ENV_KIND_BY_NAME.has(envName)) {
|
|
6133
|
+
const endLineIndex = findEnvironmentEndLine(lineIndex, envName);
|
|
6134
|
+
blocks.push(makeBlock(LATEX_PREVIEW_STRUCTURAL_ENV_KIND_BY_NAME.get(envName) || "paragraph", lineIndex, endLineIndex));
|
|
6135
|
+
lineIndex = endLineIndex + 1;
|
|
6136
|
+
continue;
|
|
6137
|
+
}
|
|
6138
|
+
|
|
6139
|
+
if (isBibliographyCommandLine(lineIndex)) {
|
|
6140
|
+
const endLineIndex = findBibliographyCommandEndLine(lineIndex);
|
|
6141
|
+
blocks.push(makeBlock("heading", lineIndex, endLineIndex));
|
|
6142
|
+
blocks.push(makeBlock("paragraph", lineIndex, endLineIndex));
|
|
6143
|
+
lineIndex = endLineIndex + 1;
|
|
6144
|
+
continue;
|
|
6145
|
+
}
|
|
6146
|
+
|
|
6147
|
+
if (envName && LATEX_PREVIEW_SKIPPED_ENV_NAMES.has(envName) && !DISPLAY_MATH_ENV_NAMES.has(envName)) {
|
|
6148
|
+
lineIndex = findEnvironmentEndLine(lineIndex, envName) + 1;
|
|
6149
|
+
continue;
|
|
6150
|
+
}
|
|
6151
|
+
|
|
6152
|
+
if (isMathStartLine(lineIndex)) {
|
|
6153
|
+
const endLineIndex = findMathEndLine(lineIndex);
|
|
6154
|
+
blocks.push(makeBlock("math", lineIndex, endLineIndex));
|
|
6155
|
+
lineIndex = endLineIndex + 1;
|
|
6156
|
+
continue;
|
|
6157
|
+
}
|
|
6158
|
+
|
|
6159
|
+
if (isLatexPreviewSkippableChunk(strippedLine)) {
|
|
6160
|
+
lineIndex += 1;
|
|
6161
|
+
continue;
|
|
6162
|
+
}
|
|
6163
|
+
|
|
6164
|
+
const paragraphStartLine = lineIndex;
|
|
6165
|
+
let paragraphEndLine = lineIndex;
|
|
6166
|
+
for (let nextLineIndex = lineIndex + 1; nextLineIndex < lines.length; nextLineIndex += 1) {
|
|
6167
|
+
if (isBlankLine(nextLineIndex) || isHeadingLine(nextLineIndex) || isMathStartLine(nextLineIndex)) {
|
|
6168
|
+
break;
|
|
6169
|
+
}
|
|
6170
|
+
const nextEnvName = getEnvironmentStartName(nextLineIndex);
|
|
6171
|
+
if (nextEnvName) {
|
|
6172
|
+
break;
|
|
6173
|
+
}
|
|
6174
|
+
paragraphEndLine = nextLineIndex;
|
|
6175
|
+
}
|
|
6176
|
+
|
|
6177
|
+
const chunkText = getChunkText(paragraphStartLine, paragraphEndLine);
|
|
6178
|
+
if (normalizeLatexPreviewBlockText(chunkText, "paragraph") && !isLatexPreviewSkippableChunk(chunkText)) {
|
|
6179
|
+
blocks.push(makeBlock("paragraph", paragraphStartLine, paragraphEndLine));
|
|
6180
|
+
}
|
|
6181
|
+
lineIndex = paragraphEndLine + 1;
|
|
6182
|
+
}
|
|
6183
|
+
|
|
6184
|
+
return blocks;
|
|
6185
|
+
}
|
|
6186
|
+
|
|
5500
6187
|
function isPreviewDisplayMathElement(element) {
|
|
5501
|
-
return Boolean(
|
|
6188
|
+
return Boolean(
|
|
6189
|
+
element
|
|
6190
|
+
&& element instanceof Element
|
|
6191
|
+
&& element.matches
|
|
6192
|
+
&& element.matches("math[display='block'], .studio-mathjax-fallback-display, .studio-display-equation, .studio-display-equation-body")
|
|
6193
|
+
);
|
|
5502
6194
|
}
|
|
5503
6195
|
|
|
5504
6196
|
function previewNodesHaveVisibleContent(nodes) {
|
|
@@ -5511,8 +6203,63 @@
|
|
|
5511
6203
|
});
|
|
5512
6204
|
}
|
|
5513
6205
|
|
|
6206
|
+
function wrapLoosePreviewInlineRunsAsParagraphs(targetEl) {
|
|
6207
|
+
if (!targetEl || !targetEl.childNodes || typeof document.createElement !== "function") return;
|
|
6208
|
+
const childNodes = Array.from(targetEl.childNodes || []);
|
|
6209
|
+
if (childNodes.length === 0) return;
|
|
6210
|
+
|
|
6211
|
+
function isDirectBlockChild(node) {
|
|
6212
|
+
if (!(node instanceof Element) || node.parentElement !== targetEl) return false;
|
|
6213
|
+
const tag = node.tagName ? node.tagName.toUpperCase() : "";
|
|
6214
|
+
if (/^H[1-6]$/.test(tag)) return true;
|
|
6215
|
+
if (tag === "P" || tag === "BLOCKQUOTE" || tag === "UL" || tag === "OL" || tag === "TABLE" || tag === "PRE" || tag === "HEADER" || tag === "FIGURE") {
|
|
6216
|
+
return true;
|
|
6217
|
+
}
|
|
6218
|
+
if (tag === "MATH") {
|
|
6219
|
+
return String(node.getAttribute("display") || "").toLowerCase() === "block";
|
|
6220
|
+
}
|
|
6221
|
+
if (tag === "DIV") return true;
|
|
6222
|
+
return false;
|
|
6223
|
+
}
|
|
6224
|
+
|
|
6225
|
+
let runNodes = [];
|
|
6226
|
+
|
|
6227
|
+
function flushRun(referenceNode) {
|
|
6228
|
+
if (runNodes.length === 0) return;
|
|
6229
|
+
if (!previewNodesHaveVisibleContent(runNodes)) {
|
|
6230
|
+
runNodes.forEach((node) => {
|
|
6231
|
+
if (node && node.parentNode === targetEl) {
|
|
6232
|
+
targetEl.removeChild(node);
|
|
6233
|
+
}
|
|
6234
|
+
});
|
|
6235
|
+
runNodes = [];
|
|
6236
|
+
return;
|
|
6237
|
+
}
|
|
6238
|
+
const paragraphEl = document.createElement("p");
|
|
6239
|
+
runNodes.forEach((node) => {
|
|
6240
|
+
paragraphEl.appendChild(node);
|
|
6241
|
+
});
|
|
6242
|
+
targetEl.insertBefore(paragraphEl, referenceNode || null);
|
|
6243
|
+
runNodes = [];
|
|
6244
|
+
}
|
|
6245
|
+
|
|
6246
|
+
childNodes.forEach((node) => {
|
|
6247
|
+
if (node instanceof Element && isDirectBlockChild(node)) {
|
|
6248
|
+
flushRun(node);
|
|
6249
|
+
return;
|
|
6250
|
+
}
|
|
6251
|
+
if (node.parentNode === targetEl) {
|
|
6252
|
+
runNodes.push(node);
|
|
6253
|
+
}
|
|
6254
|
+
});
|
|
6255
|
+
flushRun(null);
|
|
6256
|
+
}
|
|
6257
|
+
|
|
5514
6258
|
function splitMixedPreviewParagraphsAroundDisplayMath(targetEl) {
|
|
5515
6259
|
if (!targetEl || typeof targetEl.querySelectorAll !== "function") return;
|
|
6260
|
+
if (editorLanguage === "latex") {
|
|
6261
|
+
wrapLoosePreviewInlineRunsAsParagraphs(targetEl);
|
|
6262
|
+
}
|
|
5516
6263
|
Array.from(targetEl.querySelectorAll("p")).forEach((paragraphEl) => {
|
|
5517
6264
|
if (!(paragraphEl instanceof Element) || !paragraphEl.parentNode) return;
|
|
5518
6265
|
if (paragraphEl.closest && paragraphEl.closest(".preview-comment-block")) return;
|
|
@@ -5574,6 +6321,20 @@
|
|
|
5574
6321
|
const tag = element.tagName ? element.tagName.toUpperCase() : "";
|
|
5575
6322
|
if (/^H[1-6]$/.test(tag)) return "heading";
|
|
5576
6323
|
if (tag === "P") return "paragraph";
|
|
6324
|
+
if (tag === "FIGURE") {
|
|
6325
|
+
if (element.classList && element.classList.contains("studio-algorithm-block")) {
|
|
6326
|
+
return "algorithm";
|
|
6327
|
+
}
|
|
6328
|
+
return editorLanguage === "latex" ? "figure" : "";
|
|
6329
|
+
}
|
|
6330
|
+
if (tag === "DIV" && element.classList) {
|
|
6331
|
+
if (element.classList.contains("studio-display-equation")) {
|
|
6332
|
+
return "math";
|
|
6333
|
+
}
|
|
6334
|
+
if (element.classList.contains("abstract") || element.classList.contains("keywords") || element.classList.contains("references")) {
|
|
6335
|
+
return "paragraph";
|
|
6336
|
+
}
|
|
6337
|
+
}
|
|
5577
6338
|
if (tag === "BLOCKQUOTE") return "blockquote";
|
|
5578
6339
|
if (tag === "UL" || tag === "OL") return "list";
|
|
5579
6340
|
if (tag === "TABLE") return "table";
|
|
@@ -5605,11 +6366,49 @@
|
|
|
5605
6366
|
return Boolean(getPreviewCommentTargetKind(element));
|
|
5606
6367
|
}
|
|
5607
6368
|
|
|
6369
|
+
function isLatexPreviewCommentTargetElement(element, targetEl) {
|
|
6370
|
+
if (!element || !(element instanceof Element) || !targetEl) return false;
|
|
6371
|
+
const kind = getPreviewCommentTargetKind(element);
|
|
6372
|
+
if (kind === "heading" || kind === "paragraph" || kind === "figure" || kind === "algorithm" || kind === "table") {
|
|
6373
|
+
if (element.parentElement === targetEl) return true;
|
|
6374
|
+
if (
|
|
6375
|
+
kind === "paragraph"
|
|
6376
|
+
&& element.classList
|
|
6377
|
+
&& element.classList.contains("abstract")
|
|
6378
|
+
&& element.parentElement
|
|
6379
|
+
&& element.parentElement.tagName === "HEADER"
|
|
6380
|
+
&& element.parentElement.id === "title-block-header"
|
|
6381
|
+
&& element.parentElement.parentElement === targetEl
|
|
6382
|
+
) {
|
|
6383
|
+
return true;
|
|
6384
|
+
}
|
|
6385
|
+
return false;
|
|
6386
|
+
}
|
|
6387
|
+
if (kind === "math") {
|
|
6388
|
+
if (element.parentElement === targetEl) return true;
|
|
6389
|
+
const bodyEl = element.parentElement;
|
|
6390
|
+
const frameEl = bodyEl && bodyEl.parentElement;
|
|
6391
|
+
return Boolean(
|
|
6392
|
+
bodyEl
|
|
6393
|
+
&& bodyEl.classList
|
|
6394
|
+
&& bodyEl.classList.contains("studio-display-equation-body")
|
|
6395
|
+
&& frameEl
|
|
6396
|
+
&& frameEl.classList
|
|
6397
|
+
&& frameEl.classList.contains("studio-display-equation")
|
|
6398
|
+
&& frameEl.parentElement === targetEl
|
|
6399
|
+
);
|
|
6400
|
+
}
|
|
6401
|
+
return false;
|
|
6402
|
+
}
|
|
6403
|
+
|
|
5608
6404
|
function collectPreviewCommentTargetElements(targetEl) {
|
|
5609
6405
|
if (!targetEl || typeof targetEl.querySelectorAll !== "function") return [];
|
|
5610
|
-
const selector = "h1, h2, h3, h4, h5, h6, p, blockquote, ul, ol, table, div.sourceCode, pre, math[display='block'], .studio-mathjax-fallback-display, .studio-page-break, .callout-note, .callout-tip, .callout-warning, .callout-important, .callout-caution, .mermaid-container";
|
|
6406
|
+
const selector = "h1, h2, h3, h4, h5, h6, p, figure, blockquote, ul, ol, table, div.sourceCode, pre, math[display='block'], .studio-display-equation, .studio-mathjax-fallback-display, .studio-page-break, .abstract, .keywords, .references, .callout-note, .callout-tip, .callout-warning, .callout-important, .callout-caution, .mermaid-container";
|
|
5611
6407
|
return Array.from(targetEl.querySelectorAll(selector)).filter((element) => {
|
|
5612
6408
|
if (!isPreviewCommentTargetElement(element)) return false;
|
|
6409
|
+
if (editorLanguage === "latex" && !isLatexPreviewCommentTargetElement(element, targetEl)) {
|
|
6410
|
+
return false;
|
|
6411
|
+
}
|
|
5613
6412
|
let ancestor = element.parentElement;
|
|
5614
6413
|
while (ancestor && ancestor !== targetEl) {
|
|
5615
6414
|
if (ancestor.classList && ancestor.classList.contains("preview-comment-block")) return false;
|
|
@@ -5626,6 +6425,9 @@
|
|
|
5626
6425
|
function getNormalizedPreviewCommentSourceBlockText(sourceText, sourceBlock) {
|
|
5627
6426
|
if (!sourceBlock) return "";
|
|
5628
6427
|
const blockText = String(sourceText || "").slice(sourceBlock.start, sourceBlock.end);
|
|
6428
|
+
if (editorLanguage === "latex") {
|
|
6429
|
+
return normalizeLatexPreviewBlockText(blockText, sourceBlock.kind);
|
|
6430
|
+
}
|
|
5629
6431
|
if (sourceBlock.kind === "page-break") {
|
|
5630
6432
|
const match = blockText.trim().match(/^\\(newpage|pagebreak|clearpage)/i);
|
|
5631
6433
|
return match ? String(match[1] || "").toLowerCase() : "page-break";
|
|
@@ -5673,13 +6475,46 @@
|
|
|
5673
6475
|
return longer.includes(shorter);
|
|
5674
6476
|
}
|
|
5675
6477
|
|
|
6478
|
+
function tokenizePreviewComparableText(text) {
|
|
6479
|
+
return normalizeVisiblePreviewText(text)
|
|
6480
|
+
.toLowerCase()
|
|
6481
|
+
.split(/\s+/)
|
|
6482
|
+
.map((token) => token.replace(/^[^0-9A-Za-z\u00C0-\uFFFF]+|[^0-9A-Za-z\u00C0-\uFFFF]+$/g, ""))
|
|
6483
|
+
.filter((token) => token && (token.length >= 4 || /[A-Za-z\u00C0-\uFFFF]/.test(token)));
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6486
|
+
function getHighConfidenceLatexOrderedTokenMatchScore(targetText, desiredText) {
|
|
6487
|
+
if (editorLanguage !== "latex") return -1;
|
|
6488
|
+
const targetTokens = tokenizePreviewComparableText(targetText);
|
|
6489
|
+
const desiredTokens = tokenizePreviewComparableText(desiredText);
|
|
6490
|
+
if (targetTokens.length === 0 || desiredTokens.length < 5) return -1;
|
|
6491
|
+
|
|
6492
|
+
let targetTokenIndex = 0;
|
|
6493
|
+
let matchedCount = 0;
|
|
6494
|
+
for (const token of desiredTokens) {
|
|
6495
|
+
while (targetTokenIndex < targetTokens.length && targetTokens[targetTokenIndex] !== token) {
|
|
6496
|
+
targetTokenIndex += 1;
|
|
6497
|
+
}
|
|
6498
|
+
if (targetTokenIndex >= targetTokens.length) break;
|
|
6499
|
+
matchedCount += 1;
|
|
6500
|
+
targetTokenIndex += 1;
|
|
6501
|
+
}
|
|
6502
|
+
|
|
6503
|
+
const matchRatio = matchedCount / desiredTokens.length;
|
|
6504
|
+
if (matchedCount < 5 || matchRatio < 0.6) return -1;
|
|
6505
|
+
return matchedCount * 1000 + Math.round(matchRatio * 100);
|
|
6506
|
+
}
|
|
6507
|
+
|
|
5676
6508
|
function findMatchingPreviewCommentTargetIndex(sourceText, sourceBlock, targetBlocks, startIndex) {
|
|
5677
6509
|
const desiredKind = sourceBlock ? sourceBlock.kind : "";
|
|
5678
6510
|
const desiredText = getNormalizedPreviewCommentSourceBlockText(sourceText, sourceBlock);
|
|
6511
|
+
const preferredStartIndex = Math.max(0, startIndex || 0);
|
|
5679
6512
|
let fallbackIndex = -1;
|
|
5680
6513
|
let containsIndex = -1;
|
|
6514
|
+
let orderedTokenIndex = -1;
|
|
6515
|
+
let orderedTokenScore = Number.NEGATIVE_INFINITY;
|
|
5681
6516
|
|
|
5682
|
-
for (let i =
|
|
6517
|
+
for (let i = preferredStartIndex; i < targetBlocks.length; i += 1) {
|
|
5683
6518
|
const targetEntry = targetBlocks[i];
|
|
5684
6519
|
if (!targetEntry || targetEntry.kind !== desiredKind) continue;
|
|
5685
6520
|
if (fallbackIndex < 0) fallbackIndex = i;
|
|
@@ -5691,10 +6526,19 @@
|
|
|
5691
6526
|
if (containsIndex < 0 && isHighConfidencePreviewTextContainmentMatch(targetText, desiredText)) {
|
|
5692
6527
|
containsIndex = i;
|
|
5693
6528
|
}
|
|
6529
|
+
const latexTokenScore = getHighConfidenceLatexOrderedTokenMatchScore(targetText, desiredText);
|
|
6530
|
+
if (latexTokenScore >= 0) {
|
|
6531
|
+
const score = latexTokenScore - (Math.abs(i - preferredStartIndex) * 4);
|
|
6532
|
+
if (score > orderedTokenScore) {
|
|
6533
|
+
orderedTokenScore = score;
|
|
6534
|
+
orderedTokenIndex = i;
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
5694
6537
|
}
|
|
5695
6538
|
}
|
|
5696
6539
|
|
|
5697
6540
|
if (containsIndex >= 0) return containsIndex;
|
|
6541
|
+
if (orderedTokenIndex >= 0) return orderedTokenIndex;
|
|
5698
6542
|
return fallbackIndex;
|
|
5699
6543
|
}
|
|
5700
6544
|
|
|
@@ -5709,41 +6553,26 @@
|
|
|
5709
6553
|
|
|
5710
6554
|
function updatePreviewCommentBlockState(blockEl, sourceText, displayNotes) {
|
|
5711
6555
|
if (!blockEl || !blockEl.dataset) return;
|
|
5712
|
-
const lineStart = Math.max(1, Number(blockEl.dataset.reviewNoteLineStart) || 1);
|
|
5713
|
-
const lineEnd = Math.max(lineStart, Number(blockEl.dataset.reviewNoteLineEnd) || lineStart);
|
|
5714
|
-
const summaryBtn = blockEl.querySelector(".preview-comment-summary");
|
|
5715
|
-
const addBtn = blockEl.querySelector(".preview-comment-add");
|
|
5716
|
-
const lineLabel = summarizeReviewNoteAnchor({ lineStart: lineStart, lineEnd: lineEnd }).toLowerCase();
|
|
5717
|
-
const blockKindLabel = getPreviewCommentBlockKindLabel(blockEl.dataset.previewCommentKind || "paragraph");
|
|
5718
6556
|
const blockKey = getPreviewCommentBlockKey(blockEl);
|
|
5719
|
-
const
|
|
6557
|
+
const paneId = getPreviewSelectionPaneIdForNode(blockEl);
|
|
6558
|
+
const hasSelection = Boolean(
|
|
6559
|
+
activePreviewCommentSelection
|
|
6560
|
+
&& activePreviewCommentSelection.paneId === paneId
|
|
6561
|
+
&& activePreviewCommentSelection.blockKey === blockKey
|
|
6562
|
+
);
|
|
5720
6563
|
|
|
5721
6564
|
blockEl.classList.remove("has-comments");
|
|
5722
6565
|
blockEl.classList.toggle("has-selection", hasSelection);
|
|
5723
|
-
|
|
5724
|
-
if (summaryBtn) {
|
|
5725
|
-
summaryBtn.hidden = true;
|
|
5726
|
-
summaryBtn.textContent = "";
|
|
5727
|
-
summaryBtn.dataset.reviewNoteId = "";
|
|
5728
|
-
}
|
|
5729
|
-
|
|
5730
|
-
if (addBtn) {
|
|
5731
|
-
addBtn.hidden = !hasSelection;
|
|
5732
|
-
addBtn.textContent = "Comment";
|
|
5733
|
-
addBtn.dataset.previewCommentMode = hasSelection ? "selection" : "";
|
|
5734
|
-
addBtn.title = hasSelection
|
|
5735
|
-
? ("Add a local comment from the current preview selection on this " + blockKindLabel + " (" + lineLabel + ").")
|
|
5736
|
-
: "";
|
|
5737
|
-
addBtn.setAttribute("aria-label", addBtn.title || "Comment");
|
|
5738
|
-
}
|
|
5739
6566
|
}
|
|
5740
6567
|
|
|
5741
6568
|
function updatePreviewCommentBlocksForElement(targetEl) {
|
|
5742
6569
|
if (!targetEl || typeof targetEl.querySelectorAll !== "function") return;
|
|
6570
|
+
ensurePreviewSelectionActions(targetEl);
|
|
5743
6571
|
const sourceText = String(sourceTextEl && sourceTextEl.value ? sourceTextEl.value : "");
|
|
5744
6572
|
Array.from(targetEl.querySelectorAll(".preview-comment-block")).forEach((blockEl) => {
|
|
5745
6573
|
updatePreviewCommentBlockState(blockEl, sourceText);
|
|
5746
6574
|
});
|
|
6575
|
+
updatePreviewSelectionActions(targetEl);
|
|
5747
6576
|
}
|
|
5748
6577
|
|
|
5749
6578
|
function decorateRenderedEditorPreviewComments(targetEl, sourceText) {
|
|
@@ -5771,35 +6600,20 @@
|
|
|
5771
6600
|
wrapper.dataset.reviewNoteLineEnd = String(sourceBlock.lineEnd);
|
|
5772
6601
|
wrapper.dataset.previewCommentKind = sourceBlock.kind;
|
|
5773
6602
|
|
|
5774
|
-
const controls = document.createElement("div");
|
|
5775
|
-
controls.className = "preview-comment-controls";
|
|
5776
|
-
|
|
5777
|
-
const summaryBtn = document.createElement("button");
|
|
5778
|
-
summaryBtn.type = "button";
|
|
5779
|
-
summaryBtn.className = "preview-comment-summary";
|
|
5780
|
-
summaryBtn.hidden = true;
|
|
5781
|
-
controls.appendChild(summaryBtn);
|
|
5782
|
-
|
|
5783
|
-
const addBtn = document.createElement("button");
|
|
5784
|
-
addBtn.type = "button";
|
|
5785
|
-
addBtn.className = "preview-comment-add";
|
|
5786
|
-
addBtn.textContent = "Comment";
|
|
5787
|
-
controls.appendChild(addBtn);
|
|
5788
|
-
|
|
5789
6603
|
originalElement.replaceWith(wrapper);
|
|
5790
|
-
wrapper.appendChild(controls);
|
|
5791
6604
|
originalElement.classList.add("preview-comment-block-content");
|
|
5792
6605
|
wrapper.appendChild(originalElement);
|
|
5793
6606
|
}
|
|
5794
6607
|
|
|
6608
|
+
ensurePreviewSelectionActions(targetEl);
|
|
5795
6609
|
updatePreviewCommentBlocksForElement(targetEl);
|
|
5796
6610
|
}
|
|
5797
6611
|
|
|
5798
6612
|
function refreshRenderedEditorPreviewComments() {
|
|
5799
|
-
if (sourcePreviewEl
|
|
6613
|
+
if (sourcePreviewEl) {
|
|
5800
6614
|
updatePreviewCommentBlocksForElement(sourcePreviewEl);
|
|
5801
6615
|
}
|
|
5802
|
-
if (critiqueViewEl
|
|
6616
|
+
if (critiqueViewEl) {
|
|
5803
6617
|
updatePreviewCommentBlocksForElement(critiqueViewEl);
|
|
5804
6618
|
}
|
|
5805
6619
|
}
|
|
@@ -5845,6 +6659,19 @@
|
|
|
5845
6659
|
};
|
|
5846
6660
|
}
|
|
5847
6661
|
|
|
6662
|
+
if (editorLanguage === "latex") {
|
|
6663
|
+
const selectedDisplayText = buildNormalizedPreviewRangeText(range);
|
|
6664
|
+
if (!selectedDisplayText) return null;
|
|
6665
|
+
return {
|
|
6666
|
+
selectionStart: blockStart,
|
|
6667
|
+
selectionEnd: blockEnd,
|
|
6668
|
+
lineStart: getLineNumberAtOffset(source, blockStart),
|
|
6669
|
+
lineEnd: getLineNumberAtOffset(source, Math.max(blockStart, blockEnd - 1)),
|
|
6670
|
+
selectedText: source.slice(blockStart, blockEnd),
|
|
6671
|
+
selectedDisplayText,
|
|
6672
|
+
};
|
|
6673
|
+
}
|
|
6674
|
+
|
|
5848
6675
|
const sourceBlockText = source.slice(blockStart, blockEnd);
|
|
5849
6676
|
const displayMap = buildPreviewSelectionDisplayMap(sourceBlockText, kind);
|
|
5850
6677
|
if (!displayMap.text || !displayMap.charStarts.length || !displayMap.charEnds.length) return null;
|
|
@@ -5992,7 +6819,16 @@
|
|
|
5992
6819
|
const source = String(sourceTextEl && sourceTextEl.value ? sourceTextEl.value : "");
|
|
5993
6820
|
const range = resolveReviewNoteRange(note, source);
|
|
5994
6821
|
if (!range) return false;
|
|
5995
|
-
const
|
|
6822
|
+
const rangeBlock = findPreviewCommentBlockForRange(targetEl, range);
|
|
6823
|
+
const selectionText = getPreviewNoteNormalizedSelectionText(note);
|
|
6824
|
+
let blockEl = rangeBlock;
|
|
6825
|
+
if (selectionText) {
|
|
6826
|
+
const rangeContentEl = rangeBlock ? (rangeBlock.querySelector(".preview-comment-block-content") || rangeBlock) : null;
|
|
6827
|
+
const rangeText = rangeContentEl ? buildNormalizedPreviewSearchText(rangeContentEl) : "";
|
|
6828
|
+
if (!rangeText || !rangeText.includes(selectionText)) {
|
|
6829
|
+
blockEl = findPreviewCommentBlockForNoteText(targetEl, note) || rangeBlock;
|
|
6830
|
+
}
|
|
6831
|
+
}
|
|
5996
6832
|
if (!blockEl) return false;
|
|
5997
6833
|
const contentEl = blockEl.querySelector(".preview-comment-block-content") || blockEl;
|
|
5998
6834
|
if (String(blockEl.dataset && blockEl.dataset.previewCommentKind || "") === "math") {
|
|
@@ -6011,10 +6847,11 @@
|
|
|
6011
6847
|
}
|
|
6012
6848
|
|
|
6013
6849
|
function revealReviewNoteInPreview(note) {
|
|
6014
|
-
if (!supportsPreviewCommentsForCurrentEditor()) return;
|
|
6850
|
+
if (!supportsPreviewCommentsForCurrentEditor()) return false;
|
|
6015
6851
|
if (rightView === "editor-preview" && critiqueViewEl && critiqueViewEl.isConnected) {
|
|
6016
|
-
revealReviewNoteInPreviewElement(critiqueViewEl, note);
|
|
6852
|
+
return revealReviewNoteInPreviewElement(critiqueViewEl, note);
|
|
6017
6853
|
}
|
|
6854
|
+
return false;
|
|
6018
6855
|
}
|
|
6019
6856
|
|
|
6020
6857
|
function updateActivePreviewCommentSelectionFromDom() {
|
|
@@ -6046,7 +6883,9 @@
|
|
|
6046
6883
|
|
|
6047
6884
|
setActivePreviewCommentSelection({
|
|
6048
6885
|
...anchor,
|
|
6886
|
+
paneId: getPreviewSelectionPaneIdForNode(startBlock),
|
|
6049
6887
|
blockKey: getPreviewCommentBlockKey(startBlock),
|
|
6888
|
+
previewCommentKind: String(startBlock.dataset && startBlock.dataset.previewCommentKind || "paragraph"),
|
|
6050
6889
|
});
|
|
6051
6890
|
}
|
|
6052
6891
|
|
|
@@ -6141,11 +6980,27 @@
|
|
|
6141
6980
|
&& typeof sourceTextEl.selectionEnd === "number"
|
|
6142
6981
|
&& sourceTextEl.selectionEnd > sourceTextEl.selectionStart
|
|
6143
6982
|
);
|
|
6983
|
+
const canJumpToPreview = Boolean(
|
|
6984
|
+
hasSelection
|
|
6985
|
+
&& rightView === "editor-preview"
|
|
6986
|
+
&& critiqueViewEl
|
|
6987
|
+
&& supportsPreviewCommentsForCurrentEditor()
|
|
6988
|
+
);
|
|
6144
6989
|
editorSelectionCommentBtn.hidden = !hasSelection;
|
|
6990
|
+
if (editorSelectionJumpBtn) {
|
|
6991
|
+
editorSelectionJumpBtn.hidden = !canJumpToPreview;
|
|
6992
|
+
}
|
|
6993
|
+
if (editorSelectionActionsEl) {
|
|
6994
|
+
editorSelectionActionsEl.hidden = !hasSelection;
|
|
6995
|
+
}
|
|
6145
6996
|
if (hasSelection) {
|
|
6146
6997
|
editorSelectionCommentBtn.title = "Create a new local comment from the current editor selection.";
|
|
6147
6998
|
editorSelectionCommentBtn.setAttribute("aria-label", editorSelectionCommentBtn.title);
|
|
6148
6999
|
}
|
|
7000
|
+
if (editorSelectionJumpBtn && canJumpToPreview) {
|
|
7001
|
+
editorSelectionJumpBtn.title = "Jump to the current editor selection in the preview.";
|
|
7002
|
+
editorSelectionJumpBtn.setAttribute("aria-label", editorSelectionJumpBtn.title);
|
|
7003
|
+
}
|
|
6149
7004
|
}
|
|
6150
7005
|
|
|
6151
7006
|
function clearSuppressedEditorSelectionComment() {
|
|
@@ -6362,12 +7217,12 @@
|
|
|
6362
7217
|
});
|
|
6363
7218
|
}
|
|
6364
7219
|
|
|
6365
|
-
function
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
7220
|
+
function getActivePreviewSelectionAnchorForPane(paneId) {
|
|
7221
|
+
return getActivePreviewSelectionForPane(paneId);
|
|
7222
|
+
}
|
|
7223
|
+
|
|
7224
|
+
function addReviewNoteFromPreviewSelection(paneId) {
|
|
7225
|
+
const anchor = getActivePreviewSelectionAnchorForPane(paneId);
|
|
6371
7226
|
if (!anchor) {
|
|
6372
7227
|
setStatus("Select some preview text within a single block first.", "warning");
|
|
6373
7228
|
return null;
|
|
@@ -6403,6 +7258,12 @@
|
|
|
6403
7258
|
if (editorSelectionCommentBtn) {
|
|
6404
7259
|
editorSelectionCommentBtn.hidden = true;
|
|
6405
7260
|
}
|
|
7261
|
+
if (editorSelectionJumpBtn) {
|
|
7262
|
+
editorSelectionJumpBtn.hidden = true;
|
|
7263
|
+
}
|
|
7264
|
+
if (editorSelectionActionsEl) {
|
|
7265
|
+
editorSelectionActionsEl.hidden = true;
|
|
7266
|
+
}
|
|
6406
7267
|
const shouldOpenReviewNotes = !isReviewNotesOpen();
|
|
6407
7268
|
pendingReviewNoteFocusId = note.id;
|
|
6408
7269
|
setReviewNotes(reviewNotes.concat([note]));
|
|
@@ -6432,6 +7293,30 @@
|
|
|
6432
7293
|
});
|
|
6433
7294
|
}
|
|
6434
7295
|
|
|
7296
|
+
function jumpToEditorSelectionInPreview() {
|
|
7297
|
+
if (editorView !== "markdown") {
|
|
7298
|
+
setStatus("Switch to Editor (Raw) before jumping from an editor selection.", "warning");
|
|
7299
|
+
return false;
|
|
7300
|
+
}
|
|
7301
|
+
if (rightView !== "editor-preview" || !critiqueViewEl || !supportsPreviewCommentsForCurrentEditor()) {
|
|
7302
|
+
setStatus("Open Editor (Preview) on the right to jump the current editor selection there.", "warning");
|
|
7303
|
+
return false;
|
|
7304
|
+
}
|
|
7305
|
+
const anchor = getEditorAnchorForReviewNote();
|
|
7306
|
+
const jumped = revealReviewNoteInPreview(anchor);
|
|
7307
|
+
if (!jumped) {
|
|
7308
|
+
setStatus("Could not find the current editor selection in the preview.", "warning");
|
|
7309
|
+
return false;
|
|
7310
|
+
}
|
|
7311
|
+
const current = String(sourceTextEl.value || "");
|
|
7312
|
+
const range = resolveReviewNoteRange(anchor, current);
|
|
7313
|
+
if (range) {
|
|
7314
|
+
scrollEditorRangeIntoView(range);
|
|
7315
|
+
}
|
|
7316
|
+
setStatus("Jumped to the current editor selection in the preview.", "success");
|
|
7317
|
+
return true;
|
|
7318
|
+
}
|
|
7319
|
+
|
|
6435
7320
|
function addReviewNoteFromEditorLine() {
|
|
6436
7321
|
if (editorView !== "markdown") {
|
|
6437
7322
|
setStatus("Switch to Editor (Raw) before adding a line comment.", "warning");
|
|
@@ -6442,14 +7327,13 @@
|
|
|
6442
7327
|
});
|
|
6443
7328
|
}
|
|
6444
7329
|
|
|
6445
|
-
function
|
|
6446
|
-
|
|
6447
|
-
if (!note) return;
|
|
7330
|
+
function jumpToReviewAnchor(anchor, options) {
|
|
7331
|
+
if (!anchor) return false;
|
|
6448
7332
|
const current = String(sourceTextEl.value || "");
|
|
6449
|
-
const range = resolveReviewNoteRange(
|
|
7333
|
+
const range = resolveReviewNoteRange(anchor, current);
|
|
6450
7334
|
if (!range) {
|
|
6451
|
-
setStatus("Could not find the anchored location
|
|
6452
|
-
return;
|
|
7335
|
+
setStatus((options && options.notFoundStatusMessage) || "Could not find the anchored location.", "warning");
|
|
7336
|
+
return false;
|
|
6453
7337
|
}
|
|
6454
7338
|
suppressEditorSelectionComment = true;
|
|
6455
7339
|
suppressedEditorSelectionStart = range.start;
|
|
@@ -6464,9 +7348,56 @@
|
|
|
6464
7348
|
: (cb) => window.setTimeout(cb, 16);
|
|
6465
7349
|
schedule(() => {
|
|
6466
7350
|
scrollEditorRangeIntoView(range);
|
|
6467
|
-
|
|
7351
|
+
if (options && typeof options.afterJump === "function") {
|
|
7352
|
+
options.afterJump(range);
|
|
7353
|
+
}
|
|
6468
7354
|
updateEditorSelectionCommentUi();
|
|
6469
7355
|
});
|
|
7356
|
+
if (!options || options.status !== false) {
|
|
7357
|
+
setStatus((options && options.statusMessage) || "Jumped to anchored location in the editor.", "success");
|
|
7358
|
+
}
|
|
7359
|
+
return true;
|
|
7360
|
+
}
|
|
7361
|
+
|
|
7362
|
+
function jumpToPreviewSelection(paneId) {
|
|
7363
|
+
const anchor = getActivePreviewSelectionAnchorForPane(paneId);
|
|
7364
|
+
if (!anchor) {
|
|
7365
|
+
setStatus("Select some preview text within a single block first.", "warning");
|
|
7366
|
+
return false;
|
|
7367
|
+
}
|
|
7368
|
+
const previewNote = normalizeReviewNote(anchor);
|
|
7369
|
+
const jumped = jumpToReviewAnchor(previewNote, {
|
|
7370
|
+
statusMessage: "Jumped to preview selection in the raw editor.",
|
|
7371
|
+
afterJump: () => {
|
|
7372
|
+
const paneEl = getPreviewSelectionPaneElement(paneId);
|
|
7373
|
+
if (paneEl && previewNote) {
|
|
7374
|
+
revealReviewNoteInPreviewElement(paneEl, previewNote);
|
|
7375
|
+
}
|
|
7376
|
+
const schedule = typeof window.requestAnimationFrame === "function"
|
|
7377
|
+
? window.requestAnimationFrame.bind(window)
|
|
7378
|
+
: (cb) => window.setTimeout(cb, 16);
|
|
7379
|
+
schedule(() => {
|
|
7380
|
+
const selection = typeof window.getSelection === "function" ? window.getSelection() : null;
|
|
7381
|
+
if (selection && typeof selection.removeAllRanges === "function") {
|
|
7382
|
+
selection.removeAllRanges();
|
|
7383
|
+
}
|
|
7384
|
+
clearPreviewCommentSelection();
|
|
7385
|
+
});
|
|
7386
|
+
},
|
|
7387
|
+
});
|
|
7388
|
+
return jumped;
|
|
7389
|
+
}
|
|
7390
|
+
|
|
7391
|
+
function jumpToReviewNote(noteId) {
|
|
7392
|
+
const note = reviewNotes.find((entry) => entry && entry.id === noteId);
|
|
7393
|
+
if (!note) return;
|
|
7394
|
+
jumpToReviewAnchor(note, {
|
|
7395
|
+
status: false,
|
|
7396
|
+
notFoundStatusMessage: "Could not find the anchored location for this comment.",
|
|
7397
|
+
afterJump: () => {
|
|
7398
|
+
revealReviewNoteInPreview(note);
|
|
7399
|
+
},
|
|
7400
|
+
});
|
|
6470
7401
|
}
|
|
6471
7402
|
|
|
6472
7403
|
function deleteReviewNote(noteId) {
|
|
@@ -8380,6 +9311,15 @@
|
|
|
8380
9311
|
});
|
|
8381
9312
|
}
|
|
8382
9313
|
|
|
9314
|
+
if (editorSelectionJumpBtn) {
|
|
9315
|
+
editorSelectionJumpBtn.addEventListener("mousedown", (event) => {
|
|
9316
|
+
event.preventDefault();
|
|
9317
|
+
});
|
|
9318
|
+
editorSelectionJumpBtn.addEventListener("click", () => {
|
|
9319
|
+
jumpToEditorSelectionInPreview();
|
|
9320
|
+
});
|
|
9321
|
+
}
|
|
9322
|
+
|
|
8383
9323
|
if (reviewNotesInlineAllBtn) {
|
|
8384
9324
|
reviewNotesInlineAllBtn.addEventListener("click", () => {
|
|
8385
9325
|
toggleAllReviewNotesInlineAnnotations();
|
|
@@ -8399,22 +9339,26 @@
|
|
|
8399
9339
|
|
|
8400
9340
|
function handlePreviewCommentActionMouseDown(event) {
|
|
8401
9341
|
const target = event.target;
|
|
8402
|
-
const actionBtn = target instanceof Element ? target.closest(".preview-comment-add, .preview-comment-summary") : null;
|
|
9342
|
+
const actionBtn = target instanceof Element ? target.closest(".preview-comment-add, .preview-comment-jump, .preview-comment-summary") : null;
|
|
8403
9343
|
if (!actionBtn) return;
|
|
8404
9344
|
event.preventDefault();
|
|
8405
9345
|
}
|
|
8406
9346
|
|
|
8407
9347
|
function handlePreviewCommentActionClick(event) {
|
|
8408
9348
|
const target = event.target;
|
|
8409
|
-
const actionBtn = target instanceof Element ? target.closest(".preview-comment-add, .preview-comment-summary") : null;
|
|
9349
|
+
const actionBtn = target instanceof Element ? target.closest(".preview-comment-add, .preview-comment-jump, .preview-comment-summary") : null;
|
|
8410
9350
|
if (!actionBtn) return;
|
|
8411
|
-
const blockEl = actionBtn.closest(".preview-comment-block");
|
|
8412
|
-
if (!blockEl) return;
|
|
8413
9351
|
event.preventDefault();
|
|
8414
9352
|
event.stopPropagation();
|
|
8415
9353
|
const mode = String(actionBtn.dataset && actionBtn.dataset.previewCommentMode ? actionBtn.dataset.previewCommentMode : "");
|
|
8416
9354
|
if (!mode || !mode.startsWith("selection")) return;
|
|
8417
|
-
|
|
9355
|
+
const paneId = String(actionBtn.dataset && actionBtn.dataset.previewPane ? actionBtn.dataset.previewPane : "");
|
|
9356
|
+
const action = String(actionBtn.dataset && actionBtn.dataset.previewCommentAction ? actionBtn.dataset.previewCommentAction : "comment");
|
|
9357
|
+
if (action === "jump") {
|
|
9358
|
+
jumpToPreviewSelection(paneId);
|
|
9359
|
+
return;
|
|
9360
|
+
}
|
|
9361
|
+
addReviewNoteFromPreviewSelection(paneId);
|
|
8418
9362
|
}
|
|
8419
9363
|
|
|
8420
9364
|
if (leftPaneEl) {
|