From 086c8abaf3563cdc09cc2e32fa4d9987eaeebefb Mon Sep 17 00:00:00 2001 From: Ramachandra Avuthu Date: Tue, 21 Jul 2026 22:42:09 -0400 Subject: [PATCH 1/2] feat(visual diff) diff view to support LOC and MSM file changes in EW --- blocks/canvas/canvas.js | 5 ++ .../editor-utils/prose-merge-conflicts.js | 20 +++++++ .../ew-canvas-header/ew-canvas-header.js | 9 ++++ blocks/canvas/ew-editor-doc/ew-editor-doc.js | 9 ++++ blocks/canvas/ew-editor-doc/prose.js | 21 +++++++- .../ew-editor-wysiwyg/ew-editor-wysiwyg.js | 21 ++++++++ blocks/edit/prose/diff/diff-utils.js | 28 ++++++---- .../prose-merge-conflicts.test.js | 54 +++++++++++++++++++ 8 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 blocks/canvas/editor-utils/prose-merge-conflicts.js create mode 100644 test/unit/blocks/canvas/editor-utils/prose-merge-conflicts.test.js diff --git a/blocks/canvas/canvas.js b/blocks/canvas/canvas.js index 0e7414c53..fc5f5862c 100644 --- a/blocks/canvas/canvas.js +++ b/blocks/canvas/canvas.js @@ -255,6 +255,11 @@ export default async function decorate(block) { syncEditorSplitLayout({ mountRoot, view: header.editorView }); installEditorSplitDrag(mountRoot); + mountRoot.addEventListener('nx-canvas-merge-conflicts', (e) => { + header.hasUnresolvedMergeConflicts = e.detail?.hasMergeConflicts ?? false; + if (header.hasUnresolvedMergeConflicts) header.setEditorView('content'); + }); + mountRoot.addEventListener('nx-editor-undo-state', (e) => { header.undoAvailable = e.detail?.canUndo ?? false; header.redoAvailable = e.detail?.canRedo ?? false; diff --git a/blocks/canvas/editor-utils/prose-merge-conflicts.js b/blocks/canvas/editor-utils/prose-merge-conflicts.js new file mode 100644 index 000000000..b8359fe46 --- /dev/null +++ b/blocks/canvas/editor-utils/prose-merge-conflicts.js @@ -0,0 +1,20 @@ +import { Plugin } from 'da-y-wrapper'; +import { checkForLocNodes } from '../../edit/prose/diff/diff-utils.js'; +import { debounce } from '../../edit/utils/helpers.js'; + +export function createMergeConflictsPlugin(onMergeConflictsChange) { + const debouncedCheck = debounce((view) => { + onMergeConflictsChange?.(checkForLocNodes(view)); + }, 500); + + return new Plugin({ + view(editorView) { + debouncedCheck(editorView); + return { + update(view, prevState) { + if (view.state.doc !== prevState.doc) debouncedCheck(view); + }, + }; + }, + }); +} diff --git a/blocks/canvas/ew-canvas-header/ew-canvas-header.js b/blocks/canvas/ew-canvas-header/ew-canvas-header.js index e45d851d7..0f2f73b5d 100644 --- a/blocks/canvas/ew-canvas-header/ew-canvas-header.js +++ b/blocks/canvas/ew-canvas-header/ew-canvas-header.js @@ -23,6 +23,7 @@ class EWCanvasHeader extends LitElement { undoAvailable: { type: Boolean }, redoAvailable: { type: Boolean }, authorized: { type: Boolean }, + hasUnresolvedMergeConflicts: { type: Boolean }, _chatDisabled: { state: true }, }; @@ -32,6 +33,7 @@ class EWCanvasHeader extends LitElement { this.undoAvailable = false; this.redoAvailable = false; this.authorized = true; + this.hasUnresolvedMergeConflicts = false; } connectedCallback() { @@ -82,7 +84,12 @@ class EWCanvasHeader extends LitElement { ); } + setEditorView(view) { + this._setEditorView(view); + } + _setEditorView(view) { + if (this.hasUnresolvedMergeConflicts && view !== 'content') return; if (!EDITOR_VIEWS.includes(view) || view === this.editorView) return; this.editorView = view; this.dispatchEvent( @@ -130,6 +137,7 @@ class EWCanvasHeader extends LitElement { type="button" class="segment ${this.editorView === 'layout' ? 'is-selected' : ''}" aria-pressed=${this.editorView === 'layout'} + ?disabled=${this.hasUnresolvedMergeConflicts} @click=${() => this._setEditorView('layout')} >Layout diff --git a/blocks/canvas/ew-editor-doc/ew-editor-doc.js b/blocks/canvas/ew-editor-doc/ew-editor-doc.js index 8e9e2ffa2..5eea64fd4 100644 --- a/blocks/canvas/ew-editor-doc/ew-editor-doc.js +++ b/blocks/canvas/ew-editor-doc/ew-editor-doc.js @@ -87,6 +87,14 @@ export class EwEditorDoc extends LitElement { })); } + _emitMergeConflictsState(hasMergeConflicts) { + this.dispatchEvent(new CustomEvent('nx-canvas-merge-conflicts', { + bubbles: true, + composed: true, + detail: { hasMergeConflicts }, + })); + } + _observeUndoManager(mgr) { this._stopObservingUndoManager(); if (!mgr) return; @@ -215,6 +223,7 @@ export class EwEditorDoc extends LitElement { permissions, setEditable: (editable) => this._setEditable(editable), getToken: () => token, + onMergeConflictsChange: (hasConflicts) => this._emitMergeConflictsState(hasConflicts), extraPlugins: [ createExtensionsBridgePlugin(), createTrackingPlugin( diff --git a/blocks/canvas/ew-editor-doc/prose.js b/blocks/canvas/ew-editor-doc/prose.js index f642b6d83..16c29f5f8 100644 --- a/blocks/canvas/ew-editor-doc/prose.js +++ b/blocks/canvas/ew-editor-doc/prose.js @@ -41,6 +41,9 @@ import { getNx } from '../../../scripts/utils.js'; import { getAuthToken } from '../../shared/utils.js'; import { generateColor, getCollabIdentity } from './utils/collab.js'; import { checkBlockLibraryConfigured } from '../editor-utils/block-slash.js'; +import { getDiffClass } from '../../edit/prose/diff/diff-utils.js'; +import { initDaMetadata } from '../../edit/utils/helpers.js'; +import { createMergeConflictsPlugin } from '../editor-utils/prose-merge-conflicts.js'; const { DA_ADMIN, DA_COLLAB, hashChange } = await import(`${getNx()}/utils/utils.js`); @@ -82,7 +85,7 @@ function checkLibraryConfiguredOnSync(wsProvider, canWrite) { export default async function initProse({ path, permissions, setEditable, getToken, - extraPlugins = [], + extraPlugins = [], onMergeConflictsChange, }) { const editor = document.createElement('div'); editor.className = 'da-prose-mirror'; @@ -211,7 +214,7 @@ export default async function initProse({ if (canWrite) { plugins.unshift(createSlashMenuPlugin(), createSelectionToolbarPlugin()); - plugins.push(imageFocalPoint()); + plugins.push(imageFocalPoint(), createMergeConflictsPlugin(onMergeConflictsChange)); } let state = EditorState.create({ schema, plugins }); @@ -219,11 +222,25 @@ export default async function initProse({ const fix = fixTables(state); if (fix) state = state.apply(fix.setMeta('addToHistory', false)); + const diffNodeViews = canWrite ? { + diff_added(node, view, getPos) { + const MergeConflictsAddedView = getDiffClass('da-diff-added', getSchema, () => {}, { isUpstream: false }); + return new MergeConflictsAddedView(node, view, getPos); + }, + diff_deleted(node, view, getPos) { + const MergeConflictsDeletedView = getDiffClass('da-diff-deleted', getSchema, () => {}, { isUpstream: true }); + return new MergeConflictsDeletedView(node, view, getPos); + }, + } : undefined; + viewRef = new EditorView(editor, { state, editable() { return canWrite; }, + nodeViews: diffNodeViews, }); + initDaMetadata(ydoc.getMap('daMetadata')); + const undoManager = yUndoPluginKey.getState(viewRef.state)?.undoManager ?? null; return { proseEl: editor, wsProvider, view: viewRef, ydoc, undoManager }; diff --git a/blocks/canvas/ew-editor-wysiwyg/ew-editor-wysiwyg.js b/blocks/canvas/ew-editor-wysiwyg/ew-editor-wysiwyg.js index 930c942d2..45fccaf64 100644 --- a/blocks/canvas/ew-editor-wysiwyg/ew-editor-wysiwyg.js +++ b/blocks/canvas/ew-editor-wysiwyg/ew-editor-wysiwyg.js @@ -56,15 +56,35 @@ export class EwEditorWysiwyg extends LitElement { this._syncCanvasVisibility(); }; this.parentElement?.addEventListener('nx-canvas-editor-active', this._onCanvasEditorActive); + this._onMergeConflictsChange = (e) => { + const hasMergeConflicts = e.detail?.hasMergeConflicts ?? false; + if (hasMergeConflicts) { + this._hadMergeConflicts = true; + } else if (this._hadMergeConflicts) { + this._hadMergeConflicts = false; + this._reloadIframe(); + } + }; + this.parentElement?.addEventListener('nx-canvas-merge-conflicts', this._onMergeConflictsChange); this._syncCanvasVisibility(); } disconnectedCallback() { this.parentElement?.removeEventListener('nx-canvas-editor-active', this._onCanvasEditorActive); + this.parentElement?.removeEventListener('nx-canvas-merge-conflicts', this._onMergeConflictsChange); this._clearQuickEditRetry(); super.disconnectedCallback(); } + _reloadIframe() { + const iframe = this.shadowRoot?.querySelector('iframe'); + try { + iframe?.contentWindow?.location.reload(); + } catch { + /* ignore — cross-origin reload should be allowed, but don't let this throw */ + } + } + get _iframeSrc() { const { org, repo, path } = this.ctx ?? {}; if (!org || !repo || !path || !this._cookieReady) return null; @@ -105,6 +125,7 @@ export class EwEditorWysiwyg extends LitElement { _resetCookieStateForCtxChange() { this._clearQuickEditRetry(); this._cookieReady = false; + this._hadMergeConflicts = false; } updated(changed) { diff --git a/blocks/edit/prose/diff/diff-utils.js b/blocks/edit/prose/diff/diff-utils.js index 603002ce2..70a425ed2 100644 --- a/blocks/edit/prose/diff/diff-utils.js +++ b/blocks/edit/prose/diff/diff-utils.js @@ -78,23 +78,29 @@ function setDiffLabelCssVars(daEditor) { host.style.setProperty('--diff-label-upstream', `'${labels.upstream}'`); } +function getDiffHost(view) { + const classicHost = document.querySelector('da-content')?.shadowRoot + ?.querySelector('da-editor'); + if (classicHost) return classicHost; + + const root = view?.dom?.getRootNode?.(); + return root instanceof ShadowRoot ? root.host : null; +} + let locCssLoading = false; -async function loadLocCss() { +async function loadLocCss(hostEl) { if (locCssLoading) return; locCssLoading = true; try { const locSheet = await getSheet('/blocks/edit/prose/diff/diff-utils.css'); - const daEditor = document.querySelector('da-content')?.shadowRoot - ?.querySelector('da-editor'); - - if (daEditor?.shadowRoot) { - const existingSheets = daEditor.shadowRoot.adoptedStyleSheets || []; - daEditor.shadowRoot.adoptedStyleSheets = [...existingSheets, locSheet]; + if (hostEl?.shadowRoot) { + const existingSheets = hostEl.shadowRoot.adoptedStyleSheets || []; + hostEl.shadowRoot.adoptedStyleSheets = [...existingSheets, locSheet]; // Set CSS custom properties for diff labels - setDiffLabelCssVars(daEditor); + setDiffLabelCssVars(hostEl); } } catch (error) { // eslint-disable-next-line no-console @@ -192,7 +198,7 @@ export function checkForLocNodes(view) { .some((node) => isLocNode(node) || hasListLocNode(node)); if (hasLocNodes) { - loadLocCss(); + loadLocCss(getDiffHost(view)); showGlobalDialog(view); } else { hideGlobalDialog(); @@ -323,7 +329,7 @@ export function getDiffClass(elName, getSchema, dispatchTransaction, { isUpstrea } renderTabbedInterface(nodeA, view, posA, nodeB) { - loadLocCss(); + loadLocCss(getDiffHost(view)); this.dom = createElement('div', 'loc-tabbed-container', { contentEditable: 'false' }); this.contentDOM = null; // Don't let ProseMirror manage content @@ -430,7 +436,7 @@ export function getDiffClass(elName, getSchema, dispatchTransaction, { isUpstrea } renderSingleNode(node, view, pos, upstream) { - loadLocCss(); + loadLocCss(getDiffHost(view)); const isDeleted = node.type.name === 'diff_deleted'; const viewClass = isDeleted ? 'loc-deleted-view' : 'loc-added-view'; diff --git a/test/unit/blocks/canvas/editor-utils/prose-merge-conflicts.test.js b/test/unit/blocks/canvas/editor-utils/prose-merge-conflicts.test.js new file mode 100644 index 000000000..a462fb877 --- /dev/null +++ b/test/unit/blocks/canvas/editor-utils/prose-merge-conflicts.test.js @@ -0,0 +1,54 @@ +import { expect } from '@esm-bundle/chai'; +import { createMergeConflictsPlugin } from '../../../../../blocks/canvas/editor-utils/prose-merge-conflicts.js'; +import { createTestEditor, destroyEditor } from '../../edit/prose/test-helpers.js'; + +const waitFor = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); }); + +describe('createMergeConflictsPlugin', () => { + let editor; + + afterEach(() => { + if (editor) destroyEditor(editor); + editor = undefined; + }); + + it('reports false shortly after mount when the doc has no merge-conflict nodes', async () => { + const calls = []; + const plugin = createMergeConflictsPlugin((hasMergeConflicts) => calls.push(hasMergeConflicts)); + editor = await createTestEditor({ additionalPlugins: [plugin] }); + + await waitFor(600); + + expect(calls).to.deep.equal([false]); + }); + + it('reports true once a diff_added node is inserted', async () => { + const calls = []; + const plugin = createMergeConflictsPlugin((hasMergeConflicts) => calls.push(hasMergeConflicts)); + editor = await createTestEditor({ additionalPlugins: [plugin] }); + + const { schema } = editor.view.state; + const para = schema.nodes.paragraph.create(null, schema.text('new')); + const diffAdded = schema.nodes.diff_added.create({}, para); + const tr = editor.view.state.tr.insert(editor.view.state.doc.content.size, diffAdded); + editor.view.dispatch(tr); + + await waitFor(600); + + expect(calls[calls.length - 1]).to.equal(true); + }); + + it('does not re-check on transactions that leave the doc unchanged', async () => { + const calls = []; + const plugin = createMergeConflictsPlugin((hasMergeConflicts) => calls.push(hasMergeConflicts)); + editor = await createTestEditor({ additionalPlugins: [plugin] }); + + await waitFor(600); // let the initial mount check land + calls.length = 0; + + editor.view.dispatch(editor.view.state.tr); // no doc change + await waitFor(600); + + expect(calls).to.deep.equal([]); + }); +}); From 95463642ea77a852937d40928216ca9b96b8d0b3 Mon Sep 17 00:00:00 2001 From: Ramachandra Avuthu Date: Thu, 23 Jul 2026 12:19:23 -0400 Subject: [PATCH 2/2] match with ew visuals --- .../ew-editor-doc/ew-editor-doc-diff.css | 172 ++++++++++++++++++ blocks/canvas/ew-editor-doc/prose.js | 4 +- blocks/canvas/ew-editor-doc/utils/teardown.js | 3 + blocks/edit/prose/diff/diff-utils.js | 14 +- 4 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 blocks/canvas/ew-editor-doc/ew-editor-doc-diff.css diff --git a/blocks/canvas/ew-editor-doc/ew-editor-doc-diff.css b/blocks/canvas/ew-editor-doc/ew-editor-doc-diff.css new file mode 100644 index 000000000..8438c96a5 --- /dev/null +++ b/blocks/canvas/ew-editor-doc/ew-editor-doc-diff.css @@ -0,0 +1,172 @@ +/* stylelint-disable selector-class-pattern */ + +/* + * Canvas-only visual overrides for the shared regional-edits diff UI + * (blocks/edit/prose/diff/*). Adopted in addition to diff-utils.css, only + * when the diff host is canvas's — classic editor keeps its + * original look untouched. Reuses EW's --s2-* tokens. + * + * EW's own chrome is mostly monotone gray, with color reserved for + * content-level meaning rather than button fills — so button chrome here is + * neutral gray throughout, and the green=local(added)/red=upstream(removed) + * split (matching ew-canvas-versions/ew-canvas-compare.css's ins/del + * treatment) is used only for the text/overlay rules further down, where it + * actually conveys which content is which. + */ + +.loc-tabbed-container { + background: var(--s2-gray-25); + box-shadow: 0 2px 8px rgb(0 0 0 / 8%); + border-radius: var(--s2-corner-radius-400); +} + +.diff-tab-content { + background: var(--s2-gray-25); +} + +.da-diff-btn-base { + border-radius: var(--s2-corner-radius-400); + height: 32px; +} + +.da-diff-btn-base-element { + font-size: var(--s2-component-s-regular-font-size, 0.875rem); + font-weight: 600; + + &:focus-visible { + outline: 2px solid var(--s2-blue-800); + outline-offset: 2px; + } +} + +/* Checkmark / close icons, replacing the "✓" / "✕" text glyphs */ +.da-diff-btn .confirm-btn::before, +.da-diff-btn-3part .diff-accept::before { + content: ''; + display: inline-block; + width: 16px; + height: 16px; + background-color: currentcolor; + mask: no-repeat center / 16px url('/img/icons/s2-icon-checkmark-20-n.svg'); +} + +.da-diff-btn-3part .diff-delete::before { + content: ''; + display: inline-block; + width: 16px; + height: 16px; + background-color: currentcolor; + mask: no-repeat center / 16px url('/img/icons/s2-icon-close-20-n.svg'); +} + +/* Neutral button chrome — EW's own chrome (headers, panels, toolbars) is + almost entirely gray, with color reserved for content-level meaning (see + the ins/del + overlay rules below), not button fills. So Local/Upstream/ + Difference all get the same treatment; the label text + icon already say + what each button does. + The base stylesheet's per-variant rules (e.g. .da-diff-btn.is-local + .switch-btn) are 3 classes deep — :is(...) here matches that specificity + so this actually wins the cascade instead of silently losing to it. */ +.da-diff-btn:is(.is-local, .is-upstream, .is-diff) .switch-btn, +.da-diff-btn-3part:is(.is-local, .is-upstream) .diff-label { + background: var(--s2-gray-100); + color: var(--s2-gray-800); +} + +.da-diff-btn:is(.is-local, .is-upstream, .is-diff) .confirm-btn, +.da-diff-btn-3part:is(.is-local, .is-upstream) .diff-accept, +.da-diff-btn-3part:is(.is-local, .is-upstream) .diff-delete { + background: var(--s2-gray-800); + color: var(--s2-gray-25); +} + +.da-diff-btn .confirm-btn:hover, +.da-diff-btn-3part .diff-accept:hover, +.da-diff-btn-3part .diff-delete:hover { + color: var(--s2-gray-25); + filter: brightness(1.1); +} + +.da-diff-btn.is-active { + border-color: var(--s2-gray-900); +} + +.da-diff-btn.is-active .switch-btn, +.da-diff-btn.is-active .confirm-btn { + color: var(--s2-gray-25); +} + +/* Flatter tooltip, matching EW's chrome instead of the dark-gradient default */ +.diff-tooltip { + background: var(--s2-gray-1000); + color: var(--s2-gray-25); + font-weight: 500; + border: none; + border-radius: var(--s2-corner-radius-400); + box-shadow: 0 2px 6px rgb(0 0 0 / 15%); + backdrop-filter: none; +} + +.diff-tooltip::before { + display: none; +} + +.diff-tooltip::after { + border-top-color: var(--s2-gray-1000); + filter: none; +} + +/* "Difference" tab text-level highlighting, matching ew-canvas-compare's ins/del treatment */ +.diff-tab-pane del.diffdel, +.diff-tab-pane del.diffmod { + background-color: var(--s2-red-200); + color: var(--s2-red-900); + border: none; +} + +.diff-tab-pane ins.diffins, +.diff-tab-pane ins.diffmod { + background-color: var(--s2-green-200); + color: var(--s2-green-900); + border: none; +} + +/* Single-node floating overlay: colors are set inline by diff-utils.js + (renderSingleNode), so !important is required to win over that. The + distinguishing class (loc-regional/loc-langstore) is only added once the + real overlay finishes its async load; until then the inline color briefly + shows through, unchanged. + These sit on top of the actual diff content (position: absolute, covering + the node), so the fill must stay translucent — same reason the original + inline colors were hex+alpha (#902ade33 etc.) rather than solid. */ +.loc-color-overlay.loc-regional { + background-color: color-mix(in srgb, var(--s2-green-200) 60%, transparent) !important; +} + +.loc-color-overlay.loc-langstore { + background-color: color-mix(in srgb, var(--s2-red-200) 60%, transparent) !important; +} + +.diff-bg-local { + background-color: color-mix(in srgb, var(--s2-green-200) 60%, transparent); +} + +.diff-bg-upstream { + background-color: color-mix(in srgb, var(--s2-red-200) 60%, transparent); +} + +.diff-bg-diff { + background-color: color-mix(in srgb, var(--s2-gray-100) 60%, transparent); +} + +/* Global "Keep All Local / Keep All Upstream" bar */ +.da-regional-edits-overlay { + padding: var(--s2-spacing-200); + border-radius: var(--s2-corner-radius-400); + background: var(--s2-gray-25); + box-shadow: 0 2px 8px rgb(0 0 0 / 8%); +} + +.da-regional-edits-actions { + gap: var(--s2-spacing-200); +} diff --git a/blocks/canvas/ew-editor-doc/prose.js b/blocks/canvas/ew-editor-doc/prose.js index 16c29f5f8..bdc5d3daa 100644 --- a/blocks/canvas/ew-editor-doc/prose.js +++ b/blocks/canvas/ew-editor-doc/prose.js @@ -41,7 +41,7 @@ import { getNx } from '../../../scripts/utils.js'; import { getAuthToken } from '../../shared/utils.js'; import { generateColor, getCollabIdentity } from './utils/collab.js'; import { checkBlockLibraryConfigured } from '../editor-utils/block-slash.js'; -import { getDiffClass } from '../../edit/prose/diff/diff-utils.js'; +import { getDiffClass, addActiveView } from '../../edit/prose/diff/diff-utils.js'; import { initDaMetadata } from '../../edit/utils/helpers.js'; import { createMergeConflictsPlugin } from '../editor-utils/prose-merge-conflicts.js'; @@ -239,6 +239,8 @@ export default async function initProse({ nodeViews: diffNodeViews, }); + if (canWrite) addActiveView(viewRef); + initDaMetadata(ydoc.getMap('daMetadata')); const undoManager = yUndoPluginKey.getState(viewRef.state)?.undoManager ?? null; diff --git a/blocks/canvas/ew-editor-doc/utils/teardown.js b/blocks/canvas/ew-editor-doc/utils/teardown.js index a066b17ca..98c59279e 100644 --- a/blocks/canvas/ew-editor-doc/utils/teardown.js +++ b/blocks/canvas/ew-editor-doc/utils/teardown.js @@ -1,3 +1,5 @@ +import { removeActiveView } from '../../../edit/prose/diff/diff-utils.js'; + export function teardownEditorDocResources({ clearPortHandler, awarenessOff, @@ -14,6 +16,7 @@ export function teardownEditorDocResources({ wsProvider.disconnect({ data: 'unmount' }); } if (view) { + removeActiveView(view); view.destroy(); } if (proseEl?.parentNode) { diff --git a/blocks/edit/prose/diff/diff-utils.js b/blocks/edit/prose/diff/diff-utils.js index 70a425ed2..eccabcf89 100644 --- a/blocks/edit/prose/diff/diff-utils.js +++ b/blocks/edit/prose/diff/diff-utils.js @@ -96,8 +96,14 @@ async function loadLocCss(hostEl) { const locSheet = await getSheet('/blocks/edit/prose/diff/diff-utils.css'); if (hostEl?.shadowRoot) { - const existingSheets = hostEl.shadowRoot.adoptedStyleSheets || []; - hostEl.shadowRoot.adoptedStyleSheets = [...existingSheets, locSheet]; + const sheets = [...(hostEl.shadowRoot.adoptedStyleSheets || []), locSheet]; + + if (hostEl.tagName === 'EW-EDITOR-DOC') { + const ewSheet = await getSheet('/blocks/canvas/ew-editor-doc/ew-editor-doc-diff.css'); + sheets.push(ewSheet); + } + + hostEl.shadowRoot.adoptedStyleSheets = sheets; // Set CSS custom properties for diff labels setDiffLabelCssVars(hostEl); @@ -282,6 +288,10 @@ export function addActiveView(view) { activeViews.add(view); } +export function removeActiveView(view) { + activeViews.delete(view); +} + export function getDiffClass(elName, getSchema, dispatchTransaction, { isUpstream } = {}) { return class { constructor(node, view, getPos) {