@lionrapid/core 0.1.2 → 0.1.3-beta.0
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/dist/browser/editor/editor-api.d.ts +52 -0
- package/dist/browser/editor/editor-api.d.ts.map +1 -1
- package/dist/browser/editor/editor-api.js +136 -0
- package/dist/browser/editor/editor-api.js.map +1 -1
- package/dist/browser/editor/editor-init.js +118 -6
- package/dist/browser/editor/editor-init.js.map +1 -1
- package/dist/browser/editor/editor-styles.d.ts +1 -1
- package/dist/browser/editor/editor-styles.d.ts.map +1 -1
- package/dist/browser/editor/editor-styles.js +29 -0
- package/dist/browser/editor/editor-styles.js.map +1 -1
- package/dist/browser/editor/inspector.d.ts +5 -1
- package/dist/browser/editor/inspector.d.ts.map +1 -1
- package/dist/browser/editor/inspector.js +32 -1
- package/dist/browser/editor/inspector.js.map +1 -1
- package/dist/browser/editor/snapshot-capture.d.ts +57 -0
- package/dist/browser/editor/snapshot-capture.d.ts.map +1 -0
- package/dist/browser/editor/snapshot-capture.js +249 -0
- package/dist/browser/editor/snapshot-capture.js.map +1 -0
- package/dist/browser/editor/snapshot-viewer.d.ts +35 -0
- package/dist/browser/editor/snapshot-viewer.d.ts.map +1 -0
- package/dist/browser/editor/snapshot-viewer.js +90 -0
- package/dist/browser/editor/snapshot-viewer.js.map +1 -0
- package/dist/browser/editor/toolbar.d.ts +7 -1
- package/dist/browser/editor/toolbar.d.ts.map +1 -1
- package/dist/browser/editor/toolbar.js +26 -1
- package/dist/browser/editor/toolbar.js.map +1 -1
- package/dist/browser/lionrapid.editor.js +494 -6
- package/dist/browser/lionrapid.editor.js.map +3 -3
- package/dist/browser/lionrapid.editor.min.js +32 -2
- package/dist/browser/lionrapid.editor.min.js.map +4 -4
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolbar.js","sourceRoot":"","sources":["../../../src/browser/editor/toolbar.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"toolbar.js","sourceRoot":"","sources":["../../../src/browser/editor/toolbar.ts"],"names":[],"mappings":"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"}
|
|
@@ -153,6 +153,35 @@ var LionRapidEditor = (() => {
|
|
|
153
153
|
background: rgba(255,255,255,0.1);
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
.lr-fab-snapshot {
|
|
157
|
+
background: #2d7f7f;
|
|
158
|
+
border: 1px solid #2d7f7f;
|
|
159
|
+
color: #fff;
|
|
160
|
+
padding: 4px 10px;
|
|
161
|
+
border-radius: 4px;
|
|
162
|
+
cursor: pointer;
|
|
163
|
+
font-size: 13px;
|
|
164
|
+
font-weight: 600;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.lr-fab-snapshot:hover {
|
|
168
|
+
background: #3a9999;
|
|
169
|
+
border-color: #3a9999;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.lr-fab-snapshot:disabled {
|
|
173
|
+
opacity: 0.5;
|
|
174
|
+
cursor: not-allowed;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* Snapshot viewer sandbox wrapper */
|
|
178
|
+
#__lr_snapshot_root {
|
|
179
|
+
isolation: isolate;
|
|
180
|
+
position: relative;
|
|
181
|
+
width: 100%;
|
|
182
|
+
min-height: 100vh;
|
|
183
|
+
}
|
|
184
|
+
|
|
156
185
|
/* \u2500\u2500 Overlay \u2500\u2500 */
|
|
157
186
|
.lr-overlay {
|
|
158
187
|
position: fixed;
|
|
@@ -733,13 +762,19 @@ var LionRapidEditor = (() => {
|
|
|
733
762
|
var SKIP_TAGS = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "CODE", "PRE", "TEXTAREA", "NOSCRIPT", "SVG"]);
|
|
734
763
|
var BLOCK_SELECTOR2 = "p,h1,h2,h3,h4,h5,h6,li,td,th,label,button,figcaption,dt,dd,summary,caption";
|
|
735
764
|
var Inspector = class {
|
|
736
|
-
constructor(shadowRoot, onSelect) {
|
|
765
|
+
constructor(shadowRoot, onSelect, rootElement, snapshotKeyMap) {
|
|
737
766
|
__publicField(this, "overlay");
|
|
738
767
|
__publicField(this, "currentTarget", null);
|
|
739
768
|
__publicField(this, "onSelect");
|
|
740
769
|
__publicField(this, "rafId", null);
|
|
741
770
|
__publicField(this, "active", true);
|
|
771
|
+
__publicField(this, "rootElement");
|
|
772
|
+
/** When set, clicks resolve keys from this map (snapshot viewer mode)
|
|
773
|
+
* instead of re-computing from the (redacted) DOM text. */
|
|
774
|
+
__publicField(this, "snapshotKeyMap");
|
|
742
775
|
this.onSelect = onSelect;
|
|
776
|
+
this.rootElement = rootElement || document.body;
|
|
777
|
+
this.snapshotKeyMap = snapshotKeyMap || null;
|
|
743
778
|
this.overlay = document.createElement("div");
|
|
744
779
|
this.overlay.className = "lr-overlay";
|
|
745
780
|
shadowRoot.appendChild(this.overlay);
|
|
@@ -769,6 +804,7 @@ var LionRapidEditor = (() => {
|
|
|
769
804
|
const target = e.target;
|
|
770
805
|
if (!target || !target.closest) return;
|
|
771
806
|
if (target.closest("#__lionrapid-editor")) return;
|
|
807
|
+
if (!this.rootElement.contains(target)) return;
|
|
772
808
|
if (this.shouldSkip(target)) {
|
|
773
809
|
this.clearHighlight();
|
|
774
810
|
return;
|
|
@@ -787,11 +823,25 @@ var LionRapidEditor = (() => {
|
|
|
787
823
|
const target = e.target;
|
|
788
824
|
if (!target || !target.closest) return;
|
|
789
825
|
if (target.closest("#__lionrapid-editor")) return;
|
|
826
|
+
if (!this.rootElement.contains(target)) return;
|
|
790
827
|
const block = target.closest(BLOCK_SELECTOR2) || target.closest("[data-lr-block]");
|
|
791
828
|
if (!block) return;
|
|
792
829
|
if (this.shouldSkip(block)) return;
|
|
793
830
|
e.preventDefault();
|
|
794
831
|
e.stopPropagation();
|
|
832
|
+
if (this.snapshotKeyMap) {
|
|
833
|
+
const idxAttr = block.getAttribute("data-lr-snap-idx");
|
|
834
|
+
if (idxAttr !== null) {
|
|
835
|
+
const idx = parseInt(idxAttr, 10);
|
|
836
|
+
const key = this.snapshotKeyMap.get(idx);
|
|
837
|
+
if (key) {
|
|
838
|
+
const unit = parseBlockElement(block);
|
|
839
|
+
this.onSelect({ key, unit, translatedUnit: null, element: block });
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
795
845
|
const resolved = resolveKey(block);
|
|
796
846
|
if (resolved) {
|
|
797
847
|
this.onSelect(resolved);
|
|
@@ -828,7 +878,7 @@ var LionRapidEditor = (() => {
|
|
|
828
878
|
|
|
829
879
|
// src/browser/editor/toolbar.ts
|
|
830
880
|
var Toolbar = class {
|
|
831
|
-
constructor(shadowRoot, languages, currentLocale, onLanguageChange, onExit) {
|
|
881
|
+
constructor(shadowRoot, languages, currentLocale, onLanguageChange, onExit, onCaptureSnapshot = null, captureOptions = {}) {
|
|
832
882
|
__publicField(this, "el");
|
|
833
883
|
__publicField(this, "langBtnEl");
|
|
834
884
|
__publicField(this, "langMenuEl");
|
|
@@ -837,13 +887,16 @@ var LionRapidEditor = (() => {
|
|
|
837
887
|
__publicField(this, "currentLocale");
|
|
838
888
|
__publicField(this, "onLanguageChange");
|
|
839
889
|
__publicField(this, "onExit");
|
|
890
|
+
__publicField(this, "onCaptureSnapshot");
|
|
840
891
|
// Drag state
|
|
841
892
|
__publicField(this, "dragging", false);
|
|
842
893
|
__publicField(this, "dragOffsetX", 0);
|
|
843
894
|
__publicField(this, "dragOffsetY", 0);
|
|
844
895
|
this.onLanguageChange = onLanguageChange;
|
|
845
896
|
this.onExit = onExit;
|
|
897
|
+
this.onCaptureSnapshot = onCaptureSnapshot;
|
|
846
898
|
this.currentLocale = currentLocale;
|
|
899
|
+
const captureMode = captureOptions.mode ?? "capture";
|
|
847
900
|
this.el = document.createElement("div");
|
|
848
901
|
this.el.className = "lr-fab";
|
|
849
902
|
const fabBtn = document.createElement("button");
|
|
@@ -887,6 +940,22 @@ var LionRapidEditor = (() => {
|
|
|
887
940
|
}
|
|
888
941
|
langWrap.appendChild(this.langMenuEl);
|
|
889
942
|
panel.appendChild(langWrap);
|
|
943
|
+
if (this.onCaptureSnapshot) {
|
|
944
|
+
const captureBtn = document.createElement("button");
|
|
945
|
+
captureBtn.className = "lr-fab-snapshot";
|
|
946
|
+
if (captureMode === "recapture") {
|
|
947
|
+
captureBtn.textContent = "Save re-capture";
|
|
948
|
+
captureBtn.title = "Save this page as a new version of the existing snapshot";
|
|
949
|
+
} else {
|
|
950
|
+
captureBtn.textContent = "Capture";
|
|
951
|
+
captureBtn.title = "Capture a redacted snapshot of this page for translators";
|
|
952
|
+
}
|
|
953
|
+
captureBtn.addEventListener("click", (e) => {
|
|
954
|
+
e.stopPropagation();
|
|
955
|
+
if (this.onCaptureSnapshot) this.onCaptureSnapshot();
|
|
956
|
+
});
|
|
957
|
+
panel.appendChild(captureBtn);
|
|
958
|
+
}
|
|
890
959
|
this.progressEl = document.createElement("span");
|
|
891
960
|
this.progressEl.className = "lr-fab-progress";
|
|
892
961
|
panel.appendChild(this.progressEl);
|
|
@@ -1723,8 +1792,349 @@ var LionRapidEditor = (() => {
|
|
|
1723
1792
|
this.onSessionExpired();
|
|
1724
1793
|
}
|
|
1725
1794
|
}
|
|
1795
|
+
// ============================================
|
|
1796
|
+
// Snapshot API
|
|
1797
|
+
// ============================================
|
|
1798
|
+
/**
|
|
1799
|
+
* Upload a captured snapshot. Returns the created snapshot's UUID so the
|
|
1800
|
+
* caller can build a share URL (`?__lr_snapshot={uuid}`).
|
|
1801
|
+
*/
|
|
1802
|
+
async createSnapshot(snapshot) {
|
|
1803
|
+
try {
|
|
1804
|
+
const response = await fetch(`${this.apiBase}/api/editor/snapshots`, {
|
|
1805
|
+
method: "POST",
|
|
1806
|
+
headers: {
|
|
1807
|
+
"Content-Type": "application/json",
|
|
1808
|
+
"Authorization": `Bearer ${this.sessionToken}`
|
|
1809
|
+
},
|
|
1810
|
+
body: JSON.stringify({
|
|
1811
|
+
pageUrl: snapshot.pageUrl,
|
|
1812
|
+
title: snapshot.title,
|
|
1813
|
+
viewport: snapshot.viewport,
|
|
1814
|
+
textEntries: snapshot.textEntries,
|
|
1815
|
+
html: snapshot.html
|
|
1816
|
+
})
|
|
1817
|
+
});
|
|
1818
|
+
if (response.status === 401) {
|
|
1819
|
+
this.handleSessionExpired();
|
|
1820
|
+
return null;
|
|
1821
|
+
}
|
|
1822
|
+
if (!response.ok) {
|
|
1823
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1824
|
+
}
|
|
1825
|
+
const data = await response.json();
|
|
1826
|
+
return { snapshotUuid: data.snapshot_uuid || data.snapshotUuid };
|
|
1827
|
+
} catch (err) {
|
|
1828
|
+
this.onStatus("error", `Snapshot failed: ${err.message}`);
|
|
1829
|
+
return null;
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Re-capture an existing snapshot. Server appends a new version and keeps
|
|
1834
|
+
* the same `snapshotUuid`. Returns the new version number on success.
|
|
1835
|
+
*/
|
|
1836
|
+
async recaptureSnapshot(snapshotUuid, snapshot) {
|
|
1837
|
+
try {
|
|
1838
|
+
const response = await fetch(
|
|
1839
|
+
`${this.apiBase}/api/editor/snapshots/${encodeURIComponent(snapshotUuid)}`,
|
|
1840
|
+
{
|
|
1841
|
+
method: "PUT",
|
|
1842
|
+
headers: {
|
|
1843
|
+
"Content-Type": "application/json",
|
|
1844
|
+
"Authorization": `Bearer ${this.sessionToken}`
|
|
1845
|
+
},
|
|
1846
|
+
body: JSON.stringify({
|
|
1847
|
+
pageUrl: snapshot.pageUrl,
|
|
1848
|
+
title: snapshot.title,
|
|
1849
|
+
viewport: snapshot.viewport,
|
|
1850
|
+
textEntries: snapshot.textEntries,
|
|
1851
|
+
html: snapshot.html
|
|
1852
|
+
})
|
|
1853
|
+
}
|
|
1854
|
+
);
|
|
1855
|
+
if (response.status === 401) {
|
|
1856
|
+
this.handleSessionExpired();
|
|
1857
|
+
return null;
|
|
1858
|
+
}
|
|
1859
|
+
if (!response.ok) {
|
|
1860
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1861
|
+
}
|
|
1862
|
+
const data = await response.json();
|
|
1863
|
+
return {
|
|
1864
|
+
snapshotUuid: data.snapshot_uuid || data.snapshotUuid,
|
|
1865
|
+
version: data.version || 1
|
|
1866
|
+
};
|
|
1867
|
+
} catch (err) {
|
|
1868
|
+
this.onStatus("error", `Re-capture failed: ${err.message}`);
|
|
1869
|
+
return null;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
/**
|
|
1873
|
+
* Fetch a single snapshot by UUID. Returns null on auth failure or 404.
|
|
1874
|
+
*/
|
|
1875
|
+
async getSnapshot(snapshotUuid) {
|
|
1876
|
+
try {
|
|
1877
|
+
const response = await fetch(
|
|
1878
|
+
`${this.apiBase}/api/editor/snapshots/${encodeURIComponent(snapshotUuid)}`,
|
|
1879
|
+
{
|
|
1880
|
+
headers: { "Authorization": `Bearer ${this.sessionToken}` }
|
|
1881
|
+
}
|
|
1882
|
+
);
|
|
1883
|
+
if (response.status === 401) {
|
|
1884
|
+
this.handleSessionExpired();
|
|
1885
|
+
return null;
|
|
1886
|
+
}
|
|
1887
|
+
if (!response.ok) return null;
|
|
1888
|
+
return await response.json();
|
|
1889
|
+
} catch {
|
|
1890
|
+
return null;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
/**
|
|
1894
|
+
* List all snapshots the current editor session can see.
|
|
1895
|
+
*/
|
|
1896
|
+
async listSnapshots() {
|
|
1897
|
+
try {
|
|
1898
|
+
const response = await fetch(`${this.apiBase}/api/editor/snapshots`, {
|
|
1899
|
+
headers: { "Authorization": `Bearer ${this.sessionToken}` }
|
|
1900
|
+
});
|
|
1901
|
+
if (response.status === 401) {
|
|
1902
|
+
this.handleSessionExpired();
|
|
1903
|
+
return [];
|
|
1904
|
+
}
|
|
1905
|
+
if (!response.ok) return [];
|
|
1906
|
+
const data = await response.json();
|
|
1907
|
+
return data.snapshots || [];
|
|
1908
|
+
} catch {
|
|
1909
|
+
return [];
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
/**
|
|
1913
|
+
* Delete (soft) a snapshot by UUID.
|
|
1914
|
+
*/
|
|
1915
|
+
async deleteSnapshot(snapshotUuid) {
|
|
1916
|
+
try {
|
|
1917
|
+
const response = await fetch(
|
|
1918
|
+
`${this.apiBase}/api/editor/snapshots/${encodeURIComponent(snapshotUuid)}`,
|
|
1919
|
+
{
|
|
1920
|
+
method: "DELETE",
|
|
1921
|
+
headers: { "Authorization": `Bearer ${this.sessionToken}` }
|
|
1922
|
+
}
|
|
1923
|
+
);
|
|
1924
|
+
if (response.status === 401) {
|
|
1925
|
+
this.handleSessionExpired();
|
|
1926
|
+
return false;
|
|
1927
|
+
}
|
|
1928
|
+
return response.ok;
|
|
1929
|
+
} catch {
|
|
1930
|
+
return false;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1726
1933
|
};
|
|
1727
1934
|
|
|
1935
|
+
// src/browser/editor/snapshot-capture.ts
|
|
1936
|
+
var REDACTED = "[REDACTED]";
|
|
1937
|
+
function captureSnapshot() {
|
|
1938
|
+
const clone = document.documentElement.cloneNode(true);
|
|
1939
|
+
const textEntries = extractTextEntries(clone);
|
|
1940
|
+
removeDangerousElements(clone);
|
|
1941
|
+
clearFormValues(clone);
|
|
1942
|
+
redactSensitiveElements(clone);
|
|
1943
|
+
stripInlineEventHandlers(clone);
|
|
1944
|
+
redactTextNodePII(clone);
|
|
1945
|
+
stripSensitiveMetaAndHeaders(clone);
|
|
1946
|
+
const html = "<!DOCTYPE html>\n" + clone.outerHTML;
|
|
1947
|
+
return {
|
|
1948
|
+
html,
|
|
1949
|
+
textEntries,
|
|
1950
|
+
pageUrl: window.location.href,
|
|
1951
|
+
title: document.title,
|
|
1952
|
+
viewport: {
|
|
1953
|
+
width: window.innerWidth,
|
|
1954
|
+
height: window.innerHeight,
|
|
1955
|
+
devicePixelRatio: window.devicePixelRatio || 1
|
|
1956
|
+
}
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
function removeDangerousElements(root) {
|
|
1960
|
+
const selectors = [
|
|
1961
|
+
"script",
|
|
1962
|
+
"noscript",
|
|
1963
|
+
"iframe",
|
|
1964
|
+
"embed",
|
|
1965
|
+
"object",
|
|
1966
|
+
"#__lionrapid-editor",
|
|
1967
|
+
'link[rel="prefetch"]',
|
|
1968
|
+
'link[rel="preload"]',
|
|
1969
|
+
'link[rel="modulepreload"]'
|
|
1970
|
+
];
|
|
1971
|
+
for (const selector of selectors) {
|
|
1972
|
+
const matches = root.querySelectorAll(selector);
|
|
1973
|
+
for (let i = 0; i < matches.length; i++) {
|
|
1974
|
+
matches[i].remove();
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
function clearFormValues(root) {
|
|
1979
|
+
const inputs = root.querySelectorAll("input, textarea, select");
|
|
1980
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
1981
|
+
const el = inputs[i];
|
|
1982
|
+
if ("value" in el) el.value = "";
|
|
1983
|
+
if (el.hasAttribute("value")) el.setAttribute("value", "");
|
|
1984
|
+
if (el.tagName === "TEXTAREA") el.textContent = "";
|
|
1985
|
+
if (el.tagName === "SELECT") {
|
|
1986
|
+
const options = el.querySelectorAll("option");
|
|
1987
|
+
for (let j = 0; j < options.length; j++) {
|
|
1988
|
+
options[j].selected = false;
|
|
1989
|
+
options[j].removeAttribute("selected");
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
const attrs = Array.from(el.attributes);
|
|
1993
|
+
for (const attr of attrs) {
|
|
1994
|
+
if (attr.name.startsWith("data-value") || attr.name === "data-initial") {
|
|
1995
|
+
el.removeAttribute(attr.name);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
function redactSensitiveElements(root) {
|
|
2001
|
+
const sensitive = root.querySelectorAll("[data-pii], [data-sensitive]");
|
|
2002
|
+
for (let i = 0; i < sensitive.length; i++) {
|
|
2003
|
+
const el = sensitive[i];
|
|
2004
|
+
el.textContent = REDACTED;
|
|
2005
|
+
const attrs = Array.from(el.attributes);
|
|
2006
|
+
for (const attr of attrs) {
|
|
2007
|
+
if (attr.name !== "class" && attr.name !== "id" && attr.name !== "style") {
|
|
2008
|
+
el.removeAttribute(attr.name);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
function stripInlineEventHandlers(root) {
|
|
2014
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
|
|
2015
|
+
let node = walker.nextNode();
|
|
2016
|
+
while (node) {
|
|
2017
|
+
const attrs = Array.from(node.attributes);
|
|
2018
|
+
for (const attr of attrs) {
|
|
2019
|
+
if (attr.name.toLowerCase().startsWith("on")) {
|
|
2020
|
+
node.removeAttribute(attr.name);
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
node = walker.nextNode();
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
function redactTextNodePII(root) {
|
|
2027
|
+
const filter = new ExcludeFilter(
|
|
2028
|
+
{ emails: true, urls: true, numbers: true, phones: true },
|
|
2029
|
+
1
|
|
2030
|
+
// minTextLength=1 so single-char filler still passes through
|
|
2031
|
+
);
|
|
2032
|
+
const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
|
|
2033
|
+
const nodesToRedact = [];
|
|
2034
|
+
let node = walker.nextNode();
|
|
2035
|
+
while (node) {
|
|
2036
|
+
const trimmed = (node.nodeValue || "").trim();
|
|
2037
|
+
if (trimmed.length >= 5 && filter.shouldExclude(trimmed)) {
|
|
2038
|
+
nodesToRedact.push(node);
|
|
2039
|
+
}
|
|
2040
|
+
node = walker.nextNode();
|
|
2041
|
+
}
|
|
2042
|
+
for (const textNode of nodesToRedact) {
|
|
2043
|
+
textNode.nodeValue = REDACTED;
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
function stripSensitiveMetaAndHeaders(root) {
|
|
2047
|
+
const sensitiveMetas = root.querySelectorAll(
|
|
2048
|
+
'meta[name="csrf-token"], meta[name="csp-nonce"], meta[name="csrf-param"], meta[http-equiv="set-cookie"]'
|
|
2049
|
+
);
|
|
2050
|
+
for (let i = 0; i < sensitiveMetas.length; i++) {
|
|
2051
|
+
sensitiveMetas[i].remove();
|
|
2052
|
+
}
|
|
2053
|
+
const TOKEN_PARAMS = ["token", "auth", "access_token", "session", "sid", "key", "api_key", "apikey"];
|
|
2054
|
+
const urlElements = root.querySelectorAll("link[href], a[href], img[src]");
|
|
2055
|
+
for (let i = 0; i < urlElements.length; i++) {
|
|
2056
|
+
const el = urlElements[i];
|
|
2057
|
+
const attr = el.tagName === "IMG" ? "src" : "href";
|
|
2058
|
+
const raw = el.getAttribute(attr);
|
|
2059
|
+
if (!raw) continue;
|
|
2060
|
+
try {
|
|
2061
|
+
const url = new URL(raw, window.location.href);
|
|
2062
|
+
let changed = false;
|
|
2063
|
+
for (const param of TOKEN_PARAMS) {
|
|
2064
|
+
if (url.searchParams.has(param)) {
|
|
2065
|
+
url.searchParams.set(param, REDACTED);
|
|
2066
|
+
changed = true;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
if (changed) el.setAttribute(attr, url.toString());
|
|
2070
|
+
} catch {
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
function extractTextEntries(clone) {
|
|
2075
|
+
const extractor = new BlockExtractor(
|
|
2076
|
+
{ emails: true, urls: true, numbers: true, phones: true },
|
|
2077
|
+
[],
|
|
2078
|
+
2
|
|
2079
|
+
);
|
|
2080
|
+
const blocks = extractor.extract(clone);
|
|
2081
|
+
return blocks.map((block, index) => {
|
|
2082
|
+
block.element.setAttribute("data-lr-snap-idx", String(index));
|
|
2083
|
+
return {
|
|
2084
|
+
key: block.key,
|
|
2085
|
+
tagName: block.element.tagName,
|
|
2086
|
+
blockIndex: index
|
|
2087
|
+
};
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
// src/browser/editor/snapshot-viewer.ts
|
|
2092
|
+
async function activateSnapshotViewer(snapshotUuid, api) {
|
|
2093
|
+
const snapshot = await api.getSnapshot(snapshotUuid);
|
|
2094
|
+
if (!snapshot) return null;
|
|
2095
|
+
const parser = new DOMParser();
|
|
2096
|
+
const doc = parser.parseFromString(snapshot.html, "text/html");
|
|
2097
|
+
const snapshotStyles = doc.head.querySelectorAll('link[rel="stylesheet"], style');
|
|
2098
|
+
for (let i = 0; i < snapshotStyles.length; i++) {
|
|
2099
|
+
const clone = snapshotStyles[i].cloneNode(true);
|
|
2100
|
+
clone.setAttribute("data-lr-snapshot-style", "");
|
|
2101
|
+
document.head.appendChild(clone);
|
|
2102
|
+
}
|
|
2103
|
+
if (snapshot.title) {
|
|
2104
|
+
document.title = `[Snapshot] ${snapshot.title}`;
|
|
2105
|
+
}
|
|
2106
|
+
const editorHost = document.getElementById("__lionrapid-editor");
|
|
2107
|
+
while (document.body.firstChild) {
|
|
2108
|
+
if (document.body.firstChild === editorHost) {
|
|
2109
|
+
const next = document.body.firstChild.nextSibling;
|
|
2110
|
+
if (!next) break;
|
|
2111
|
+
document.body.removeChild(next);
|
|
2112
|
+
} else {
|
|
2113
|
+
document.body.removeChild(document.body.firstChild);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
const root = document.createElement("div");
|
|
2117
|
+
root.id = "__lr_snapshot_root";
|
|
2118
|
+
root.setAttribute("data-lr-snapshot-uuid", snapshotUuid);
|
|
2119
|
+
root.innerHTML = doc.body.innerHTML;
|
|
2120
|
+
if (editorHost && editorHost.parentNode === document.body) {
|
|
2121
|
+
document.body.insertBefore(root, editorHost);
|
|
2122
|
+
} else {
|
|
2123
|
+
document.body.appendChild(root);
|
|
2124
|
+
}
|
|
2125
|
+
const keyMap = /* @__PURE__ */ new Map();
|
|
2126
|
+
const entries = snapshot.textEntries || [];
|
|
2127
|
+
for (const entry of entries) {
|
|
2128
|
+
keyMap.set(entry.blockIndex, entry.key);
|
|
2129
|
+
}
|
|
2130
|
+
return {
|
|
2131
|
+
root,
|
|
2132
|
+
pageUrl: snapshot.pageUrl,
|
|
2133
|
+
title: snapshot.title,
|
|
2134
|
+
keyMap
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
|
|
1728
2138
|
// src/browser/editor/editor-init.ts
|
|
1729
2139
|
var SESSION_KEY = "__lr_editor_session";
|
|
1730
2140
|
function getStoredSession() {
|
|
@@ -1755,6 +2165,17 @@ var LionRapidEditor = (() => {
|
|
|
1755
2165
|
function clearSession() {
|
|
1756
2166
|
sessionStorage.removeItem(SESSION_KEY);
|
|
1757
2167
|
}
|
|
2168
|
+
function extractSessionRole(token) {
|
|
2169
|
+
try {
|
|
2170
|
+
const raw = token.startsWith("es_") ? token.slice(3) : token;
|
|
2171
|
+
const parts = raw.split(".");
|
|
2172
|
+
if (parts.length !== 3) return "";
|
|
2173
|
+
const payload = JSON.parse(atob(parts[1]));
|
|
2174
|
+
return typeof payload.role === "string" ? payload.role : "";
|
|
2175
|
+
} catch {
|
|
2176
|
+
return "";
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
1758
2179
|
function showEditorNotice(message) {
|
|
1759
2180
|
const el = document.createElement("div");
|
|
1760
2181
|
el.setAttribute("data-no-translate", "");
|
|
@@ -1791,12 +2212,15 @@ var LionRapidEditor = (() => {
|
|
|
1791
2212
|
setTimeout(() => el.remove(), 8e3);
|
|
1792
2213
|
}
|
|
1793
2214
|
function initEditor() {
|
|
1794
|
-
const scriptEl = document.querySelector("script[data-grant]");
|
|
2215
|
+
const scriptEl = document.querySelector("script[data-grant]") || document.querySelector("script[data-lr-editor]");
|
|
1795
2216
|
if (!scriptEl) {
|
|
1796
|
-
console.warn("[LionRapid Editor] No script tag with data-grant found");
|
|
1797
2217
|
return;
|
|
1798
2218
|
}
|
|
1799
2219
|
const grant = scriptEl.dataset.grant || "";
|
|
2220
|
+
const hasValidSession = getStoredSession() !== null;
|
|
2221
|
+
if (!grant && !hasValidSession) {
|
|
2222
|
+
return;
|
|
2223
|
+
}
|
|
1800
2224
|
const locale = scriptEl.dataset.locale || "";
|
|
1801
2225
|
const apiBase = scriptEl.dataset.api || "https://api.lionrapid.com";
|
|
1802
2226
|
const projectId = scriptEl.dataset.project || "";
|
|
@@ -1850,6 +2274,8 @@ var LionRapidEditor = (() => {
|
|
|
1850
2274
|
showEditorNotice("Editor session expired. Please reopen the editor from the dashboard.");
|
|
1851
2275
|
return;
|
|
1852
2276
|
}
|
|
2277
|
+
const role = extractSessionRole(sessionToken);
|
|
2278
|
+
const canCaptureSnapshots = role === "project_manager" || role === "admin";
|
|
1853
2279
|
const host = document.createElement("div");
|
|
1854
2280
|
host.id = "__lionrapid-editor";
|
|
1855
2281
|
host.setAttribute("data-no-translate", "");
|
|
@@ -1862,12 +2288,26 @@ var LionRapidEditor = (() => {
|
|
|
1862
2288
|
if (switcherEl) switcherEl.style.display = "none";
|
|
1863
2289
|
const api = new EditorAPI(normalizedApiBase, sessionToken, projectId);
|
|
1864
2290
|
api.startAutoFlush();
|
|
2291
|
+
const currentUrl = new URL(window.location.href);
|
|
2292
|
+
const snapshotUuid = currentUrl.searchParams.get("__lr_snapshot");
|
|
2293
|
+
const recaptureUuid = currentUrl.searchParams.get("__lr_recapture");
|
|
2294
|
+
let inspectorRoot;
|
|
2295
|
+
let snapshotKeyMap;
|
|
2296
|
+
if (snapshotUuid) {
|
|
2297
|
+
const result = await activateSnapshotViewer(snapshotUuid, api);
|
|
2298
|
+
if (!result) {
|
|
2299
|
+
showEditorNotice("Snapshot not found or expired. Please check the dashboard.");
|
|
2300
|
+
return;
|
|
2301
|
+
}
|
|
2302
|
+
inspectorRoot = result.root;
|
|
2303
|
+
snapshotKeyMap = result.keyMap;
|
|
2304
|
+
}
|
|
1865
2305
|
const handleBlockSelect = (resolved) => {
|
|
1866
2306
|
sidebar.open(resolved);
|
|
1867
2307
|
inspector.clearHighlight();
|
|
1868
2308
|
};
|
|
1869
2309
|
const sidebar = new Sidebar(shadow, api, currentLocale, namespace, defaultLocale, handleBlockSelect);
|
|
1870
|
-
const inspector = new Inspector(shadow, handleBlockSelect);
|
|
2310
|
+
const inspector = new Inspector(shadow, handleBlockSelect, inspectorRoot, snapshotKeyMap);
|
|
1871
2311
|
const toolbar = new Toolbar(
|
|
1872
2312
|
shadow,
|
|
1873
2313
|
languages,
|
|
@@ -1898,7 +2338,43 @@ var LionRapidEditor = (() => {
|
|
|
1898
2338
|
sidebar.destroy();
|
|
1899
2339
|
host.remove();
|
|
1900
2340
|
clearSession();
|
|
1901
|
-
}
|
|
2341
|
+
},
|
|
2342
|
+
// Capture Snapshot — only available in live-page mode (not viewer) AND
|
|
2343
|
+
// only for PM/admin roles. Translators and proofreaders cannot capture;
|
|
2344
|
+
// the server enforces the same check on POST /api/editor/snapshots.
|
|
2345
|
+
//
|
|
2346
|
+
// If ?__lr_recapture={uuid} is present, the same button re-captures that
|
|
2347
|
+
// existing snapshot (PUT) instead of creating a new one (POST).
|
|
2348
|
+
snapshotUuid || !canCaptureSnapshots ? null : async () => {
|
|
2349
|
+
try {
|
|
2350
|
+
const snapshot = captureSnapshot();
|
|
2351
|
+
if (recaptureUuid) {
|
|
2352
|
+
const result = await api.recaptureSnapshot(recaptureUuid, snapshot);
|
|
2353
|
+
if (result) {
|
|
2354
|
+
showEditorNotice(
|
|
2355
|
+
`Re-captured as v${result.version} (${snapshot.textEntries.length} blocks). You can close this tab.`
|
|
2356
|
+
);
|
|
2357
|
+
} else {
|
|
2358
|
+
showEditorNotice("Re-capture failed. Please try again.");
|
|
2359
|
+
}
|
|
2360
|
+
} else {
|
|
2361
|
+
const result = await api.createSnapshot(snapshot);
|
|
2362
|
+
if (result) {
|
|
2363
|
+
showEditorNotice(
|
|
2364
|
+
`Snapshot captured (${snapshot.textEntries.length} blocks). Open it from the Snapshots tab in the dashboard.`
|
|
2365
|
+
);
|
|
2366
|
+
} else {
|
|
2367
|
+
showEditorNotice("Snapshot upload failed. Please try again.");
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
} catch (err) {
|
|
2371
|
+
console.error("[LionRapid Editor] Snapshot capture error:", err);
|
|
2372
|
+
showEditorNotice("Snapshot capture failed. See console for details.");
|
|
2373
|
+
}
|
|
2374
|
+
},
|
|
2375
|
+
// When in recapture mode, surface it to the toolbar so the button label
|
|
2376
|
+
// can change from "Capture" to "Save re-capture".
|
|
2377
|
+
recaptureUuid ? { mode: "recapture" } : void 0
|
|
1902
2378
|
);
|
|
1903
2379
|
sidebar.setProgressCallback((translated, total) => {
|
|
1904
2380
|
toolbar.updateProgress(translated, total);
|
|
@@ -1907,8 +2383,20 @@ var LionRapidEditor = (() => {
|
|
|
1907
2383
|
toolbar.updateProgress(sidebar.getTranslatedCount(), sidebar.getTotalCount());
|
|
1908
2384
|
});
|
|
1909
2385
|
const url = new URL(window.location.href);
|
|
2386
|
+
let dirty = false;
|
|
1910
2387
|
if (url.searchParams.has("__lr_editor")) {
|
|
1911
2388
|
url.searchParams.delete("__lr_editor");
|
|
2389
|
+
dirty = true;
|
|
2390
|
+
}
|
|
2391
|
+
if (url.searchParams.has("__lr_snapshot")) {
|
|
2392
|
+
url.searchParams.delete("__lr_snapshot");
|
|
2393
|
+
dirty = true;
|
|
2394
|
+
}
|
|
2395
|
+
if (url.searchParams.has("__lr_recapture")) {
|
|
2396
|
+
url.searchParams.delete("__lr_recapture");
|
|
2397
|
+
dirty = true;
|
|
2398
|
+
}
|
|
2399
|
+
if (dirty) {
|
|
1912
2400
|
history.replaceState(null, "", url.toString());
|
|
1913
2401
|
}
|
|
1914
2402
|
}
|