From 53ba798235cc6f00df318b8aa4c4f34068f36cc9 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 15 Aug 2025 11:59:30 -0400 Subject: [PATCH 1/7] Load JSON-LD from hash fragments. Supports `startTab` and `json-ld` (for the document). --- playground/next/editor.bundle.js | 310 +++++++++++++++++++++---------- playground/next/editor.mjs | 7 + playground/next/index.html | 2 +- 3 files changed, 217 insertions(+), 102 deletions(-) diff --git a/playground/next/editor.bundle.js b/playground/next/editor.bundle.js index 6ff237b5..8e6ac79a 100644 --- a/playground/next/editor.bundle.js +++ b/playground/next/editor.bundle.js @@ -4253,6 +4253,34 @@ return name } + function crelt() { + var elt = arguments[0]; + if (typeof elt == "string") elt = document.createElement(elt); + var i = 1, next = arguments[1]; + if (next && typeof next == "object" && next.nodeType == null && !Array.isArray(next)) { + for (var name in next) if (Object.prototype.hasOwnProperty.call(next, name)) { + var value = next[name]; + if (typeof value == "string") elt.setAttribute(name, value); + else if (value != null) elt[name] = value; + } + i++; + } + for (; i < arguments.length; i++) add(elt, arguments[i]); + return elt + } + + function add(elt, child) { + if (typeof child == "string") { + elt.appendChild(document.createTextNode(child)); + } else if (child == null) ; else if (child.nodeType != null) { + elt.appendChild(child); + } else if (Array.isArray(child)) { + for (var i = 0; i < child.length; i++) add(elt, child[i]); + } else { + throw new RangeError("Unsupported child node: " + child) + } + } + function getSelection(root) { let target; // Browsers differ on whether shadow roots have a getSelection @@ -6668,8 +6696,7 @@ let handler = state.facet(exceptionSink); if (handler.length) handler[0](exception); - else if (window.onerror) - window.onerror(String(exception), context, undefined, undefined, exception); + else if (window.onerror && window.onerror(String(exception), context, undefined, undefined, exception)) ; else if (context) console.error(context + ":", exception); else @@ -6677,11 +6704,23 @@ } const editable = /*@__PURE__*/Facet.define({ combine: values => values.length ? values[0] : true }); let nextPluginID = 0; - const viewPlugin = /*@__PURE__*/Facet.define(); + const viewPlugin = /*@__PURE__*/Facet.define({ + combine(plugins) { + return plugins.filter((p, i) => { + for (let j = 0; j < i; j++) + if (plugins[j].plugin == p.plugin) + return false; + return true; + }); + } + }); /** View plugins associate stateful values with a view. They can influence the way the content is drawn, and are notified of things - that happen in the view. + that happen in the view. They optionally take an argument, in + which case you need to call [`of`](https://codemirror.net/6/docs/ref/#view.ViewPlugin.of) to create + an extension for the plugin. When the argument type is undefined, + you can use the plugin instance as an extension directly. */ class ViewPlugin { constructor( @@ -6705,7 +6744,14 @@ this.create = create; this.domEventHandlers = domEventHandlers; this.domEventObservers = domEventObservers; - this.extension = buildExtensions(this); + this.baseExtensions = buildExtensions(this); + this.extension = this.baseExtensions.concat(viewPlugin.of({ plugin: this, arg: undefined })); + } + /** + Create an extension for this plugin with the given argument. + */ + of(arg) { + return this.baseExtensions.concat(viewPlugin.of({ plugin: this, arg })); } /** Define a plugin from a constructor function that creates the @@ -6714,7 +6760,7 @@ static define(create, spec) { const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {}; return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => { - let ext = [viewPlugin.of(plugin)]; + let ext = []; if (deco) ext.push(decorations.of(view => { let pluginInst = view.plugin(plugin); @@ -6730,7 +6776,7 @@ editor view as argument. */ static fromClass(cls, spec) { - return ViewPlugin.define(view => new cls(view), spec); + return ViewPlugin.define((view, arg) => new cls(view, arg), spec); } } class PluginInstance { @@ -6738,18 +6784,19 @@ this.spec = spec; // When starting an update, all plugins have this field set to the // update object, indicating they need to be updated. When finished - // updating, it is set to `false`. Retrieving a plugin that needs to + // updating, it is set to `null`. Retrieving a plugin that needs to // be updated with `view.plugin` forces an eager update. this.mustUpdate = null; // This is null when the plugin is initially created, but // initialized on the first update. this.value = null; } + get plugin() { return this.spec && this.spec.plugin; } update(view) { if (!this.value) { if (this.spec) { try { - this.value = this.spec.create(view); + this.value = this.spec.plugin.create(view, this.spec.arg); } catch (e) { logException(view.state, e, "CodeMirror plugin crashed"); @@ -7705,8 +7752,7 @@ closestRect = rect; closestX = dx; closestY = dy; - let side = dy ? (y < rect.top ? -1 : 1) : dx ? (x < rect.left ? -1 : 1) : 0; - closestOverlap = !side || (side > 0 ? i < rects.length - 1 : i > 0); + closestOverlap = !dx ? true : x < rect.left ? i > 0 : i < rects.length - 1; } if (dx == 0) { if (y > rect.bottom && (!aboveRect || aboveRect.bottom < rect.bottom)) { @@ -7882,13 +7928,24 @@ // line before. This is used to detect such a result so that it can be // ignored (issue #401). function isSuspiciousSafariCaretResult(node, offset, x) { - let len; + let len, scan = node; if (node.nodeType != 3 || offset != (len = node.nodeValue.length)) return false; - for (let next = node.nextSibling; next; next = next.nextSibling) - if (next.nodeType != 1 || next.nodeName != "BR") + for (;;) { // Check that there is no content after this node + let next = scan.nextSibling; + if (next) { + if (next.nodeName == "BR") + break; return false; - return textRange(node, len - 1, len).getBoundingClientRect().left > x; + } + else { + let parent = scan.parentNode; + if (!parent || parent.nodeName == "DIV") + break; + scan = parent; + } + } + return textRange(node, len - 1, len).getBoundingClientRect().right > x; } // Chrome will move positions between lines to the start of the next line function isSuspiciousChromeCaretResult(node, offset, x) { @@ -8607,16 +8664,16 @@ return result[type] || (result[type] = { observers: [], handlers: [] }); } for (let plugin of plugins) { - let spec = plugin.spec; - if (spec && spec.domEventHandlers) - for (let type in spec.domEventHandlers) { - let f = spec.domEventHandlers[type]; + let spec = plugin.spec, handlers = spec && spec.plugin.domEventHandlers, observers = spec && spec.plugin.domEventObservers; + if (handlers) + for (let type in handlers) { + let f = handlers[type]; if (f) record(type).handlers.push(bindHandler(plugin.value, f)); } - if (spec && spec.domEventObservers) - for (let type in spec.domEventObservers) { - let f = spec.domEventObservers[type]; + if (observers) + for (let type in observers) { + let f = observers[type]; if (f) record(type).observers.push(bindHandler(plugin.value, f)); } @@ -9311,7 +9368,7 @@ heightForLine(length) { if (!this.lineWrapping) return this.lineHeight; - let lines = 1 + Math.max(0, Math.ceil((length - this.lineLength) / (this.lineLength - 5))); + let lines = 1 + Math.max(0, Math.ceil((length - this.lineLength) / Math.max(1, this.lineLength - 5))); return lines * this.lineHeight; } setDoc(doc) { this.doc = doc; return this; } @@ -10281,7 +10338,7 @@ refresh = true; if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) { let { lineHeight, charWidth, textHeight } = view.docView.measureTextSize(); - refresh = lineHeight > 0 && oracle.refresh(whiteSpace, lineHeight, charWidth, textHeight, contentWidth / charWidth, lineHeights); + refresh = lineHeight > 0 && oracle.refresh(whiteSpace, lineHeight, charWidth, textHeight, Math.max(5, contentWidth / charWidth), lineHeights); if (refresh) { view.docView.minWidth = 0; result |= 16 /* UpdateFlag.Geometry */; @@ -10806,13 +10863,16 @@ display: "flex", height: "100%", boxSizing: "border-box", - insetInlineStart: 0, - zIndex: 200 + zIndex: 200, }, + ".cm-gutters-before": { insetInlineStart: 0 }, + ".cm-gutters-after": { insetInlineEnd: 0 }, "&light .cm-gutters": { backgroundColor: "#f5f5f5", color: "#6c6c6c", - borderRight: "1px solid #ddd" + border: "0px solid #ddd", + "&.cm-gutters-before": { borderRightWidth: "1px" }, + "&.cm-gutters-after": { borderLeftWidth: "1px" }, }, "&dark .cm-gutters": { backgroundColor: "#333338", @@ -10862,6 +10922,21 @@ backgroundColor: "#333338", color: "white" }, + ".cm-dialog": { + padding: "2px 19px 4px 6px", + position: "relative", + "& label": { fontSize: "80%" }, + }, + ".cm-dialog-close": { + position: "absolute", + top: "3px", + right: "4px", + backgroundColor: "inherit", + border: "none", + font: "inherit", + fontSize: "14px", + padding: "0" + }, ".cm-tab": { display: "inline-block", overflow: "hidden", @@ -10988,7 +11063,7 @@ else this.flush(); }); - if (window.EditContext && view.constructor.EDIT_CONTEXT !== false && + if (window.EditContext && browser.android && view.constructor.EDIT_CONTEXT !== false && // Chrome <126 doesn't support inverted selections in edit context (#1392) !(browser.chrome && browser.chrome_version < 126)) { this.editContext = new EditContextManager(view); @@ -12174,8 +12249,8 @@ */ plugin(plugin) { let known = this.pluginMap.get(plugin); - if (known === undefined || known && known.spec != plugin) - this.pluginMap.set(plugin, known = this.plugins.find(p => p.spec == plugin) || null); + if (known === undefined || known && known.plugin != plugin) + this.pluginMap.set(plugin, known = this.plugins.find(p => p.plugin == plugin) || null); return known && known.update(this).value; } /** @@ -12999,6 +13074,8 @@ else if (isChar && (event.altKey || event.metaKey || event.ctrlKey) && // Ctrl-Alt may be used for AltGr on Windows !(browser.windows && event.ctrlKey && event.altKey) && + // Alt-combinations on macOS tend to be typed characters + !(browser.mac && event.altKey && !event.ctrlKey) && (baseName = base[event.keyCode]) && baseName != name) { if (runFor(scopeObj[prefix + modifiers(baseName, event, true)])) { handled = true; @@ -13578,7 +13655,7 @@ updateRange(view, deco, updateFrom, updateTo) { for (let r of view.visibleRanges) { let from = Math.max(r.from, updateFrom), to = Math.min(r.to, updateTo); - if (to > from) { + if (to >= from) { let fromLine = view.state.doc.lineAt(from), toLine = fromLine.to < to ? view.state.doc.lineAt(to) : fromLine; let start = Math.max(r.from, fromLine.from), end = Math.min(r.to, toLine.to); if (this.boundary) { @@ -14824,7 +14901,8 @@ lineMarkerChange: null, initialSpacer: null, updateSpacer: null, - domEventHandlers: {} + domEventHandlers: {}, + side: "before" }; const activeGutters = /*@__PURE__*/Facet.define(); /** @@ -14832,7 +14910,7 @@ determined by their extension priority. */ function gutter(config) { - return [gutters(), activeGutters.of(Object.assign(Object.assign({}, defaults$1), config))]; + return [gutters(), activeGutters.of({ ...defaults$1, ...config })]; } const unfixGutters = /*@__PURE__*/Facet.define({ combine: values => values.some(x => x) @@ -14856,15 +14934,20 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class { constructor(view) { this.view = view; + this.domAfter = null; this.prevViewport = view.viewport; this.dom = document.createElement("div"); - this.dom.className = "cm-gutters"; + this.dom.className = "cm-gutters cm-gutters-before"; this.dom.setAttribute("aria-hidden", "true"); this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px"; this.gutters = view.state.facet(activeGutters).map(conf => new SingleGutterView(view, conf)); - for (let gutter of this.gutters) - this.dom.appendChild(gutter.dom); this.fixed = !view.state.facet(unfixGutters); + for (let gutter of this.gutters) { + if (gutter.config.side == "after") + this.getDOMAfter().appendChild(gutter.dom); + else + this.dom.appendChild(gutter.dom); + } if (this.fixed) { // FIXME IE11 fallback, which doesn't support position: sticky, // by using position: relative + event handlers that realign the @@ -14874,6 +14957,17 @@ this.syncGutters(false); view.scrollDOM.insertBefore(this.dom, view.contentDOM); } + getDOMAfter() { + if (!this.domAfter) { + this.domAfter = document.createElement("div"); + this.domAfter.className = "cm-gutters cm-gutters-after"; + this.domAfter.setAttribute("aria-hidden", "true"); + this.domAfter.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px"; + this.domAfter.style.position = this.fixed ? "sticky" : ""; + this.view.scrollDOM.appendChild(this.domAfter); + } + return this.domAfter; + } update(update) { if (this.updateGutters(update)) { // Detach during sync when the viewport changed significantly @@ -14884,18 +14978,26 @@ this.syncGutters(vpOverlap < (vpB.to - vpB.from) * 0.8); } if (update.geometryChanged) { - this.dom.style.minHeight = (this.view.contentHeight / this.view.scaleY) + "px"; + let min = (this.view.contentHeight / this.view.scaleY) + "px"; + this.dom.style.minHeight = min; + if (this.domAfter) + this.domAfter.style.minHeight = min; } if (this.view.state.facet(unfixGutters) != !this.fixed) { this.fixed = !this.fixed; this.dom.style.position = this.fixed ? "sticky" : ""; + if (this.domAfter) + this.domAfter.style.position = this.fixed ? "sticky" : ""; } this.prevViewport = update.view.viewport; } syncGutters(detach) { let after = this.dom.nextSibling; - if (detach) + if (detach) { this.dom.remove(); + if (this.domAfter) + this.domAfter.remove(); + } let lineClasses = RangeSet.iter(this.view.state.facet(gutterLineClass), this.view.viewport.from); let classSet = []; let contexts = this.gutters.map(gutter => new UpdateContext(gutter, this.view.viewport, -this.view.documentPadding.top)); @@ -14929,8 +15031,11 @@ } for (let cx of contexts) cx.finish(); - if (detach) + if (detach) { this.view.scrollDOM.insertBefore(this.dom, after); + if (this.domAfter) + this.view.scrollDOM.appendChild(this.domAfter); + } } updateGutters(update) { let prev = update.startState.facet(activeGutters), cur = update.state.facet(activeGutters); @@ -14959,8 +15064,12 @@ if (gutters.indexOf(g) < 0) g.destroy(); } - for (let g of gutters) - this.dom.appendChild(g.dom); + for (let g of gutters) { + if (g.config.side == "after") + this.getDOMAfter().appendChild(g.dom); + else + this.dom.appendChild(g.dom); + } this.gutters = gutters; } return change; @@ -14969,15 +15078,18 @@ for (let view of this.gutters) view.destroy(); this.dom.remove(); + if (this.domAfter) + this.domAfter.remove(); } }, { provide: plugin => EditorView.scrollMargins.of(view => { let value = view.plugin(plugin); if (!value || value.gutters.length == 0 || !value.fixed) return null; + let before = value.dom.offsetWidth * view.scaleX, after = value.domAfter ? value.domAfter.offsetWidth * view.scaleX : 0; return view.textDirection == Direction.LTR - ? { left: value.dom.offsetWidth * view.scaleX } - : { right: value.dom.offsetWidth * view.scaleX }; + ? { left: before, right: after } + : { right: before, left: after }; }) }); function asArray(val) { return (Array.isArray(val) ? val : [val]); } @@ -15219,7 +15331,8 @@ let max = formatNumber(update.view, maxLineNumber(update.view.state.doc.lines)); return max == spacer.number ? spacer : new NumberMarker(max); }, - domEventHandlers: state.facet(lineNumberConfig).domEventHandlers + domEventHandlers: state.facet(lineNumberConfig).domEventHandlers, + side: "before" })); /** Create a line number gutter extension. @@ -18485,8 +18598,8 @@ const indentService = /*@__PURE__*/Facet.define(); /** Facet for overriding the unit by which indentation happens. Should - be a string consisting either entirely of the same whitespace - character. When not set, this defaults to 2 spaces. + be a string consisting entirely of the same whitespace character. + When not set, this defaults to 2 spaces. */ const indentUnit = /*@__PURE__*/Facet.define({ combine: values => { @@ -18655,7 +18768,8 @@ let inner = ast.resolveInner(pos, -1).resolve(pos, 0).enterUnfinishedNodesBefore(pos); if (inner != stack.node) { let add = []; - for (let cur = inner; cur && !(cur.from == stack.node.from && cur.type == stack.node.type); cur = cur.parent) + for (let cur = inner; cur && !(cur.from < stack.node.from || cur.to > stack.node.to || + cur.from == stack.node.from && cur.type == stack.node.type); cur = cur.parent) add.push(cur); for (let i = add.length - 1; i >= 0; i--) stack = { node: add[i], next: stack }; @@ -18969,6 +19083,8 @@ return Decoration.none; }, update(folded, tr) { + if (tr.isUserEvent("delete")) + tr.changes.iterChangedRanges((fromA, toA) => folded = clearTouchedFolds(folded, fromA, toA)); folded = folded.map(tr.changes); for (let e of tr.effects) { if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to)) { @@ -18983,17 +19099,8 @@ } } // Clear folded ranges that cover the selection head - if (tr.selection) { - let onSelection = false, { head } = tr.selection.main; - folded.between(head, head, (a, b) => { if (a < head && b > head) - onSelection = true; }); - if (onSelection) - folded = folded.update({ - filterFrom: head, - filterTo: head, - filter: (a, b) => b <= head || a >= head - }); - } + if (tr.selection) + folded = clearTouchedFolds(folded, tr.selection.main.head); return folded; }, provide: f => EditorView.decorations.from(f), @@ -19015,6 +19122,16 @@ return Decoration.set(ranges, true); } }); + function clearTouchedFolds(folded, from, to = from) { + let touched = false; + folded.between(from, to, (a, b) => { if (a < to && b > from) + touched = true; }); + return !touched ? folded : folded.update({ + filterFrom: from, + filterTo: to, + filter: (a, b) => a >= to || b <= from + }); + } function findFold(state, from, to) { var _a; let found = null; @@ -19187,7 +19304,7 @@ to fold or unfold the line). */ function foldGutter(config = {}) { - let fullConfig = Object.assign(Object.assign({}, foldGutterDefaults), config); + let fullConfig = { ...foldGutterDefaults, ...config }; let canFold = new FoldMarker(fullConfig, true), canUnfold = new FoldMarker(fullConfig, false); let markers = ViewPlugin.fromClass(class { constructor(view) { @@ -19222,7 +19339,9 @@ initialSpacer() { return new FoldMarker(fullConfig, false); }, - domEventHandlers: Object.assign(Object.assign({}, domEventHandlers), { click: (view, line, event) => { + domEventHandlers: { + ...domEventHandlers, + click: (view, line, event) => { if (domEventHandlers.click && domEventHandlers.click(view, line, event)) return true; let folded = findFold(view.state, line.from, line.to); @@ -19236,7 +19355,8 @@ return true; } return false; - } }) + } + } }), codeFolding() ]; @@ -19921,7 +20041,7 @@ advance() { let context = ParseContext.get(); let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt); - let end = Math.min(parseEnd, this.chunkStart + 2048 /* C.ChunkSize */); + let end = Math.min(parseEnd, this.chunkStart + 512 /* C.ChunkSize */); if (context) end = Math.min(end, context.viewport.to); while (this.parsedPos < end) @@ -20027,7 +20147,7 @@ length: this.parsedPos - this.chunkStart, nodeSet, topID: 0, - maxBufferLength: 2048 /* C.ChunkSize */, + maxBufferLength: 512 /* C.ChunkSize */, reused: this.chunkReused }); tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]); @@ -21576,34 +21696,6 @@ */ const indentWithTab = { key: "Tab", run: indentMore, shift: indentLess }; - function crelt() { - var elt = arguments[0]; - if (typeof elt == "string") elt = document.createElement(elt); - var i = 1, next = arguments[1]; - if (next && typeof next == "object" && next.nodeType == null && !Array.isArray(next)) { - for (var name in next) if (Object.prototype.hasOwnProperty.call(next, name)) { - var value = next[name]; - if (typeof value == "string") elt.setAttribute(name, value); - else if (value != null) elt[name] = value; - } - i++; - } - for (; i < arguments.length; i++) add(elt, arguments[i]); - return elt - } - - function add(elt, child) { - if (typeof child == "string") { - elt.appendChild(document.createTextNode(child)); - } else if (child == null) ; else if (child.nodeType != null) { - elt.appendChild(child); - } else if (Array.isArray(child)) { - for (var i = 0; i < child.length; i++) add(elt, child[i]); - } else { - throw new RangeError("Unsupported child node: " + child) - } - } - const basicNormalize = typeof String.prototype.normalize == "function" ? x => x.normalize("NFKD") : x => x; /** @@ -22533,14 +22625,16 @@ next = query.nextMatch(state, next.from, next.to); effects.push(EditorView.announce.of(state.phrase("replaced match on line $", state.doc.lineAt(from).number) + ".")); } + let changeSet = view.state.changes(changes); if (next) { - let off = changes.length == 0 || changes[0].from >= match.to ? 0 : match.to - match.from - replacement.length; - selection = EditorSelection.single(next.from - off, next.to - off); + selection = EditorSelection.single(next.from, next.to).map(changeSet); effects.push(announceMatch(view, next)); effects.push(state.facet(searchConfigFacet).scrollToMatch(selection.main, view)); } view.dispatch({ - changes, selection, effects, + changes: changeSet, + selection, + effects, userEvent: "input.replace" }); return true; @@ -28587,7 +28681,7 @@ const { blockQuote, commentString, lineWidth } = ctx.options; // 1. Block can't end in whitespace unless the last line is non-empty. // 2. Strings consisting of only whitespace are best rendered explicitly. - if (!blockQuote || /\n[\t ]+$/.test(value) || /^\s*$/.test(value)) { + if (!blockQuote || /\n[\t ]+$/.test(value)) { return quotedString(value, ctx); } const indent = ctx.indent || @@ -36134,7 +36228,7 @@ * * @author Dan Kogai (https://github.com/dankogai) */ - const version = '3.7.7'; + const version = '3.7.8'; /** * @deprecated use lowercase `version`. */ @@ -36288,17 +36382,24 @@ if (!b64re.test(asc)) throw new TypeError('malformed base64.'); asc += '=='.slice(2 - (asc.length & 3)); - let u24, bin = '', r1, r2; + let u24, r1, r2; + let binArray = []; // use array to avoid minor gc in loop for (let i = 0; i < asc.length;) { u24 = b64tab[asc.charAt(i++)] << 18 | b64tab[asc.charAt(i++)] << 12 | (r1 = b64tab[asc.charAt(i++)]) << 6 | (r2 = b64tab[asc.charAt(i++)]); - bin += r1 === 64 ? _fromCC(u24 >> 16 & 255) - : r2 === 64 ? _fromCC(u24 >> 16 & 255, u24 >> 8 & 255) - : _fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255); + if (r1 === 64) { + binArray.push(_fromCC(u24 >> 16 & 255)); + } + else if (r2 === 64) { + binArray.push(_fromCC(u24 >> 16 & 255, u24 >> 8 & 255)); + } + else { + binArray.push(_fromCC(u24 >> 16 & 255, u24 >> 8 & 255, u24 & 255)); + } } - return bin; + return binArray.join(''); }; /** * does what `window.atob` of web browsers do. @@ -39864,6 +39965,13 @@ ${O$2.repeat(r.depth)}}`:r.close="}";break}case f$4.TAG:e+=String(i),e+=a(f$4.PO '@context': this.doc['@context'] }; setEditorValue(this.contextEditor, this.contextDoc); + }, + gatherHash() { + const url = new URL(window.location); + const hash = new URLSearchParams(url.hash.slice(1)); + this.doc = JSON.parse(hash.get('json-ld') || {}); + setEditorValue(this.mainEditor, this.doc); + this.outputTab = hash.get('startTab').slice(4); } }).mount(); diff --git a/playground/next/editor.mjs b/playground/next/editor.mjs index 587e7541..80e013f2 100644 --- a/playground/next/editor.mjs +++ b/playground/next/editor.mjs @@ -538,5 +538,12 @@ window.app = createApp({ '@context': this.doc['@context'] }; setEditorValue(this.contextEditor, this.contextDoc); + }, + gatherHash() { + const url = new URL(window.location); + const hash = new URLSearchParams(url.hash.slice(1)); + this.doc = JSON.parse(hash.get('json-ld') || {}); + setEditorValue(this.mainEditor, this.doc); + this.outputTab = hash.get('startTab').slice(4); } }).mount(); diff --git a/playground/next/index.html b/playground/next/index.html index 9814eb2f..7074f9f9 100644 --- a/playground/next/index.html +++ b/playground/next/index.html @@ -29,7 +29,7 @@

JSON-LD Playground

Shift+Tab) to navigate out of the editor.

-
+
- -