fix(forms): close completed drawers and restore portal styling
This commit is contained in:
15 files changed
+598
-9
No files matched your search
@@ -1,4 +1,5 @@
|
||||
import { decryptPortalStream, validatePortalPayload } from '../utils/portal-crypto.mjs';
|
||||
import { portalLinkPresentation } from '../utils/portal-presentation';
|
||||
|
||||
/** Build a passive DOM, never inject executable decrypted HTML. Remote media,
|
||||
* embedded forms and styles require owner review and are not loaded on unlock. */
|
||||
@@ -21,9 +22,21 @@ export function safePortalContent(html: string): DocumentFragment {
|
||||
element.setAttribute('rel', 'noopener noreferrer');
|
||||
element.setAttribute('referrerpolicy', 'no-referrer');
|
||||
if (url.origin !== location.origin && url.protocol === 'https:') element.setAttribute('target', '_blank');
|
||||
const presentation = portalLinkPresentation([...source.classList], source.style.backgroundColor, url.protocol);
|
||||
if (presentation) element.className = presentation;
|
||||
}
|
||||
} catch { /* Invalid destination remains visible text without navigation. */ }
|
||||
}
|
||||
// Preserve directory semantics without retaining arbitrary legacy attributes.
|
||||
if (source.tagName === 'TH' && ['row', 'col', 'rowgroup', 'colgroup'].includes(source.getAttribute('scope') ?? '')) {
|
||||
element.setAttribute('scope', source.getAttribute('scope')!);
|
||||
}
|
||||
if (['TD', 'TH'].includes(source.tagName)) {
|
||||
for (const attr of ['colspan', 'rowspan']) {
|
||||
const value = source.getAttribute(attr);
|
||||
if (value && /^[1-9]\d?$/.test(value)) element.setAttribute(attr, value);
|
||||
}
|
||||
}
|
||||
for (const child of source.childNodes) copy(child, element);
|
||||
target.appendChild(element);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user