Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/lib/studio/monaco-style-manager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
const MONACO_STYLE_ATTRIBUTE = 'data-appwrite-studio-monaco-style';

const MONACO_STYLE_SELECTORS = [
'link[rel="stylesheet"][data-name^="vs/"]',
'style[data-name^="vs/"]',
'link[rel="stylesheet"][href*="monaco-editor"]'
] as const;

let monacoStyleObserver: MutationObserver | null = null;

function collectMonacoStyleNodes(): Element[] {
if (typeof document === 'undefined') {
return [];
}

const nodes: Element[] = [];
for (const selector of MONACO_STYLE_SELECTORS) {
document.head?.querySelectorAll(selector).forEach((node) => {
nodes.push(node);
});
}
return nodes;
}

function getMonacoStyleKey(node: Element): string | null {
const tag = node.tagName.toLowerCase();
const dataName = node.getAttribute('data-name');
if (dataName) {
return `${tag}:${dataName}`;
}

const id = node.getAttribute('id');
if (id) {
return `${tag}#${id}`;
}

if (node instanceof HTMLLinkElement && node.href) {
return `${tag}:${node.href}`;
}

if (node instanceof HTMLStyleElement && node.textContent) {
let hash = 0;
for (let index = 0; index < node.textContent.length; index += 1) {
hash = (hash << 5) - hash + node.textContent.charCodeAt(index);
hash |= 0;
}
return `${tag}:text-${hash}`;
}

return null;
}

function syncMonacoStyles(shadow: ShadowRoot) {
if (typeof document === 'undefined') {
return;
}

const existingKeys = new Set(
Array.from(
shadow.querySelectorAll<HTMLElement>(`[${MONACO_STYLE_ATTRIBUTE}]`)
).map((existing) => existing.getAttribute(MONACO_STYLE_ATTRIBUTE) ?? '')
);

for (const node of collectMonacoStyleNodes()) {
const key = getMonacoStyleKey(node);
if (!key || existingKeys.has(key)) {
continue;
}

const clone = node.cloneNode(true) as HTMLElement;
clone.setAttribute(MONACO_STYLE_ATTRIBUTE, key);
shadow.appendChild(clone);
existingKeys.add(key);
}
}

export function ensureMonacoStyles(shadow: ShadowRoot) {
syncMonacoStyles(shadow);

if (
monacoStyleObserver ||
typeof MutationObserver === 'undefined' ||
typeof document === 'undefined'
) {
return;
}

monacoStyleObserver = new MutationObserver(() => {
syncMonacoStyles(shadow);
});

monacoStyleObserver.observe(document.head, { childList: true });
}

4 changes: 3 additions & 1 deletion src/lib/studio/studio-widget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { app } from '$lib/stores/app';
import { get } from 'svelte/store';
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { ensureMonacoStyles } from './monaco-style-manager';

const COMPONENT_SELECTOR = 'imagine-web-components-wrapper[data-appwrite-studio]';
const STYLE_ATTRIBUTE = 'data-appwrite-studio-style';
const BLOCK_START_BASE_OFFSET = 48;
const INLINE_START_BASE_OFFSET = 8;

let component: HTMLElement | null = null;
let configInitialized = false;
let routingInitialized = false;
Expand Down Expand Up @@ -79,6 +79,7 @@ function injectStyles(node: HTMLElement, attempt = 0) {
}

if (shadow.querySelector<HTMLLinkElement>(`link[${STYLE_ATTRIBUTE}]`)) {
ensureMonacoStyles(shadow);
return;
}

Expand All @@ -87,6 +88,7 @@ function injectStyles(node: HTMLElement, attempt = 0) {
link.href = ImagineCss;
link.setAttribute(STYLE_ATTRIBUTE, 'true');
shadow.prepend(link);
ensureMonacoStyles(shadow);
})
.catch(() => {
/* no-op */
Expand Down
Loading