+
${this._brand}
+ ${this._breadcrumbs}
${this._actions}
- `;
+ `;
}
}
diff --git a/nx2/blocks/shared/breadcrumb/breadcrumb.css b/nx2/blocks/shared/breadcrumb/breadcrumb.css
new file mode 100644
index 000000000..15d9537b0
--- /dev/null
+++ b/nx2/blocks/shared/breadcrumb/breadcrumb.css
@@ -0,0 +1,60 @@
+:host {
+ display: block;
+ min-width: 0;
+}
+
+.nx-breadcrumb ol {
+ display: flex;
+ align-items: center;
+ flex-wrap: nowrap;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ gap: 0;
+ min-width: 0;
+ font-family: var(--s2-font-family);
+ font-size: var(--s2-component-m-regular-font-size);
+ line-height: var(--s2-component-m-regular-line-height);
+ font-weight: var(--s2-component-m-regular-font-weight);
+ color: var(--s2-gray-700);
+}
+
+.crumb {
+ display: flex;
+ align-items: center;
+ min-width: 0;
+}
+
+.crumb-sep {
+ flex-shrink: 0;
+ width: 8px;
+ height: 8px;
+ margin-inline: var(--s2-spacing-75);
+ color: currentcolor;
+ display: block;
+}
+
+.crumb a,
+.crumb .current {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ min-width: 0;
+ color: inherit;
+ text-decoration: none;
+}
+
+.crumb a:hover {
+ color: var(--s2-gray-800);
+}
+
+.crumb a:focus-visible {
+ outline: 2px solid var(--s2-blue-800);
+ outline-offset: 2px;
+ border-radius: var(--s2-corner-radius-100);
+}
+
+.crumb .current {
+ color: var(--s2-gray-800);
+ font-weight: var(--s2-component-m-bold-font-weight);
+}
diff --git a/nx2/blocks/shared/breadcrumb/breadcrumb.js b/nx2/blocks/shared/breadcrumb/breadcrumb.js
new file mode 100644
index 000000000..7ec90c42c
--- /dev/null
+++ b/nx2/blocks/shared/breadcrumb/breadcrumb.js
@@ -0,0 +1,42 @@
+import { LitElement, html, nothing } from 'da-lit';
+import { loadStyle } from '../../../utils/utils.js';
+import { getConfig } from '../../../scripts/nx.js';
+import { pathSegmentsToCrumbs } from './utils.js';
+
+const style = await loadStyle(import.meta.url);
+const { codeBase } = getConfig();
+const CRUMB_SEP = html`
`;
+
+export default class NxBreadcrumb extends LitElement {
+ static properties = {
+ pathSegments: { type: Array, attribute: false },
+ baseUrl: { type: String, attribute: false },
+ };
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.shadowRoot.adoptedStyleSheets = [style];
+ }
+
+ render() {
+ const crumbs = pathSegmentsToCrumbs(this.pathSegments, { baseUrl: this.baseUrl });
+ if (!crumbs.length) return nothing;
+
+ return html`
+
+
+ ${crumbs.map((c, i) => html`
+
+ ${i > 0 ? CRUMB_SEP : nothing}
+ ${i === crumbs.length - 1
+ ? html`${c.label} `
+ : html`${c.label} `}
+
+ `)}
+
+
+ `;
+ }
+}
+
+customElements.define('nx-breadcrumb', NxBreadcrumb);
diff --git a/nx2/blocks/shared/breadcrumb/utils.js b/nx2/blocks/shared/breadcrumb/utils.js
new file mode 100644
index 000000000..97c4263b6
--- /dev/null
+++ b/nx2/blocks/shared/breadcrumb/utils.js
@@ -0,0 +1,39 @@
+export function hashStateToPathSegments(state) {
+ if (!state?.org || !state?.site) return undefined;
+ const rest = (state.path || '').split('/').filter(Boolean);
+ return [state.org, state.site, ...rest];
+}
+
+/**
+ * With `baseUrl`, applies current `location.search` then `hash` (base has no query).
+ */
+export function resolveBreadcrumbHref({ baseUrl, hash }) {
+ const h = hash.startsWith('#') ? hash : `#${hash}`;
+ if (!baseUrl) return h;
+ try {
+ const doc = typeof window !== 'undefined' && window.location
+ ? window.location.href
+ : 'https://localhost/';
+ const u = new URL(baseUrl, doc);
+ if (typeof window !== 'undefined' && window.location) {
+ u.search = window.location.search;
+ }
+ u.hash = h;
+ return u.href;
+ } catch {
+ return h;
+ }
+}
+
+export function pathSegmentsToCrumbs(segments, opts) {
+ if (!Array.isArray(segments) || segments.length === 0) return [];
+ const baseUrl = opts?.baseUrl;
+ const n = segments.length;
+ return segments.map((label, i) => {
+ if (i === n - 1) return { label, href: '' };
+ const hash = i === 0
+ ? `#/${segments[0]}/${segments[1]}`
+ : `#/${segments.slice(0, i + 1).join('/')}`;
+ return { label, href: resolveBreadcrumbHref({ baseUrl, hash }) };
+ });
+}
diff --git a/nx2/blocks/shared/dialog/dialog.css b/nx2/blocks/shared/dialog/dialog.css
new file mode 100644
index 000000000..7109783b3
--- /dev/null
+++ b/nx2/blocks/shared/dialog/dialog.css
@@ -0,0 +1,84 @@
+:host {
+ display: contents;
+ font-family: var(--s2-font-family, adobe-clean, 'Source Sans Pro', system-ui, sans-serif);
+}
+
+dialog {
+ box-sizing: border-box;
+ margin: 0;
+ padding: var(--s2-spacing-500);
+ border: 0;
+ width: 100%;
+ min-height: 100vh;
+ min-height: 100dvh;
+ max-width: none;
+ max-height: none;
+ background: none;
+ color: inherit;
+ overflow: auto;
+
+ &:focus-visible {
+ outline: none;
+ }
+
+ &[open] {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ &::backdrop {
+ background: light-dark(rgb(0 0 0 / 40%), rgb(0 0 0 / 60%));
+ backdrop-filter: blur(0.5px);
+ }
+}
+
+.panel {
+ box-sizing: border-box;
+ display: flex;
+ flex-flow: column nowrap;
+ min-width: min(400px, calc(100vw - 2 * var(--s2-spacing-500)));
+ max-width: min(480px, calc(100vw - 2 * var(--s2-spacing-500)));
+ max-height: min(90vh, calc(100vh - 2 * var(--s2-spacing-500)));
+ max-height: min(90dvh, calc(100dvh - 2 * var(--s2-spacing-500)));
+ padding: var(--s2-spacing-500);
+ gap: var(--s2-spacing-300);
+ border: 1px solid var(--s2-gray-200);
+ border-radius: var(--s2-corner-radius-500);
+ background: var(--s2-gray-25);
+ color: var(--s2-gray-900);
+ box-shadow: 0 8px 32px light-dark(rgb(0 0 0 / 18%), rgb(0 0 0 / 45%));
+}
+
+.heading {
+ flex-shrink: 0;
+ padding-bottom: var(--s2-spacing-200);
+ border-bottom: 1px solid var(--s2-gray-200);
+}
+
+.title {
+ margin: 0;
+ padding: 0;
+ font-size: var(--s2-component-xl-bold-font-size, 18px);
+ font-weight: var(--s2-component-xl-bold-font-weight, 700);
+ line-height: var(--s2-component-xl-bold-line-height, 22px);
+ color: var(--s2-gray-900);
+ overflow-wrap: break-word;
+}
+
+.body {
+ flex: 1 1 auto;
+ min-height: 0;
+ overflow: auto;
+ font-size: var(--s2-component-m-medium-font-size, 14px);
+ line-height: var(--s2-component-m-medium-line-height, 1.43);
+ color: var(--s2-gray-800);
+}
+
+.actions {
+ flex-shrink: 0;
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: flex-end;
+ gap: var(--s2-spacing-100);
+}
diff --git a/nx2/blocks/shared/dialog/dialog.js b/nx2/blocks/shared/dialog/dialog.js
new file mode 100644
index 000000000..5268b2f8b
--- /dev/null
+++ b/nx2/blocks/shared/dialog/dialog.js
@@ -0,0 +1,68 @@
+import { LitElement, html, nothing } from 'da-lit';
+import { loadStyle } from '../../../utils/utils.js';
+
+const styles = await loadStyle(import.meta.url);
+
+class NxDialog extends LitElement {
+ static properties = {
+ title: { type: String },
+ busy: { type: Boolean },
+ };
+
+ get _dialog() { return this.shadowRoot.querySelector('dialog'); }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.shadowRoot.adoptedStyleSheets = [styles];
+ }
+
+ firstUpdated() {
+ this._dialog.showModal();
+ queueMicrotask(() => {
+ const target = this.querySelector('[autofocus]');
+ if (!target) return;
+ target.focus();
+ if (target instanceof HTMLInputElement) target.select();
+ });
+ }
+
+ close() {
+ if (!this._dialog?.open) return;
+ this._dialog.close();
+ this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }));
+ }
+
+ _onCancel(e) {
+ e.preventDefault();
+ if (!this.busy) this.close();
+ }
+
+ _onBackdropClick(e) {
+ if (e.target !== e.currentTarget) return;
+ if (!this.busy) this.close();
+ }
+
+ render() {
+ const titleText = this.title?.trim();
+ return html`
+
+
+ ${titleText ? html`
+
+
${titleText}
+
+ ` : nothing}
+
+
+
+
+ `;
+ }
+}
+
+customElements.define('nx-dialog', NxDialog);
diff --git a/nx2/blocks/shared/menu/menu.css b/nx2/blocks/shared/menu/menu.css
new file mode 100644
index 000000000..dc9eef434
--- /dev/null
+++ b/nx2/blocks/shared/menu/menu.css
@@ -0,0 +1,72 @@
+:host {
+ display: contents;
+ font-family: var(--s2-font-family);
+
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+}
+
+.menu-section {
+ display: block;
+ padding: var(--s2-spacing-75) var(--s2-spacing-100);
+ color: light-dark(var(--s2-gray-600), var(--s2-gray-700));
+ font-size: var(--s2-body-size-xs);
+ font-weight: var(--s2-heading-font-weight);
+ cursor: default;
+}
+
+.menu-item {
+ display: flex;
+ align-items: center;
+ gap: var(--s2-spacing-100);
+ width: 100%;
+ padding: var(--s2-spacing-75) var(--s2-spacing-100);
+ border: none;
+ border-radius: var(--s2-corner-radius-200);
+ background: none;
+ color: var(--s2-gray-900);
+ font-family: inherit;
+ font-size: var(--s2-body-size-s);
+ margin-bottom: 2px;
+ cursor: pointer;
+ box-sizing: border-box;
+ outline: none;
+}
+
+.menu-item:hover,
+.menu-item-active {
+ background: var(--s2-gray-200);
+}
+
+.menu-item-icon {
+ display: flex;
+ align-items: center;
+ flex-shrink: 0;
+ width: 16px;
+ height: 16px;
+
+ svg {
+ display: block;
+ width: 100%;
+ height: 100%;
+ }
+
+ path {
+ fill: var(--s2-gray-800);
+ }
+
+ text {
+ fill: var(--s2-gray-800);
+ }
+}
+
+.menu-divider {
+ margin: calc(var(--s2-spacing-100) - 2px) var(--s2-spacing-200)
+ var(--s2-spacing-100);
+ border: none;
+ border-top: 1px solid var(--s2-gray-200);
+ border-radius: var(--s2-corner-radius-75);
+}
diff --git a/nx2/blocks/shared/menu/menu.js b/nx2/blocks/shared/menu/menu.js
new file mode 100644
index 000000000..c426a8892
--- /dev/null
+++ b/nx2/blocks/shared/menu/menu.js
@@ -0,0 +1,145 @@
+import { LitElement, html, nothing } from 'da-lit';
+import { loadStyle } from '../../../utils/utils.js';
+import { getConfig } from '../../../scripts/nx.js';
+import '../popover/popover.js';
+import { listKeydown } from '../utils/list-nav.js';
+
+const { codeBase } = getConfig();
+
+const styles = await loadStyle(import.meta.url);
+
+class NxMenu extends LitElement {
+ static properties = {
+ items: { attribute: false },
+ _active: { state: true },
+ ignoreFocus: { attribute: true },
+ scoped: { type: Boolean },
+ };
+
+ get _popover() { return this.shadowRoot.querySelector('nx-popover'); }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.shadowRoot.adoptedStyleSheets = [styles];
+ }
+
+ firstUpdated() {
+ this._wireTrigger(this.shadowRoot.querySelector('slot[name="trigger"]'));
+ }
+
+ _wireTrigger(slot) {
+ const [trigger] = slot.assignedElements();
+ if (!trigger || trigger === this._trigger) return;
+ this._trigger = trigger;
+ this._popover.anchor = trigger;
+ trigger.setAttribute('aria-haspopup', 'menu');
+ trigger.setAttribute('aria-expanded', 'false');
+ trigger.addEventListener('click', () => this._toggle(trigger));
+ }
+
+ _onTriggerSlotChange(e) {
+ this._wireTrigger(e.target);
+ }
+
+ _onMenuToggle(e) {
+ if (e.newState !== 'open') return;
+ this._trigger?.toggleAttribute('data-active', true);
+ this._trigger?.setAttribute('aria-expanded', 'true');
+ const first = this.items?.find((i) => !i.divider && !i.section);
+ this._active = first?.id;
+ this.updateComplete.then(() => {
+ if (!this.ignoreFocus) this.shadowRoot.querySelector(`[data-id="${this._active}"]`)?.focus();
+ });
+ }
+
+ show({ anchor, placement } = {}) {
+ this._popover?.show({
+ anchor,
+ placement: placement ?? this.getAttribute('placement') ?? 'below',
+ });
+ }
+
+ close() {
+ this._popover?.close();
+ }
+
+ reposition() {
+ this._popover?.reposition();
+ }
+
+ get open() {
+ return this._popover?.open ?? false;
+ }
+
+ _toggle(trigger) {
+ if (this.open) {
+ this.close();
+ return;
+ }
+ this.show({ anchor: trigger });
+ }
+
+ _onClose() {
+ this._trigger?.toggleAttribute('data-active', false);
+ this._trigger?.setAttribute('aria-expanded', 'false');
+ }
+
+ _select(item) {
+ this.close();
+ this.dispatchEvent(new CustomEvent('select', { detail: { id: item.id }, bubbles: true, composed: true }));
+ }
+
+ handleKey(key) {
+ return listKeydown(key, {
+ items: this.items,
+ active: this._active,
+ itemKey: 'id',
+ shadowRoot: this.shadowRoot,
+ setActive: (val) => { this._active = val; },
+ onSelect: (item) => this._select(item),
+ onClose: () => this.close(),
+ focusActiveItem: !this.ignoreFocus,
+ });
+ }
+
+ _onKeydown(e) {
+ const handled = this.handleKey(e.key);
+ if (handled) e.preventDefault();
+ }
+
+ _renderItem(item) {
+ if (item.divider) return html`
`;
+ if (item.section) return html`
`;
+ if (!item.label || !item.id) return nothing;
+
+ return html`
+
+
+
+ `;
+ }
+
+ render() {
+ return html`
+
+
+
+ ${this.items?.map((item) => this._renderItem(item))}
+
+
+ `;
+ }
+}
+
+customElements.define('nx-menu', NxMenu);
diff --git a/nx2/blocks/shared/picker/picker.css b/nx2/blocks/shared/picker/picker.css
new file mode 100644
index 000000000..3e99f78f0
--- /dev/null
+++ b/nx2/blocks/shared/picker/picker.css
@@ -0,0 +1,102 @@
+:host {
+ display: contents;
+ font-family: var(--s2-font-family);
+ font-size: var(--s2-body-size-xs);
+
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+}
+
+.picker-section {
+ padding: var(--s2-spacing-75) var(--s2-spacing-100) var(--s2-spacing-50);
+ margin: 0;
+ list-style: none;
+ font-size: var(--s2-body-size-xs);
+ font-weight: 600;
+ letter-spacing: 0.02em;
+ color: var(--s2-gray-700);
+ text-transform: none;
+}
+
+.picker-trigger {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ padding: var(--s2-spacing-75) var(--s2-spacing-100);
+ border: none;
+ background: none;
+ color: var(--s2-gray-900);
+ font-family: inherit;
+ font-size: var(--s2-body-size-xs);
+ cursor: pointer;
+ outline: none;
+ width: fit-content;
+
+ &[data-active] {
+ background: var(--s2-gray-200);
+ border-color: var(--s2-gray-400);
+ }
+
+ &:focus-visible {
+ outline: 2px solid var(--s2-focus-ring-color, currentColor);
+ outline-offset: 2px;
+ }
+
+ .picker-chevron {
+ width: 10px;
+ height: 10px;
+ flex-shrink: 0;
+ display: block;
+ transform: rotate(90deg);
+ }
+}
+
+.picker-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: var(--s2-spacing-100);
+ width: 100%;
+ padding: var(--s2-spacing-75) var(--s2-spacing-100);
+ border: none;
+ border-radius: var(--s2-corner-radius-200);
+ background: none;
+ color: var(--s2-gray-900);
+ font-family: inherit;
+ font-size: var(--s2-body-size-xs);
+ margin-bottom: 2px;
+ cursor: pointer;
+ box-sizing: border-box;
+ outline: none;
+
+ &:hover,
+ &.picker-item-active {
+ background: var(--s2-gray-200);
+ }
+}
+
+.picker-open-in-icon {
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+ display: block;
+ pointer-events: none;
+}
+
+.picker-check {
+ width: 14px;
+ height: 14px;
+ flex-shrink: 0;
+ color: var(--s2-gray-900);
+}
+
+.picker-divider {
+ margin: calc(var(--s2-spacing-100) - 2px) var(--s2-spacing-200)
+ var(--s2-spacing-100);
+ border: none;
+ border-top: 1px solid var(--s2-gray-200);
+ border-radius: var(--s2-corner-radius-75);
+}
diff --git a/nx2/blocks/shared/picker/picker.js b/nx2/blocks/shared/picker/picker.js
new file mode 100644
index 000000000..bfd547a53
--- /dev/null
+++ b/nx2/blocks/shared/picker/picker.js
@@ -0,0 +1,189 @@
+import { LitElement, html, nothing } from 'da-lit';
+import { loadStyle } from '../../../utils/utils.js';
+import { getConfig } from '../../../scripts/nx.js';
+import '../popover/popover.js';
+import { listKeydown } from '../utils/list-nav.js';
+
+const styles = await loadStyle(import.meta.url);
+const { codeBase } = getConfig();
+const CHEVRON_HREF = `${codeBase}/img/icons/s2-icon-chevronleft-10-n.svg#icon`;
+
+// todo: replace with s2 icon once tools PR is merged
+const CHECKMARK = html`
+
+ `;
+
+class NxPicker extends LitElement {
+ static properties = {
+ items: { attribute: false },
+ value: {},
+ /**
+ * Non-empty string shown on the trigger instead of the label from `items` for `value`.
+ * Set to '' to use the normal label lookup again.
+ */
+ labelOverride: { type: String },
+ _active: { state: true },
+ ignoreFocus: { attribute: true },
+ };
+
+ get _popover() { return this.shadowRoot.querySelector('nx-popover'); }
+
+ get _button() { return this.shadowRoot.querySelector('.picker-trigger'); }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.shadowRoot.adoptedStyleSheets = [styles];
+ }
+
+ get open() { return this._popover?.open ?? false; }
+
+ get _selectedLabel() {
+ return this.items?.find((i) => i.value === this.value)?.label ?? '';
+ }
+
+ get _triggerLabel() {
+ const o = this.labelOverride;
+ if (typeof o === 'string' && o.length > 0) return o;
+ return this._selectedLabel;
+ }
+
+ show() {
+ this._popover?.show({
+ anchor: this._button,
+ placement: this.getAttribute('placement') ?? 'below',
+ });
+ }
+
+ close() { this._popover?.close(); }
+
+ _toggle() {
+ if (this.open) {
+ this.close();
+ return;
+ }
+ this.show();
+ }
+
+ _onClose() {
+ this._button?.toggleAttribute('data-active', false);
+ this._button?.setAttribute('aria-expanded', 'false');
+ }
+
+ _onPopoverToggle(e) {
+ if (e.newState !== 'open') return;
+ const selectable = this.items?.filter((i) => !i.divider && !i.section) ?? [];
+ const matched = selectable.some((i) => i.value === this.value);
+ this._active = matched ? this.value : (selectable[0]?.value ?? this.value);
+ this.updateComplete.then(() => {
+ const key = String(this._active ?? '');
+ const esc = typeof CSS !== 'undefined' && CSS.escape ? CSS.escape(key) : key.replace(/"/g, '\\"');
+ if (!this.ignoreFocus) this.shadowRoot.querySelector(`[data-value="${esc}"]`)?.focus();
+ });
+ }
+
+ _select(item) {
+ this.value = item.value;
+ this.close();
+ this.dispatchEvent(new CustomEvent('change', { detail: { value: item.value }, bubbles: true, composed: true }));
+ }
+
+ handleKey(key) {
+ return listKeydown(key, {
+ items: this.items,
+ active: this._active,
+ itemKey: 'value',
+ shadowRoot: this.shadowRoot,
+ setActive: (val) => { this._active = val; },
+ onSelect: (item) => this._handleItemActivated(item),
+ onClose: () => this.close(),
+ focusActiveItem: !this.ignoreFocus,
+ });
+ }
+
+ _handleItemActivated(item) {
+ if (!item) return;
+ if (item.action) {
+ this.close();
+ this.dispatchEvent(new CustomEvent('change', { detail: { value: item.value }, bubbles: true, composed: true }));
+ return;
+ }
+ this._select(item);
+ }
+
+ _onTriggerKeydown(e) {
+ if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && !this.open) {
+ e.preventDefault();
+ this.show();
+ }
+ }
+
+ _onKeydown(e) {
+ const handled = this.handleKey(e.key);
+ if (handled) e.preventDefault();
+ }
+
+ _renderItem(item) {
+ if (item.section) {
+ return html`
${item.section} `;
+ }
+ if (item.divider) return html`
`;
+ if (!item.label || item.value === undefined) return nothing;
+
+ const selected = item.value === this.value;
+ const active = item.value === this._active;
+
+ return html`
+
+ this._handleItemActivated(item)}
+ @mouseenter=${() => { this._active = item.value; }}
+ @focus=${() => { this._active = item.value; }}
+ >
+ ${item.label}
+ ${item.trailingIcon ? html`
+ ` : nothing}
+ ${selected ? CHECKMARK : nothing}
+
+
+ `;
+ }
+
+ render() {
+ return html`
+
+ ${this._triggerLabel}
+
+
+
+
+ ${this.items?.map((item) => this._renderItem(item))}
+
+
+ `;
+ }
+}
+
+customElements.define('nx-picker', NxPicker);
diff --git a/nx2/blocks/shared/popover/popover.css b/nx2/blocks/shared/popover/popover.css
new file mode 100644
index 000000000..df503503c
--- /dev/null
+++ b/nx2/blocks/shared/popover/popover.css
@@ -0,0 +1,27 @@
+:host {
+ --popover-gap: 4px;
+
+ position: fixed;
+ z-index: 100;
+ display: none;
+ right: auto;
+ bottom: auto;
+ flex-direction: column;
+ padding: var(--s2-spacing-100);
+ min-width: 100px;
+ background: var(--s2-gray-25);
+ border: 1px solid var(--s2-gray-200);
+ border-radius: var(--s2-corner-radius-500);
+ box-shadow: 0 4px 16px rgb(0 0 0 / 12%);
+}
+
+:host([open]),
+:host(:popover-open) {
+ display: block;
+}
+
+slot[name="actions"]::slotted(*) {
+ display: flex;
+ gap: var(--s2-spacing-75);
+ justify-content: flex-end;
+}
diff --git a/nx2/blocks/shared/popover/popover.js b/nx2/blocks/shared/popover/popover.js
new file mode 100644
index 000000000..af2c997dc
--- /dev/null
+++ b/nx2/blocks/shared/popover/popover.js
@@ -0,0 +1,152 @@
+import { LitElement, html } from 'da-lit';
+import { loadStyle } from '../../../utils/utils.js';
+
+const styles = await loadStyle(import.meta.url);
+const SUPPORTS_POPOVER = typeof HTMLElement.prototype.showPopover === 'function';
+
+class NxPopover extends LitElement {
+ static properties = {
+ open: { type: Boolean, reflect: true },
+ scoped: { type: Boolean },
+ persistent: { type: Boolean },
+ };
+
+ _placement = 'below';
+
+ get anchor() { return this._anchor; }
+
+ set anchor(val) {
+ this._anchor = val;
+ if (this.open) this._position();
+ }
+
+ get _useNative() { return SUPPORTS_POPOVER && !this.scoped; }
+
+ _onKeydown = (e) => { if (e.key === 'Escape') this.close(); };
+
+ _onOutsideClick = (e) => {
+ const path = e.composedPath();
+ if (!path.includes(this) && !path.includes(this._anchor)) this.close();
+ };
+
+ _onWindowBlur = () => {
+ // Clicks in outer frames don't reach this document. Close when focus leaves.
+ requestAnimationFrame(() => { if (!document.hasFocus()) this.close(); });
+ };
+
+ _onToggle = (e) => {
+ if (e.newState === 'closed') this.close();
+ };
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.shadowRoot.adoptedStyleSheets = [styles];
+ if (this._useNative) {
+ this.setAttribute('popover', 'manual');
+ this.addEventListener('toggle', this._onToggle);
+ }
+ }
+
+ disconnectedCallback() {
+ super.disconnectedCallback();
+ if (this._useNative) this.removeEventListener('toggle', this._onToggle);
+ this._removeListeners();
+ }
+
+ show({ anchor, x, y, placement } = {}) {
+ this._coords = (!anchor && (x !== undefined || y !== undefined)) ? { x, y } : null;
+ this._placement = placement ?? this.getAttribute('placement') ?? 'below';
+ this.anchor = anchor ?? null;
+ this.open = true;
+ }
+
+ updated(changed) {
+ if (!changed.has('open')) return;
+ if (this.open) {
+ this._addListeners();
+ this._position();
+ if (this._useNative) this.togglePopover(true);
+ } else {
+ this._removeListeners();
+ if (this._useNative) this.togglePopover(false);
+ }
+ }
+
+ close() {
+ if (!this.open) return;
+ this.open = false;
+ this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }));
+ }
+
+ reposition() {
+ if (this.open) this._position();
+ }
+
+ _position() {
+ if (this._coords) {
+ const { x, y } = this._coords;
+ this.style.left = `${x}px`;
+ this.style.top = `${y}px`;
+ return;
+ }
+
+ if (!this._anchor) return;
+ const rect = this._anchor.getBoundingClientRect();
+ const gap = parseFloat(getComputedStyle(this).getPropertyValue('--popover-gap')) ?? 0;
+
+ this.style.visibility = 'hidden';
+
+ // For scoped popovers, position:fixed is relative to the containing block.
+ // Measure it by sizing to 100%/100% — the browser resolves percentages
+ // against the containing block, so getBoundingClientRect() gives its rect.
+ let cb = null;
+ if (this.scoped) {
+ Object.assign(this.style, { top: '0', left: '0', width: '100%', height: '100%' });
+ cb = this.getBoundingClientRect();
+ this.style.width = '';
+ this.style.height = '';
+ }
+ requestAnimationFrame(() => {
+ const pop = this.getBoundingClientRect();
+ const cbTop = cb?.top ?? 0;
+ let { left } = rect;
+ let placement = this._placement;
+ if (placement === 'auto') {
+ const spaceBelow = (cb?.bottom ?? window.innerHeight) - rect.bottom - gap;
+ const spaceAbove = rect.top - cbTop - gap;
+ placement = spaceBelow < pop.height && spaceAbove >= pop.height ? 'above' : 'below';
+ this._placement = placement;
+ }
+
+ if (placement === 'below-end' || left + pop.width > (cb?.right ?? window.innerWidth)) left = rect.right - pop.width;
+
+ this.style.left = `${left - (cb?.left ?? 0)}px`;
+ this.style.top = placement === 'above'
+ ? `${rect.top - gap - pop.height - cbTop}px`
+ : `${rect.bottom + gap - cbTop}px`;
+ this.style.visibility = '';
+ });
+ }
+
+ _addListeners() {
+ if (this.persistent) return;
+ document.addEventListener('keydown', this._onKeydown);
+ document.addEventListener('pointerdown', this._onOutsideClick);
+ window.addEventListener('blur', this._onWindowBlur);
+ }
+
+ _removeListeners() {
+ document.removeEventListener('keydown', this._onKeydown);
+ document.removeEventListener('pointerdown', this._onOutsideClick);
+ window.removeEventListener('blur', this._onWindowBlur);
+ }
+
+ render() {
+ return html`
+
+
+ `;
+ }
+}
+
+customElements.define('nx-popover', NxPopover);
diff --git a/nx2/blocks/shared/toast/toast-host.css b/nx2/blocks/shared/toast/toast-host.css
new file mode 100644
index 000000000..242eaf493
--- /dev/null
+++ b/nx2/blocks/shared/toast/toast-host.css
@@ -0,0 +1,14 @@
+#nx-toast-host {
+ position: fixed;
+ z-index: 10001;
+ left: 0;
+ right: 0;
+ bottom: var(--s2-spacing-400);
+ display: flex;
+ flex-direction: column-reverse;
+ align-items: center;
+ gap: var(--s2-spacing-100);
+ padding: 0 var(--s2-spacing-300);
+ box-sizing: border-box;
+ pointer-events: none;
+}
diff --git a/nx2/blocks/shared/toast/toast.css b/nx2/blocks/shared/toast/toast.css
new file mode 100644
index 000000000..67fd14472
--- /dev/null
+++ b/nx2/blocks/shared/toast/toast.css
@@ -0,0 +1,77 @@
+:host {
+ display: block;
+ box-sizing: border-box;
+ max-width: min(22rem, calc(100vw - 2 * var(--s2-spacing-300)));
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-family: inherit;
+}
+
+.toast {
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: var(--s2-spacing-100);
+ padding: var(--s2-spacing-100) var(--s2-spacing-200);
+ border-radius: var(--s2-corner-radius-100);
+ border: 1px solid;
+ box-shadow: 0 2px 8px light-dark(rgb(0 0 0 / 12%), rgb(0 0 0 / 35%));
+
+ &.toast-success {
+ border-color: var(--s2-green-800);
+ background: var(--s2-green-900);
+ color: var(--s2-gray-25);
+ }
+
+ &.toast-error {
+ border-color: var(--s2-red-900);
+ background: var(--s2-red-900);
+ color: var(--s2-gray-25);
+ }
+}
+
+.text {
+ flex: 1 1 auto;
+ margin: 0;
+ min-width: 0;
+ font-size: var(--s2-component-m-medium-font-size, 14px);
+ line-height: var(--s2-component-m-medium-line-height, 1.4);
+ overflow-wrap: anywhere;
+ white-space: pre-line;
+}
+
+.close {
+ flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0;
+ padding: var(--s2-spacing-50);
+ border: none;
+ border-radius: var(--s2-corner-radius-75);
+ background: transparent;
+ color: inherit;
+ cursor: pointer;
+ opacity: 0.85;
+
+ svg {
+ display: block;
+ width: 20px;
+ height: 20px;
+
+ path {
+ fill: currentcolor;
+ }
+ }
+
+ &:hover {
+ opacity: 1;
+ }
+
+ &:focus-visible {
+ outline: 2px solid var(--s2-gray-25);
+ outline-offset: 2px;
+ }
+}
diff --git a/nx2/blocks/shared/toast/toast.js b/nx2/blocks/shared/toast/toast.js
new file mode 100644
index 000000000..c107c1d35
--- /dev/null
+++ b/nx2/blocks/shared/toast/toast.js
@@ -0,0 +1,105 @@
+import { LitElement, html, nothing } from 'da-lit';
+import { loadStyle } from '../../../utils/utils.js';
+import { loadHrefSvg } from '../../../utils/svg.js';
+
+export const VARIANT_SUCCESS = 'success';
+export const VARIANT_ERROR = 'error';
+
+const CLOSE_ICON_URL = new URL('../../../img/icons/S2_Icon_Close_20_N.svg', import.meta.url).href;
+
+const styles = await loadStyle(import.meta.url);
+const hostSheet = await loadStyle(new URL('toast-host.css', import.meta.url).href);
+
+let closeIconPromise;
+const getCloseIcon = () => {
+ closeIconPromise ??= loadHrefSvg(CLOSE_ICON_URL);
+ return closeIconPromise;
+};
+
+const HOST_ID = 'nx-toast-host';
+
+function ensureHost() {
+ if (!document.adoptedStyleSheets.includes(hostSheet)) {
+ document.adoptedStyleSheets = [...document.adoptedStyleSheets, hostSheet];
+ }
+ let host = document.getElementById(HOST_ID);
+ if (!host) {
+ host = document.createElement('div');
+ host.id = HOST_ID;
+ host.setAttribute('role', 'region');
+ host.setAttribute('aria-label', 'Notifications');
+ document.body.append(host);
+ }
+ return host;
+}
+
+class NxToast extends LitElement {
+ static properties = {
+ message: { type: String, attribute: false },
+ variant: { type: String, attribute: false },
+ _closeIcon: { state: true },
+ };
+
+ duration = 6000;
+
+ _timerId;
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.shadowRoot.adoptedStyleSheets = [styles];
+ this.style.pointerEvents = 'auto';
+ const ms = Math.max(6000, Number(this.duration) || 6000);
+ this._timerId = window.setTimeout(this.dismiss, ms);
+ this._loadIcon();
+ }
+
+ async _loadIcon() {
+ const icon = await getCloseIcon();
+ if (!this.isConnected) return;
+ this._closeIcon = icon;
+ }
+
+ disconnectedCallback() {
+ window.clearTimeout(this._timerId);
+ this._timerId = undefined;
+ super.disconnectedCallback();
+ }
+
+ dismiss = () => {
+ window.clearTimeout(this._timerId);
+ this._timerId = undefined;
+ this.remove();
+ };
+
+ render() {
+ const text = this.message?.trim();
+ if (!text) return nothing;
+ const isError = this.variant === VARIANT_ERROR;
+ return html`
+
+
${text}
+
${this._closeIcon?.cloneNode(true)}
+
+ `;
+ }
+}
+
+export function showToast({ text, variant = VARIANT_SUCCESS, timeout = 6000 } = {}) {
+ const messageText = text?.trim();
+ if (!messageText) return;
+ const toast = document.createElement('nx-toast');
+ toast.message = messageText;
+ toast.variant = variant === VARIANT_ERROR ? VARIANT_ERROR : VARIANT_SUCCESS;
+ toast.duration = timeout;
+ ensureHost().append(toast);
+}
+
+customElements.define('nx-toast', NxToast);
diff --git a/nx2/blocks/shared/utils/list-nav.js b/nx2/blocks/shared/utils/list-nav.js
new file mode 100644
index 000000000..bf151f426
--- /dev/null
+++ b/nx2/blocks/shared/utils/list-nav.js
@@ -0,0 +1,28 @@
+export function listKeydown(key, {
+ items, active, itemKey, shadowRoot, setActive, onSelect, onClose,
+ focusActiveItem = true,
+}) {
+ const selectable = items?.filter((i) => !i.divider && !i.section) ?? [];
+ if (!selectable.length) return false;
+
+ const curIdx = selectable.findIndex((i) => i[itemKey] === active);
+
+ if (key === 'ArrowDown') {
+ const next = selectable[(curIdx + 1) % selectable.length][itemKey];
+ setActive(next);
+ if (focusActiveItem) shadowRoot.querySelector(`[data-${itemKey}="${next}"]`)?.focus();
+ return true;
+ } else if (key === 'ArrowUp') {
+ const prev = selectable[(curIdx <= 0 ? selectable.length : curIdx) - 1][itemKey];
+ setActive(prev);
+ if (focusActiveItem) shadowRoot.querySelector(`[data-${itemKey}="${prev}"]`)?.focus();
+ return true;
+ } else if (key === 'Enter' && active !== undefined) {
+ onSelect(selectable.find((i) => i[itemKey] === active));
+ return true;
+ } else if (key === 'Escape') {
+ onClose();
+ return true;
+ }
+ return false;
+}
diff --git a/nx2/deps/mdast/dist/index.js b/nx2/deps/mdast/dist/index.js
new file mode 100644
index 000000000..0b2eb2571
--- /dev/null
+++ b/nx2/deps/mdast/dist/index.js
@@ -0,0 +1,3 @@
+var Re=Object.create;var tn=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var De=Object.getOwnPropertyNames;var Ve=Object.getPrototypeOf,qe=Object.prototype.hasOwnProperty;var He=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),je=(t,e)=>{for(var n in e)tn(t,n,{get:e[n],enumerable:!0})},Ue=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of De(e))!qe.call(t,i)&&i!==n&&tn(t,i,{get:()=>e[i],enumerable:!(r=Me(e,i))||r.enumerable});return t};var Qe=(t,e,n)=>(n=t!=null?Re(Ve(t)):{},Ue(e||!t||!t.__esModule?tn(n,"default",{value:t,enumerable:!0}):n,t));var Qn=He((Bi,Un)=>{"use strict";var Dt=Object.prototype.hasOwnProperty,jn=Object.prototype.toString,Rn=Object.defineProperty,Mn=Object.getOwnPropertyDescriptor,Dn=function(e){return typeof Array.isArray=="function"?Array.isArray(e):jn.call(e)==="[object Array]"},Vn=function(e){if(!e||jn.call(e)!=="[object Object]")return!1;var n=Dt.call(e,"constructor"),r=e.constructor&&e.constructor.prototype&&Dt.call(e.constructor.prototype,"isPrototypeOf");if(e.constructor&&!n&&!r)return!1;var i;for(i in e);return typeof i>"u"||Dt.call(e,i)},qn=function(e,n){Rn&&n.name==="__proto__"?Rn(e,n.name,{enumerable:!0,configurable:!0,value:n.newValue,writable:!0}):e[n.name]=n.newValue},Hn=function(e,n){if(n==="__proto__")if(Dt.call(e,n)){if(Mn)return Mn(e,n).value}else return;return e[n]};Un.exports=function t(){var e,n,r,i,o,u,a=arguments[0],h=1,p=arguments.length,c=!1;for(typeof a=="boolean"&&(c=a,a=arguments[1]||{},h=2),(a==null||typeof a!="object"&&typeof a!="function")&&(a={});h
u.length,h;a&&u.push(i);try{h=t.apply(this,u)}catch(p){let c=p;if(a&&n)throw c;return i(c)}a||(h&&h.then&&typeof h.then=="function"?h.then(o,i):h instanceof Error?i(h):o(h))}function i(u,...a){n||(n=!0,e(u,...a))}function o(u){i(null,u)}}function ct(t){return!t||typeof t!="object"?"":"position"in t||"type"in t?Yn(t.position):"start"in t||"end"in t?Yn(t):"line"in t||"column"in t?rn(t):""}function rn(t){return Zn(t&&t.line)+":"+Zn(t&&t.column)}function Yn(t){return rn(t&&t.start)+"-"+rn(t&&t.end)}function Zn(t){return t&&typeof t=="number"?t:1}var Q=class extends Error{constructor(e,n,r){super(),typeof n=="string"&&(r=n,n=void 0);let i="",o={},u=!1;if(n&&("line"in n&&"column"in n?o={place:n}:"start"in n&&"end"in n?o={place:n}:"type"in n?o={ancestors:[n],place:n.position}:o={...n}),typeof e=="string"?i=e:!o.cause&&e&&(u=!0,i=e.message,o.cause=e),!o.ruleId&&!o.source&&typeof r=="string"){let h=r.indexOf(":");h===-1?o.ruleId=r:(o.source=r.slice(0,h),o.ruleId=r.slice(h+1))}if(!o.place&&o.ancestors&&o.ancestors){let h=o.ancestors[o.ancestors.length-1];h&&(o.place=h.position)}let a=o.place&&"start"in o.place?o.place.start:o.place;this.ancestors=o.ancestors||void 0,this.cause=o.cause||void 0,this.column=a?a.column:void 0,this.fatal=void 0,this.file="",this.message=i,this.line=a?a.line:void 0,this.name=ct(o.place)||"1:1",this.place=o.place||void 0,this.reason=this.message,this.ruleId=o.ruleId||void 0,this.source=o.source||void 0,this.stack=u&&o.cause&&typeof o.cause.stack=="string"?o.cause.stack:"",this.actual=void 0,this.expected=void 0,this.note=void 0,this.url=void 0}};Q.prototype.file="";Q.prototype.name="";Q.prototype.reason="";Q.prototype.message="";Q.prototype.stack="";Q.prototype.column=void 0;Q.prototype.line=void 0;Q.prototype.ancestors=void 0;Q.prototype.cause=void 0;Q.prototype.fatal=void 0;Q.prototype.place=void 0;Q.prototype.ruleId=void 0;Q.prototype.source=void 0;var rt={basename:We,dirname:Ye,extname:Ze,join:Ge,sep:"/"};function We(t,e){if(e!==void 0&&typeof e!="string")throw new TypeError('"ext" argument must be a string');Lt(t);let n=0,r=-1,i=t.length,o;if(e===void 0||e.length===0||e.length>t.length){for(;i--;)if(t.codePointAt(i)===47){if(o){n=i+1;break}}else r<0&&(o=!0,r=i+1);return r<0?"":t.slice(n,r)}if(e===t)return"";let u=-1,a=e.length-1;for(;i--;)if(t.codePointAt(i)===47){if(o){n=i+1;break}}else u<0&&(o=!0,u=i+1),a>-1&&(t.codePointAt(i)===e.codePointAt(a--)?a<0&&(r=i):(a=-1,r=u));return n===r?r=u:r<0&&(r=t.length),t.slice(n,r)}function Ye(t){if(Lt(t),t.length===0)return".";let e=-1,n=t.length,r;for(;--n;)if(t.codePointAt(n)===47){if(r){e=n;break}}else r||(r=!0);return e<0?t.codePointAt(0)===47?"/":".":e===1&&t.codePointAt(0)===47?"//":t.slice(0,e)}function Ze(t){Lt(t);let e=t.length,n=-1,r=0,i=-1,o=0,u;for(;e--;){let a=t.codePointAt(e);if(a===47){if(u){r=e+1;break}continue}n<0&&(u=!0,n=e+1),a===46?i<0?i=e:o!==1&&(o=1):i>-1&&(o=-1)}return i<0||n<0||o===0||o===1&&i===n-1&&i===r+1?"":t.slice(i,n)}function Ge(...t){let e=-1,n;for(;++e0&&t.codePointAt(t.length-1)===47&&(n+="/"),e?"/"+n:n}function Ke(t,e){let n="",r=0,i=-1,o=0,u=-1,a,h;for(;++u<=t.length;){if(u2){if(h=n.lastIndexOf("/"),h!==n.length-1){h<0?(n="",r=0):(n=n.slice(0,h),r=n.length-1-n.lastIndexOf("/")),i=u,o=0;continue}}else if(n.length>0){n="",r=0,i=u,o=0;continue}}e&&(n=n.length>0?n+"/..":"..",r=2)}else n.length>0?n+="/"+t.slice(i+1,u):n=t.slice(i+1,u),r=u-i-1;i=u,o=0}else a===46&&o>-1?o++:o=-1}return n}function Lt(t){if(typeof t!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(t))}var Gn={cwd:Xe};function Xe(){return"/"}function St(t){return!!(t!==null&&typeof t=="object"&&"href"in t&&t.href&&"protocol"in t&&t.protocol&&t.auth===void 0)}function Jn(t){if(typeof t=="string")t=new URL(t);else if(!St(t)){let e=new TypeError('The "path" argument must be of type string or an instance of URL. Received `'+t+"`");throw e.code="ERR_INVALID_ARG_TYPE",e}if(t.protocol!=="file:"){let e=new TypeError("The URL must be of scheme file");throw e.code="ERR_INVALID_URL_SCHEME",e}return $e(t)}function $e(t){if(t.hostname!==""){let r=new TypeError('File URL host must be "localhost" or empty on darwin');throw r.code="ERR_INVALID_FILE_URL_HOST",r}let e=t.pathname,n=-1;for(;++n0){let[f,...A]=c,F=r[m][1];At(F)&&At(f)&&(f=(0,qt.default)(!0,F,f)),r[m]=[p,f,...A]}}}},te=new fn().freeze();function ln(t,e){if(typeof e!="function")throw new TypeError("Cannot `"+t+"` without `parser`")}function sn(t,e){if(typeof e!="function")throw new TypeError("Cannot `"+t+"` without `compiler`")}function cn(t,e){if(e)throw new Error("Cannot call `"+t+"` on a frozen processor.\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.")}function $n(t){if(!At(t)||typeof t.type!="string")throw new TypeError("Expected node, got `"+t+"`")}function vn(t,e,n){if(!n)throw new Error("`"+t+"` finished async. Use `"+e+"` instead")}function Vt(t){return nr(t)?t:new Ft(t)}function nr(t){return!!(t&&typeof t=="object"&&"message"in t&&"messages"in t)}function er(t){return typeof t=="string"||rr(t)}function rr(t){return!!(t&&typeof t=="object"&&"byteLength"in t&&"byteOffset"in t)}var ir={};function pn(t,e){let n=e||ir,r=typeof n.includeImageAlt=="boolean"?n.includeImageAlt:!0,i=typeof n.includeHtml=="boolean"?n.includeHtml:!0;return ee(t,r,i)}function ee(t,e,n){if(or(t)){if("value"in t)return t.type==="html"&&!n?"":t.value;if(e&&"alt"in t&&t.alt)return t.alt;if("children"in t)return ne(t.children,e,n)}return Array.isArray(t)?ne(t,e,n):""}function ne(t,e,n){let r=[],i=-1;for(;++ii?0:i+e:e=e>i?i:e,n=n>0?n:0,r.length<1e4)u=Array.from(r),u.unshift(e,n),t.splice(...u);else for(n&&t.splice(e,n);o0?(Y(t,t.length,0,e),t):e}var ie={}.hasOwnProperty;function oe(t){let e={},n=-1;for(;++n13&&n<32||n>126&&n<160||n>55295&&n<57344||n>64975&&n<65008||(n&65535)===65535||(n&65535)===65534||n>1114111?"\uFFFD":String.fromCodePoint(n)}function st(t){return t.replace(/[\t\n\r ]+/g," ").replace(/^ | $/g,"").toLowerCase().toUpperCase()}var $=ft(/[A-Za-z]/),J=ft(/[\dA-Za-z]/),ue=ft(/[#-'*+\--9=?A-Z^-~]/);function Pt(t){return t!==null&&(t<32||t===127)}var _t=ft(/\d/),ae=ft(/[\dA-Fa-f]/),le=ft(/[!-/:-@[-`{-~]/);function w(t){return t!==null&&t<-2}function H(t){return t!==null&&(t<0||t===32)}function I(t){return t===-2||t===-1||t===32}var se=ft(/\p{P}|\p{S}/u),ce=ft(/\s/);function ft(t){return e;function e(n){return n!==null&&n>-1&&t.test(String.fromCharCode(n))}}function z(t,e,n,r){let i=r?r-1:Number.POSITIVE_INFINITY,o=0;return u;function u(h){return I(h)?(t.enter(n),a(h)):e(h)}function a(h){return I(h)&&o++u))return;let N=e.events.length,j=N,P,M;for(;j--;)if(e.events[j][0]==="exit"&&e.events[j][1].type==="chunkFlow"){if(P){M=e.events[j][1].end;break}P=!0}for(y(r),g=N;gb;){let D=n[R];e.containerState=D[1],D[0].exit.call(e,t)}n.length=b}function V(){i.write([null]),o=void 0,i=void 0,e.containerState._closeFlow=void 0}}function cr(t,e,n){return z(t,t.attempt(this.parser.constructs.document,e,n),"linePrefix",this.parser.constructs.disable.null.includes("codeIndented")?void 0:4)}function hn(t){if(t===null||H(t)||ce(t))return 1;if(se(t))return 2}function Et(t,e,n){let r=[],i=-1;for(;++i1&&t[n][1].end.offset-t[n][1].start.offset>1?2:1;let d={...t[r][1].end},m={...t[n][1].start};me(d,-h),me(m,h),u={type:h>1?"strongSequence":"emphasisSequence",start:d,end:{...t[r][1].end}},a={type:h>1?"strongSequence":"emphasisSequence",start:{...t[n][1].start},end:m},o={type:h>1?"strongText":"emphasisText",start:{...t[r][1].end},end:{...t[n][1].start}},i={type:h>1?"strong":"emphasis",start:{...u.start},end:{...a.end}},t[r][1].end={...u.start},t[n][1].start={...a.end},p=[],t[r][1].end.offset-t[r][1].start.offset&&(p=G(p,[["enter",t[r][1],e],["exit",t[r][1],e]])),p=G(p,[["enter",i,e],["enter",u,e],["exit",u,e],["enter",o,e]]),p=G(p,Et(e.parser.constructs.insideSpan.null,t.slice(r+1,n),e)),p=G(p,[["exit",o,e],["enter",a,e],["exit",a,e],["exit",i,e]]),t[n][1].end.offset-t[n][1].start.offset?(c=2,p=G(p,[["enter",t[n][1],e],["exit",t[n][1],e]])):c=0,Y(t,r-1,n-r+3,p),n=r+p.length-c-2;break}}for(n=-1;++n0&&I(g)?z(t,V,"linePrefix",o+1)(g):V(g)}function V(g){return g===null||w(g)?t.check(de,F,R)(g):(t.enter("codeFlowValue"),b(g))}function b(g){return g===null||w(g)?(t.exit("codeFlowValue"),V(g)):(t.consume(g),b)}function R(g){return t.exit("codeFenced"),e(g)}function D(g,N,j){let P=0;return M;function M(C){return g.enter("lineEnding"),g.consume(C),g.exit("lineEnding"),v}function v(C){return g.enter("codeFencedFence"),I(C)?z(g,_,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(C):_(C)}function _(C){return C===a?(g.enter("codeFencedFenceSequence"),S(C)):j(C)}function S(C){return C===a?(P++,g.consume(C),S):P>=u?(g.exit("codeFencedFenceSequence"),I(C)?z(g,T,"whitespace")(C):T(C)):j(C)}function T(C){return C===null||w(C)?(g.exit("codeFencedFence"),N(C)):j(C)}}}function br(t,e,n){let r=this;return i;function i(u){return u===null?n(u):(t.enter("lineEnding"),t.consume(u),t.exit("lineEnding"),o)}function o(u){return r.parser.lazy[r.now().line]?n(u):e(u)}}var Nt={name:"codeIndented",tokenize:Ir},Sr={partial:!0,tokenize:Er};function Ir(t,e,n){let r=this;return i;function i(p){return t.enter("codeIndented"),z(t,o,"linePrefix",5)(p)}function o(p){let c=r.events[r.events.length-1];return c&&c[1].type==="linePrefix"&&c[2].sliceSerialize(c[1],!0).length>=4?u(p):n(p)}function u(p){return p===null?h(p):w(p)?t.attempt(Sr,u,h)(p):(t.enter("codeFlowValue"),a(p))}function a(p){return p===null||w(p)?(t.exit("codeFlowValue"),u(p)):(t.consume(p),a)}function h(p){return t.exit("codeIndented"),e(p)}}function Er(t,e,n){let r=this;return i;function i(u){return r.parser.lazy[r.now().line]?n(u):w(u)?(t.enter("lineEnding"),t.consume(u),t.exit("lineEnding"),i):z(t,o,"linePrefix",5)(u)}function o(u){let a=r.events[r.events.length-1];return a&&a[1].type==="linePrefix"&&a[2].sliceSerialize(a[1],!0).length>=4?e(u):w(u)?i(u):n(u)}}var dn={name:"codeText",previous:Cr,resolve:zr,tokenize:Tr};function zr(t){let e=t.length-4,n=3,r,i;if((t[n][1].type==="lineEnding"||t[n][1].type==="space")&&(t[e][1].type==="lineEnding"||t[e][1].type==="space")){for(r=n;++r=this.left.length+this.right.length)throw new RangeError("Cannot access index `"+e+"` in a splice buffer of size `"+(this.left.length+this.right.length)+"`");return ethis.left.length?this.right.slice(this.right.length-r+this.left.length,this.right.length-e+this.left.length).reverse():this.left.slice(e).concat(this.right.slice(this.right.length-r+this.left.length).reverse())}splice(e,n,r){let i=n||0;this.setCursor(Math.trunc(e));let o=this.right.splice(this.right.length-i,Number.POSITIVE_INFINITY);return r&&Ot(this.left,r),o.reverse()}pop(){return this.setCursor(Number.POSITIVE_INFINITY),this.left.pop()}push(e){this.setCursor(Number.POSITIVE_INFINITY),this.left.push(e)}pushMany(e){this.setCursor(Number.POSITIVE_INFINITY),Ot(this.left,e)}unshift(e){this.setCursor(0),this.right.push(e)}unshiftMany(e){this.setCursor(0),Ot(this.right,e.reverse())}setCursor(e){if(!(e===this.left.length||e>this.left.length&&this.right.length===0||e<0&&this.left.length===0))if(e=4?e(u):t.interrupt(r.parser.constructs.flow,n,e)(u)}}function Gt(t,e,n,r,i,o,u,a,h){let p=h||Number.POSITIVE_INFINITY,c=0;return d;function d(y){return y===60?(t.enter(r),t.enter(i),t.enter(o),t.consume(y),t.exit(o),m):y===null||y===32||y===41||Pt(y)?n(y):(t.enter(r),t.enter(u),t.enter(a),t.enter("chunkString",{contentType:"string"}),F(y))}function m(y){return y===62?(t.enter(o),t.consume(y),t.exit(o),t.exit(i),t.exit(r),e):(t.enter(a),t.enter("chunkString",{contentType:"string"}),f(y))}function f(y){return y===62?(t.exit("chunkString"),t.exit(a),m(y)):y===null||y===60||w(y)?n(y):(t.consume(y),y===92?A:f)}function A(y){return y===60||y===62||y===92?(t.consume(y),f):f(y)}function F(y){return!c&&(y===null||y===41||H(y))?(t.exit("chunkString"),t.exit(a),t.exit(u),t.exit(r),e(y)):c999||f===null||f===91||f===93&&!h||f===94&&!a&&"_hiddenFootnoteSupport"in u.parser.constructs?n(f):f===93?(t.exit(o),t.enter(i),t.consume(f),t.exit(i),t.exit(r),e):w(f)?(t.enter("lineEnding"),t.consume(f),t.exit("lineEnding"),c):(t.enter("chunkString",{contentType:"string"}),d(f))}function d(f){return f===null||f===91||f===93||w(f)||a++>999?(t.exit("chunkString"),c(f)):(t.consume(f),h||(h=!I(f)),f===92?m:d)}function m(f){return f===91||f===92||f===93?(t.consume(f),a++,d):d(f)}}function Kt(t,e,n,r,i,o){let u;return a;function a(m){return m===34||m===39||m===40?(t.enter(r),t.enter(i),t.consume(m),t.exit(i),u=m===40?41:m,h):n(m)}function h(m){return m===u?(t.enter(i),t.consume(m),t.exit(i),t.exit(r),e):(t.enter(o),p(m))}function p(m){return m===u?(t.exit(o),h(u)):m===null?n(m):w(m)?(t.enter("lineEnding"),t.consume(m),t.exit("lineEnding"),z(t,p,"linePrefix")):(t.enter("chunkString",{contentType:"string"}),c(m))}function c(m){return m===u||m===null||w(m)?(t.exit("chunkString"),p(m)):(t.consume(m),m===92?d:c)}function d(m){return m===u||m===92?(t.consume(m),c):c(m)}}function gt(t,e){let n;return r;function r(i){return w(i)?(t.enter("lineEnding"),t.consume(i),t.exit("lineEnding"),n=!0,r):I(i)?z(t,r,n?"linePrefix":"lineSuffix")(i):e(i)}}var gn={name:"definition",tokenize:Nr},Br={partial:!0,tokenize:Or};function Nr(t,e,n){let r=this,i;return o;function o(f){return t.enter("definition"),u(f)}function u(f){return Jt.call(r,t,a,n,"definitionLabel","definitionLabelMarker","definitionLabelString")(f)}function a(f){return i=st(r.sliceSerialize(r.events[r.events.length-1][1]).slice(1,-1)),f===58?(t.enter("definitionMarker"),t.consume(f),t.exit("definitionMarker"),h):n(f)}function h(f){return H(f)?gt(t,p)(f):p(f)}function p(f){return Gt(t,c,n,"definitionDestination","definitionDestinationLiteral","definitionDestinationLiteralMarker","definitionDestinationRaw","definitionDestinationString")(f)}function c(f){return t.attempt(Br,d,d)(f)}function d(f){return I(f)?z(t,m,"whitespace")(f):m(f)}function m(f){return f===null||w(f)?(t.exit("definition"),r.parser.defined.push(i),e(f)):n(f)}}function Or(t,e,n){return r;function r(a){return H(a)?gt(t,i)(a):n(a)}function i(a){return Kt(t,o,n,"definitionTitle","definitionTitleMarker","definitionTitleString")(a)}function o(a){return I(a)?z(t,u,"whitespace")(a):u(a)}function u(a){return a===null||w(a)?e(a):n(a)}}var kn={name:"hardBreakEscape",tokenize:Rr};function Rr(t,e,n){return r;function r(o){return t.enter("hardBreakEscape"),t.consume(o),i}function i(o){return w(o)?(t.exit("hardBreakEscape"),e(o)):n(o)}}var yn={name:"headingAtx",resolve:Mr,tokenize:Dr};function Mr(t,e){let n=t.length-2,r=3,i,o;return t[r][1].type==="whitespace"&&(r+=2),n-2>r&&t[n][1].type==="whitespace"&&(n-=2),t[n][1].type==="atxHeadingSequence"&&(r===n-1||n-4>r&&t[n-2][1].type==="whitespace")&&(n-=r+1===n?2:4),n>r&&(i={type:"atxHeadingText",start:t[r][1].start,end:t[n][1].end},o={type:"chunkText",start:t[r][1].start,end:t[n][1].end,contentType:"text"},Y(t,r,n-r+1,[["enter",i,e],["enter",o,e],["exit",o,e],["exit",i,e]])),t}function Dr(t,e,n){let r=0;return i;function i(c){return t.enter("atxHeading"),o(c)}function o(c){return t.enter("atxHeadingSequence"),u(c)}function u(c){return c===35&&r++<6?(t.consume(c),u):c===null||H(c)?(t.exit("atxHeadingSequence"),a(c)):n(c)}function a(c){return c===35?(t.enter("atxHeadingSequence"),h(c)):c===null||w(c)?(t.exit("atxHeading"),e(c)):I(c)?z(t,a,"whitespace")(c):(t.enter("atxHeadingText"),p(c))}function h(c){return c===35?(t.consume(c),h):(t.exit("atxHeadingSequence"),a(c))}function p(c){return c===null||c===35||H(c)?(t.exit("atxHeadingText"),a(c)):(t.consume(c),p)}}var xe=["address","article","aside","base","basefont","blockquote","body","caption","center","col","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hr","html","iframe","legend","li","link","main","menu","menuitem","nav","noframes","ol","optgroup","option","p","param","search","section","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"],wn=["pre","script","style","textarea"];var bn={concrete:!0,name:"htmlFlow",resolveTo:Hr,tokenize:jr},Vr={partial:!0,tokenize:Qr},qr={partial:!0,tokenize:Ur};function Hr(t){let e=t.length;for(;e--&&!(t[e][0]==="enter"&&t[e][1].type==="htmlFlow"););return e>1&&t[e-2][1].type==="linePrefix"&&(t[e][1].start=t[e-2][1].start,t[e+1][1].start=t[e-2][1].start,t.splice(e-2,2)),t}function jr(t,e,n){let r=this,i,o,u,a,h;return p;function p(s){return c(s)}function c(s){return t.enter("htmlFlow"),t.enter("htmlFlowData"),t.consume(s),d}function d(s){return s===33?(t.consume(s),m):s===47?(t.consume(s),o=!0,F):s===63?(t.consume(s),i=3,r.interrupt?e:l):$(s)?(t.consume(s),u=String.fromCharCode(s),O):n(s)}function m(s){return s===45?(t.consume(s),i=2,f):s===91?(t.consume(s),i=5,a=0,A):$(s)?(t.consume(s),i=4,r.interrupt?e:l):n(s)}function f(s){return s===45?(t.consume(s),r.interrupt?e:l):n(s)}function A(s){let K="CDATA[";return s===K.charCodeAt(a++)?(t.consume(s),a===K.length?r.interrupt?e:_:A):n(s)}function F(s){return $(s)?(t.consume(s),u=String.fromCharCode(s),O):n(s)}function O(s){if(s===null||s===47||s===62||H(s)){let K=s===47,mt=u.toLowerCase();return!K&&!o&&wn.includes(mt)?(i=1,r.interrupt?e(s):_(s)):xe.includes(u.toLowerCase())?(i=6,K?(t.consume(s),y):r.interrupt?e(s):_(s)):(i=7,r.interrupt&&!r.parser.lazy[r.now().line]?n(s):o?V(s):b(s))}return s===45||J(s)?(t.consume(s),u+=String.fromCharCode(s),O):n(s)}function y(s){return s===62?(t.consume(s),r.interrupt?e:_):n(s)}function V(s){return I(s)?(t.consume(s),V):M(s)}function b(s){return s===47?(t.consume(s),M):s===58||s===95||$(s)?(t.consume(s),R):I(s)?(t.consume(s),b):M(s)}function R(s){return s===45||s===46||s===58||s===95||J(s)?(t.consume(s),R):D(s)}function D(s){return s===61?(t.consume(s),g):I(s)?(t.consume(s),D):b(s)}function g(s){return s===null||s===60||s===61||s===62||s===96?n(s):s===34||s===39?(t.consume(s),h=s,N):I(s)?(t.consume(s),g):j(s)}function N(s){return s===h?(t.consume(s),h=null,P):s===null||w(s)?n(s):(t.consume(s),N)}function j(s){return s===null||s===34||s===39||s===47||s===60||s===61||s===62||s===96||H(s)?D(s):(t.consume(s),j)}function P(s){return s===47||s===62||I(s)?b(s):n(s)}function M(s){return s===62?(t.consume(s),v):n(s)}function v(s){return s===null||w(s)?_(s):I(s)?(t.consume(s),v):n(s)}function _(s){return s===45&&i===2?(t.consume(s),q):s===60&&i===1?(t.consume(s),U):s===62&&i===4?(t.consume(s),tt):s===63&&i===3?(t.consume(s),l):s===93&&i===5?(t.consume(s),it):w(s)&&(i===6||i===7)?(t.exit("htmlFlowData"),t.check(Vr,ot,S)(s)):s===null||w(s)?(t.exit("htmlFlowData"),S(s)):(t.consume(s),_)}function S(s){return t.check(qr,T,ot)(s)}function T(s){return t.enter("lineEnding"),t.consume(s),t.exit("lineEnding"),C}function C(s){return s===null||w(s)?S(s):(t.enter("htmlFlowData"),_(s))}function q(s){return s===45?(t.consume(s),l):_(s)}function U(s){return s===47?(t.consume(s),u="",nt):_(s)}function nt(s){if(s===62){let K=u.toLowerCase();return wn.includes(K)?(t.consume(s),tt):_(s)}return $(s)&&u.length<8?(t.consume(s),u+=String.fromCharCode(s),nt):_(s)}function it(s){return s===93?(t.consume(s),l):_(s)}function l(s){return s===62?(t.consume(s),tt):s===45&&i===2?(t.consume(s),l):_(s)}function tt(s){return s===null||w(s)?(t.exit("htmlFlowData"),ot(s)):(t.consume(s),tt)}function ot(s){return t.exit("htmlFlow"),e(s)}}function Ur(t,e,n){let r=this;return i;function i(u){return w(u)?(t.enter("lineEnding"),t.consume(u),t.exit("lineEnding"),o):n(u)}function o(u){return r.parser.lazy[r.now().line]?n(u):e(u)}}function Qr(t,e,n){return r;function r(i){return t.enter("lineEnding"),t.consume(i),t.exit("lineEnding"),t.attempt(pt,e,n)}}var Sn={name:"htmlText",tokenize:Wr};function Wr(t,e,n){let r=this,i,o,u;return a;function a(l){return t.enter("htmlText"),t.enter("htmlTextData"),t.consume(l),h}function h(l){return l===33?(t.consume(l),p):l===47?(t.consume(l),D):l===63?(t.consume(l),b):$(l)?(t.consume(l),j):n(l)}function p(l){return l===45?(t.consume(l),c):l===91?(t.consume(l),o=0,A):$(l)?(t.consume(l),V):n(l)}function c(l){return l===45?(t.consume(l),f):n(l)}function d(l){return l===null?n(l):l===45?(t.consume(l),m):w(l)?(u=d,U(l)):(t.consume(l),d)}function m(l){return l===45?(t.consume(l),f):d(l)}function f(l){return l===62?q(l):l===45?m(l):d(l)}function A(l){let tt="CDATA[";return l===tt.charCodeAt(o++)?(t.consume(l),o===tt.length?F:A):n(l)}function F(l){return l===null?n(l):l===93?(t.consume(l),O):w(l)?(u=F,U(l)):(t.consume(l),F)}function O(l){return l===93?(t.consume(l),y):F(l)}function y(l){return l===62?q(l):l===93?(t.consume(l),y):F(l)}function V(l){return l===null||l===62?q(l):w(l)?(u=V,U(l)):(t.consume(l),V)}function b(l){return l===null?n(l):l===63?(t.consume(l),R):w(l)?(u=b,U(l)):(t.consume(l),b)}function R(l){return l===62?q(l):b(l)}function D(l){return $(l)?(t.consume(l),g):n(l)}function g(l){return l===45||J(l)?(t.consume(l),g):N(l)}function N(l){return w(l)?(u=N,U(l)):I(l)?(t.consume(l),N):q(l)}function j(l){return l===45||J(l)?(t.consume(l),j):l===47||l===62||H(l)?P(l):n(l)}function P(l){return l===47?(t.consume(l),q):l===58||l===95||$(l)?(t.consume(l),M):w(l)?(u=P,U(l)):I(l)?(t.consume(l),P):q(l)}function M(l){return l===45||l===46||l===58||l===95||J(l)?(t.consume(l),M):v(l)}function v(l){return l===61?(t.consume(l),_):w(l)?(u=v,U(l)):I(l)?(t.consume(l),v):P(l)}function _(l){return l===null||l===60||l===61||l===62||l===96?n(l):l===34||l===39?(t.consume(l),i=l,S):w(l)?(u=_,U(l)):I(l)?(t.consume(l),_):(t.consume(l),T)}function S(l){return l===i?(t.consume(l),i=void 0,C):l===null?n(l):w(l)?(u=S,U(l)):(t.consume(l),S)}function T(l){return l===null||l===34||l===39||l===60||l===61||l===96?n(l):l===47||l===62||H(l)?P(l):(t.consume(l),T)}function C(l){return l===47||l===62||H(l)?P(l):n(l)}function q(l){return l===62?(t.consume(l),t.exit("htmlTextData"),t.exit("htmlText"),e):n(l)}function U(l){return t.exit("htmlTextData"),t.enter("lineEnding"),t.consume(l),t.exit("lineEnding"),nt}function nt(l){return I(l)?z(t,it,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(l):it(l)}function it(l){return t.enter("htmlTextData"),u(l)}}var kt={name:"labelEnd",resolveAll:Jr,resolveTo:Kr,tokenize:Xr},Yr={tokenize:$r},Zr={tokenize:vr},Gr={tokenize:ti};function Jr(t){let e=-1,n=[];for(;++e=3&&(p===null||w(p))?(t.exit("thematicBreak"),e(p)):n(p)}function h(p){return p===i?(t.consume(p),r++,h):(t.exit("thematicBreakSequence"),I(p)?z(t,a,"whitespace")(p):a(p))}}var Z={continuation:{tokenize:li},exit:ci,name:"list",tokenize:ai},oi={partial:!0,tokenize:fi},ui={partial:!0,tokenize:si};function ai(t,e,n){let r=this,i=r.events[r.events.length-1],o=i&&i[1].type==="linePrefix"?i[2].sliceSerialize(i[1],!0).length:0,u=0;return a;function a(f){let A=r.containerState.type||(f===42||f===43||f===45?"listUnordered":"listOrdered");if(A==="listUnordered"?!r.containerState.marker||f===r.containerState.marker:_t(f)){if(r.containerState.type||(r.containerState.type=A,t.enter(A,{_container:!0})),A==="listUnordered")return t.enter("listItemPrefix"),f===42||f===45?t.check(yt,n,p)(f):p(f);if(!r.interrupt||f===49)return t.enter("listItemPrefix"),t.enter("listItemValue"),h(f)}return n(f)}function h(f){return _t(f)&&++u<10?(t.consume(f),h):(!r.interrupt||u<2)&&(r.containerState.marker?f===r.containerState.marker:f===41||f===46)?(t.exit("listItemValue"),p(f)):n(f)}function p(f){return t.enter("listItemMarker"),t.consume(f),t.exit("listItemMarker"),r.containerState.marker=r.containerState.marker||f,t.check(pt,r.interrupt?n:c,t.attempt(oi,m,d))}function c(f){return r.containerState.initialBlankLine=!0,o++,m(f)}function d(f){return I(f)?(t.enter("listItemPrefixWhitespace"),t.consume(f),t.exit("listItemPrefixWhitespace"),m):n(f)}function m(f){return r.containerState.size=o+r.sliceSerialize(t.exit("listItemPrefix"),!0).length,e(f)}}function li(t,e,n){let r=this;return r.containerState._closeFlow=void 0,t.check(pt,i,o);function i(a){return r.containerState.furtherBlankLines=r.containerState.furtherBlankLines||r.containerState.initialBlankLine,z(t,e,"listItemIndent",r.containerState.size+1)(a)}function o(a){return r.containerState.furtherBlankLines||!I(a)?(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,u(a)):(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,t.attempt(ui,e,u)(a))}function u(a){return r.containerState._closeFlow=!0,r.interrupt=void 0,z(t,t.attempt(Z,e,n),"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(a)}}function si(t,e,n){let r=this;return z(t,i,"listItemIndent",r.containerState.size+1);function i(o){let u=r.events[r.events.length-1];return u&&u[1].type==="listItemIndent"&&u[2].sliceSerialize(u[1],!0).length===r.containerState.size?e(o):n(o)}}function ci(t){t.exit(this.containerState.type)}function fi(t,e,n){let r=this;return z(t,i,"listItemPrefixWhitespace",r.parser.constructs.disable.null.includes("codeIndented")?void 0:5);function i(o){let u=r.events[r.events.length-1];return!I(o)&&u&&u[1].type==="listItemPrefixWhitespace"?e(o):n(o)}}var Xt={name:"setextUnderline",resolveTo:pi,tokenize:hi};function pi(t,e){let n=t.length,r,i,o;for(;n--;)if(t[n][0]==="enter"){if(t[n][1].type==="content"){r=n;break}t[n][1].type==="paragraph"&&(i=n)}else t[n][1].type==="content"&&t.splice(n,1),!o&&t[n][1].type==="definition"&&(o=n);let u={type:"setextHeading",start:{...t[r][1].start},end:{...t[t.length-1][1].end}};return t[i][1].type="setextHeadingText",o?(t.splice(i,0,["enter",u,e]),t.splice(o+1,0,["exit",t[r][1],e]),t[r][1].end={...t[o][1].end}):t[r][1]=u,t.push(["exit",u,e]),t}function hi(t,e,n){let r=this,i;return o;function o(p){let c=r.events.length,d;for(;c--;)if(r.events[c][1].type!=="lineEnding"&&r.events[c][1].type!=="linePrefix"&&r.events[c][1].type!=="content"){d=r.events[c][1].type==="paragraph";break}return!r.parser.lazy[r.now().line]&&(r.interrupt||d)?(t.enter("setextHeadingLine"),i=p,u(p)):n(p)}function u(p){return t.enter("setextHeadingLineSequence"),a(p)}function a(p){return p===i?(t.consume(p),a):(t.exit("setextHeadingLineSequence"),I(p)?z(t,h,"lineSuffix")(p):h(p))}function h(p){return p===null||w(p)?(t.exit("setextHeadingLine"),e(p)):n(p)}}var ge={tokenize:mi};function mi(t){let e=this,n=t.attempt(pt,r,t.attempt(this.parser.constructs.flowInitial,i,z(t,t.attempt(this.parser.constructs.flow,i,t.attempt(xn,i)),"linePrefix")));return n;function r(o){if(o===null){t.consume(o);return}return t.enter("lineEndingBlank"),t.consume(o),t.exit("lineEndingBlank"),e.currentConstruct=void 0,n}function i(o){if(o===null){t.consume(o);return}return t.enter("lineEnding"),t.consume(o),t.exit("lineEnding"),e.currentConstruct=void 0,n}}var ke={resolveAll:Se()},ye=be("string"),we=be("text");function be(t){return{resolveAll:Se(t==="text"?di:void 0),tokenize:e};function e(n){let r=this,i=this.parser.constructs[t],o=n.attempt(i,u,a);return u;function u(c){return p(c)?o(c):a(c)}function a(c){if(c===null){n.consume(c);return}return n.enter("data"),n.consume(c),h}function h(c){return p(c)?(n.exit("data"),o(c)):(n.consume(c),h)}function p(c){if(c===null)return!0;let d=i[c],m=-1;if(d)for(;++mIi,contentInitial:()=>gi,disable:()=>Ei,document:()=>xi,flow:()=>yi,flowInitial:()=>ki,insideSpan:()=>Si,string:()=>wi,text:()=>bi});var xi={42:Z,43:Z,45:Z,48:Z,49:Z,50:Z,51:Z,52:Z,53:Z,54:Z,55:Z,56:Z,57:Z,62:jt},gi={91:gn},ki={[-2]:Nt,[-1]:Nt,32:Nt},yi={35:yn,42:yt,45:[Xt,yt],60:bn,61:Xt,95:yt,96:Wt,126:Wt},wi={38:Qt,92:Ut},bi={[-5]:Rt,[-4]:Rt,[-3]:Rt,33:In,38:Qt,42:Bt,60:[mn,Sn],91:En,92:[kn,Ut],93:kt,95:Bt,96:dn},Si={null:[Bt,ke]},Ii={null:[42,95]},Ei={null:[]};function Ie(t,e,n){let r={_bufferIndex:-1,_index:0,line:n&&n.line||1,column:n&&n.column||1,offset:n&&n.offset||0},i={},o=[],u=[],a=[],h=!0,p={attempt:P(N),check:P(j),consume:R,enter:D,exit:g,interrupt:P(j,{interrupt:!0})},c={code:null,containerState:{},defineSkip:y,events:[],now:O,parser:t,previous:null,sliceSerialize:A,sliceStream:F,write:f},d=e.tokenize.call(c,p),m;return e.resolveAll&&o.push(e),c;function f(S){return u=G(u,S),V(),u[u.length-1]!==null?[]:(M(e,0),c.events=Et(o,c.events,c),c.events)}function A(S,T){return Ci(F(S),T)}function F(S){return zi(u,S)}function O(){let{_bufferIndex:S,_index:T,line:C,column:q,offset:U}=r;return{_bufferIndex:S,_index:T,line:C,column:q,offset:U}}function y(S){i[S.line]=S.column,_()}function V(){let S;for(;r._index-1){let a=u[0];typeof a=="string"?u[0]=a.slice(r):u.shift()}o>0&&u.push(t[i].slice(0,o))}return u}function Ci(t,e){let n=-1,r=[],i;for(;++n0){let et=E.tokenStack[E.tokenStack.length-1];(et[1]||Ce).call(E,void 0,et[0])}for(k.position={start:ht(x.length>0?x[0][1].start:{line:1,column:1,offset:0}),end:ht(x.length>0?x[x.length-2][1].end:{line:1,column:1,offset:0})},B=-1;++B
+
+
diff --git a/nx2/img/icons/S2_Icon_Close_20_N.svg b/nx2/img/icons/S2_Icon_Close_20_N.svg
new file mode 100644
index 000000000..eec3dc692
--- /dev/null
+++ b/nx2/img/icons/S2_Icon_Close_20_N.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/nx2/scripts/nx.js b/nx2/scripts/nx.js
index c10d09243..22bb25a19 100644
--- a/nx2/scripts/nx.js
+++ b/nx2/scripts/nx.js
@@ -10,10 +10,6 @@
* governing permissions and limitations under the License.
*/
-const NX_BLOCKS = new Set([
- 'importer',
-]);
-
const LOG = async (ex, el) => (await import('../utils/error.js')).default(ex, el);
export function getColorScheme() {
@@ -99,18 +95,28 @@ export const loc = ([first], ...values) => {
};
export async function loadBlock(block) {
- const { codeBase, log } = getConfig();
+ const { nxBase, codeBase, providers, log } = getConfig();
const { classList } = block;
let name = classList[0];
- const isNx = name.startsWith('nx-');
- name = isNx ? name.replace('nx-', '') : name;
- block.dataset.blockName = name;
- const nxBase = NX_BLOCKS.has(name) ? '/nx' : '/nx2';
+ let path;
+ const isNx = name.startsWith('nx-');
+ if (isNx) {
+ name = name.replace('nx-', '');
+ path = `${nxBase}/blocks`;
+ } else {
+ const prefix = name.split('-')[0];
+ const provider = providers[prefix];
+ if (provider) {
+ name = name.slice(prefix.length + 1);
+ path = `${provider}/blocks`;
+ } else {
+ path = `${codeBase}/blocks`;
+ }
+ }
- const path = isNx ? `${nxBase}/blocks` : `${codeBase}/blocks`;
- const blockPath = `${path}/${name}/${name}`;
block.dataset.blockName = name;
+ const blockPath = `${path}/${name}/${name}`;
try {
await (await import(`${blockPath}.js`)).default(block);
} catch (ex) {
@@ -283,11 +289,6 @@ async function decorateDoc() {
const pageId = window.location.hash?.replace('#', '');
if (pageId) localStorage.setItem('lazyhash', pageId);
-
- if (localStorage.getItem('nx-panels')) {
- const { restorePanels } = await import('../utils/panel.js');
- await restorePanels();
- }
}
export async function loadArea({ area } = { area: document }) {
@@ -318,4 +319,34 @@ export async function loadArea({ area } = { area: document }) {
import('../utils/favicon.js');
}
}
+
+ if (isDoc && localStorage.getItem('nx-panels')) {
+ const { restorePanels } = await import('../utils/panel.js');
+ await restorePanels();
+ }
}
+
+const cache = {};
+
+// eslint-disable-next-line import/prefer-default-export
+export const loadStyle = (supplied) => {
+ // Convenience replacement for WCs
+ const path = supplied.replace('.js', '.css');
+
+ try {
+ cache[path] ??= new Promise((resolve) => {
+ (async () => {
+ const resp = await fetch(path);
+ const text = await resp.text();
+ const sheet = new CSSStyleSheet({ baseURL: path });
+ sheet.path = path;
+ sheet.replaceSync(text);
+ resolve(sheet);
+ })();
+ });
+ } catch {
+ // eslint-disable-next-line no-console
+ console.warn(`Could not load ${path}`);
+ }
+ return cache[path];
+};
diff --git a/nx2/scripts/scripts.js b/nx2/scripts/scripts.js
index 8f069f3e9..bc9843d92 100644
--- a/nx2/scripts/scripts.js
+++ b/nx2/scripts/scripts.js
@@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/
-import { loadArea, setConfig } from './nx.js';
+import { loadArea, setConfig, env } from './nx.js';
const hostnames = ['nx.live'];
@@ -40,6 +40,12 @@ const decorateArea = ({ area = document }) => {
eagerLoad(area, 'img');
};
+const EW_ORIGINS = {
+ dev: 'http://localhost:3001',
+ stage: 'https://main--ew-extensions--exp-workspace.aem.page',
+ prod: 'https://main--ew-extensions--exp-workspace.aem.live',
+};
+
const conf = {
hostnames,
locales,
@@ -47,6 +53,9 @@ const conf = {
imsScope,
linkBlocks,
decorateArea,
+ providers: {
+ ew: EW_ORIGINS[env],
+ },
};
export async function loadPage() {
diff --git a/nx2/styles/styles.css b/nx2/styles/styles.css
index e4924cee3..e1cd0553e 100644
--- a/nx2/styles/styles.css
+++ b/nx2/styles/styles.css
@@ -469,6 +469,10 @@ html:has(meta[content="edge-delivery"]) {
margin: 0;
max-height: none;
+ path {
+ fill: currentcolor;
+ }
+
.panel-wrapper {
--panel-bottom-margin: 12px;
@@ -600,6 +604,7 @@ html:has(meta[content="edge-delivery"]) {
aside.panel {
position: static;
+ min-width: 0;
.panel-wrapper {
height: calc(100vh - var(--s2-nav-height) - var(--panel-bottom-margin));
diff --git a/nx2/test/unit/nx/blocks/shared/toast/toast.test.js b/nx2/test/unit/nx/blocks/shared/toast/toast.test.js
new file mode 100644
index 000000000..38174cb3e
--- /dev/null
+++ b/nx2/test/unit/nx/blocks/shared/toast/toast.test.js
@@ -0,0 +1,63 @@
+import { expect } from '@esm-bundle/chai';
+import sinon from 'sinon';
+import { showToast } from '../../../../../../blocks/shared/toast/toast.js';
+
+const HOST_ID = 'nx-toast-host';
+
+function cleanup() {
+ document.querySelectorAll('nx-toast').forEach((el) => el.remove());
+ document.getElementById(HOST_ID)?.remove();
+}
+
+afterEach(cleanup);
+
+// ─── showToast no-op ─────────────────────────────────────────────────────────
+
+describe('showToast', () => {
+ it('does nothing for empty text', () => {
+ showToast({ text: '' });
+ expect(document.querySelector('nx-toast')).to.be.null;
+ });
+
+ it('does nothing for whitespace-only text', () => {
+ showToast({ text: ' ' });
+ expect(document.querySelector('nx-toast')).to.be.null;
+ });
+});
+
+// ─── Timeout floor ───────────────────────────────────────────────────────────
+
+describe('NxToast timeout', () => {
+ let timeoutStub;
+
+ beforeEach(() => {
+ timeoutStub = sinon.stub(window, 'setTimeout').returns(1);
+ });
+
+ afterEach(() => timeoutStub.restore());
+
+ it('clamps timeout below 6000ms to 6000ms', () => {
+ showToast({ text: 'hello', timeout: 100 });
+ expect(timeoutStub.calledOnce).to.be.true;
+ expect(timeoutStub.firstCall.args[1]).to.equal(6000);
+ });
+
+ it('respects timeout values above 6000ms', () => {
+ showToast({ text: 'hello', timeout: 8000 });
+ expect(timeoutStub.calledOnce).to.be.true;
+ expect(timeoutStub.firstCall.args[1]).to.equal(8000);
+ });
+});
+
+// ─── dismiss ─────────────────────────────────────────────────────────────────
+
+describe('NxToast dismiss', () => {
+ it('removes the element and cancels the pending timer', () => {
+ showToast({ text: 'hello' });
+ const toast = document.querySelector('nx-toast');
+ expect(toast._timerId).to.not.be.undefined;
+ toast.dismiss();
+ expect(document.querySelector('nx-toast')).to.be.null;
+ expect(toast._timerId).to.be.undefined;
+ });
+});
diff --git a/nx2/test/unit/nx/scripts/nx.test.js b/nx2/test/unit/nx/scripts/nx.test.js
index 851d7dad8..fe6d53842 100644
--- a/nx2/test/unit/nx/scripts/nx.test.js
+++ b/nx2/test/unit/nx/scripts/nx.test.js
@@ -331,7 +331,7 @@ describe('loadBlock', () => {
expect(block.dataset.blockName).to.equal('myblock');
});
- it('sets dataset.blockName for provider-prefixed blocks', async () => {
+ it('strips provider prefix and sets blockName to the remainder', async () => {
const block = document.createElement('div');
block.classList.add('custom-widget');
@@ -341,7 +341,7 @@ describe('loadBlock', () => {
// Expected to fail in test environment
}
- expect(block.dataset.blockName).to.equal('custom-widget');
+ expect(block.dataset.blockName).to.equal('widget');
});
it('calls log on error', async () => {
diff --git a/nx2/utils/aem-preview-publish.js b/nx2/utils/aem-preview-publish.js
new file mode 100644
index 000000000..c6a319236
--- /dev/null
+++ b/nx2/utils/aem-preview-publish.js
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2026 Adobe. All rights reserved.
+ * AEM admin preview / live (publish) flows aligned with da.live helpers.
+ */
+import { HLX_ADMIN } from './utils.js';
+import { daFetch } from './api.js';
+
+async function fetchSidekickHosts(org, site) {
+ const path = `/${org}/${site}/config.json`;
+ const [owner, repo, ...parts] = path.slice(1).split('/');
+ const name = parts.pop() || repo || owner;
+ parts.push(name.replace('.html', ''));
+ const url = `${HLX_ADMIN}/sidekick/${owner}/${repo}/main/${parts.join('/')}`;
+ const resp = await daFetch({ url, opts: { method: 'GET' } });
+ if (!resp.ok) return {};
+ try {
+ return await resp.json();
+ } catch {
+ return {};
+ }
+}
+
+async function getAemHrefs(fullPath) {
+ const [org, site, ...pathParts] = fullPath.slice(1).split('/');
+ const pathname = `/${pathParts.join('/')}`;
+ const { host, liveHost, previewHost: preview } = await fetchSidekickHosts(org, site);
+ const prod = host || liveHost;
+ if (!preview || !prod) return null;
+ return {
+ preview: new URL(pathname, `https://${preview}`),
+ prod: new URL(pathname, `https://${prod}`),
+ };
+}
+
+async function saveToAem(path, action) {
+ const [owner, repo, ...parts] = path.slice(1).toLowerCase().split('/');
+ const aemPath = parts.join('/');
+ const url = `${HLX_ADMIN}/${action}/${owner}/${repo}/main/${aemPath}`;
+ const resp = await daFetch({ url, opts: { method: 'POST' } });
+ if (!resp.ok) {
+ const { status } = resp;
+ const authErr = [401, 403].includes(status);
+ const message = authErr ? `Not authorized to ${action}` : `Error during ${action}`;
+ const xerror = resp.headers.get('x-error');
+ const error = { action, status, type: 'error', message };
+ if (xerror && !authErr) {
+ error.details = xerror.replace('[admin] ', '').replace(/^Unable to preview '[^']*':\s*/, '');
+ }
+ return { error };
+ }
+ return resp.json();
+}
+
+/**
+ * @param {{ org?: string, site?: string, path?: string }} state
+ * @returns {string | null} Lowercased AEM path `/owner/repo/...` or null if incomplete.
+ */
+export function buildAemPathFromHashState(state) {
+ const { org, site, path } = state || {};
+ if (!org || !site || !path) return null;
+ const segments = [org, site, ...path.split('/').filter(Boolean)].map((s) => s.toLowerCase());
+ return `/${segments.join('/')}`;
+}
+
+/**
+ * @param {{ message?: string, details?: string }} error
+ * @returns {string}
+ */
+export function formatAemPreviewPublishError(error) {
+ if (!error?.message) return 'Unknown error';
+ return error.details ? `${error.message}: ${error.details}` : error.message;
+}
+
+/**
+ * Preview (admin `preview`) or publish (`live` after a successful preview),
+ * then resolve the URL to open.
+ * @param {{ aemPath: string, action: 'preview' | 'publish' }} params
+ * @returns {Promise<{ ok: true, url: string } | { ok: false, error: Record }>}
+ */
+export async function runAemPreviewOrPublish({ aemPath, action }) {
+ if (action !== 'preview' && action !== 'publish') {
+ return { ok: false, error: { message: 'Invalid action', type: 'error' } };
+ }
+
+ const jsonPreview = await saveToAem(aemPath, 'preview');
+ if (jsonPreview.error) {
+ return { ok: false, error: jsonPreview.error };
+ }
+
+ let json = jsonPreview;
+ if (action === 'publish') {
+ json = await saveToAem(aemPath, 'live');
+ if (json.error) {
+ return { ok: false, error: json.error };
+ }
+ }
+
+ const branch = action === 'publish' ? json.live : json.preview;
+ const href = branch?.url;
+ const aemHrefs = await getAemHrefs(aemPath);
+ const tier = action === 'publish' ? 'prod' : 'preview';
+ const url = (aemHrefs?.[tier] && json.webPath)
+ ? `${aemHrefs[tier].origin}${json.webPath}`
+ : href;
+
+ if (!url) {
+ return { ok: false, error: { message: 'Preview URL missing from response.', type: 'error' } };
+ }
+
+ return { ok: true, url };
+}
diff --git a/nx2/utils/daConfig.js b/nx2/utils/daConfig.js
new file mode 100644
index 000000000..40dbb5838
--- /dev/null
+++ b/nx2/utils/daConfig.js
@@ -0,0 +1,36 @@
+import { DA_ADMIN } from './utils.js';
+import { daFetch } from './api.js';
+
+/** Returns the primary data array from a DA config JSON response (handles multi-sheet). */
+export function getFirstSheet(json) {
+ if (json[':type'] !== 'multi-sheet') return json.data;
+ return json[Object.keys(json)[0]]?.data;
+}
+
+/** Memoized fetches for `/{org}` and optional `/{org}/{site}` config documents. */
+export const fetchDaConfigs = (() => {
+ const cache = {};
+
+ const fetchConfig = async (pathname) => {
+ const resp = await daFetch({ url: `${DA_ADMIN}/config${pathname}/` });
+ if (!resp.ok) return { error: `Error loading ${pathname}`, status: resp.status };
+ return resp.json();
+ };
+
+ const cacheConfig = (key) => {
+ cache[key] = fetchConfig(key).then((result) => {
+ if (result.error) delete cache[key];
+ return result;
+ });
+ return cache[key];
+ };
+
+ return ({ org, site }) => {
+ const orgKey = `/${org}`;
+ const siteKey = site ? `/${org}/${site}` : null;
+
+ const configs = [cache[orgKey] ?? cacheConfig(orgKey)];
+ if (siteKey) configs.push(cache[siteKey] ?? cacheConfig(siteKey));
+ return configs;
+ };
+})();
diff --git a/nx2/utils/ims.js b/nx2/utils/ims.js
index d55dccfa7..492dbab0e 100644
--- a/nx2/utils/ims.js
+++ b/nx2/utils/ims.js
@@ -34,10 +34,12 @@ const IO_ENV = {
export const IMS_ORIGIN = (() => `https://${IMS_ENDPOINT[imsEnv || env]}`)();
export function handleSignIn() {
+ localStorage.setItem('nx-ims', true);
window.adobeIMS.signIn();
}
export function handleSignOut() {
+ localStorage.removeItem('nx-ims');
window.adobeIMS.signOut();
}
@@ -130,9 +132,11 @@ export const loadIms = (() => {
onReady: () => {
const accessToken = window.adobeIMS.getAccessToken();
if (!accessToken) {
+ localStorage.removeItem('nx-ims');
done({ anonymous: true });
return;
}
+ localStorage.setItem('nx-ims', true);
loadDetails(accessToken).then(done, fail);
},
};
diff --git a/nx2/utils/panel.js b/nx2/utils/panel.js
index c6c0b20e7..33ab9f1be 100644
--- a/nx2/utils/panel.js
+++ b/nx2/utils/panel.js
@@ -110,6 +110,12 @@ function resizePointerDown(downEvent) {
handle.addEventListener('pointercancel', onPointerUp);
}
+export function hidePanel(aside) {
+ removePanelState(aside.dataset.position);
+ aside.hidden = true;
+ setPanelsGrid();
+}
+
function buildPanelDOM(aside) {
const edge = aside.dataset.position === 'before' ? 'trailing' : 'leading';
@@ -131,8 +137,10 @@ function buildPanelDOM(aside) {
shell.append(body);
wrapper.append(shell, handle);
aside.append(wrapper);
-}
+ // Allow any consumer inside the panel to close it by firing nx-panel-close.
+ aside.addEventListener('nx-panel-close', () => hidePanel(aside));
+}
export function createPanel({ width = '400px', beforeMain = false, content, fragment } = {}) {
const aside = document.createElement('aside');
aside.classList.add('panel');
@@ -157,13 +165,7 @@ export function createPanel({ width = '400px', beforeMain = false, content, frag
return aside;
}
-export function hidePanel(aside) {
- removePanelState(aside.dataset.position);
- aside.hidden = true;
- setPanelsGrid();
-}
-
-export function unhidePanel(aside) {
+export function showPanel(aside) {
aside.hidden = false;
savePanelState(aside.dataset.position, {
width: aside.dataset.width,
@@ -172,9 +174,10 @@ export function unhidePanel(aside) {
setPanelsGrid();
}
-export { getPanelStore };
+// unhidePanel: legacy alias for showPanel, kept pending a full rename across all callers.
+export { getPanelStore, showPanel as unhidePanel };
-export function showPanel(opts) {
+function mountPanel(opts) {
const aside = createPanel(opts);
setPanelsGrid();
return aside;
@@ -194,7 +197,7 @@ export async function loadPanelContent(value) {
export async function openPanelWithFragment({ width = '400px', beforeMain = false, fragment } = {}) {
const { content, fragment: persistedFragment } = await loadPanelContent(fragment);
if (!content) return undefined;
- return showPanel({ width, beforeMain, content, fragment: persistedFragment });
+ return mountPanel({ width, beforeMain, content, fragment: persistedFragment });
}
export async function restorePanels() {
@@ -205,8 +208,22 @@ export async function restorePanels() {
const { content, fragment: frag } = await loadPanelContent(fragment);
if (content) {
const beforeMain = position === 'before';
- showPanel({ width, beforeMain, content, fragment: frag });
+ mountPanel({ width, beforeMain, content, fragment: frag });
}
}
}
+ document.dispatchEvent(new CustomEvent('nx-panels-restored'));
+}
+
+export async function openPanel({ position, width = '400px', getContent } = {}) {
+ const existing = document.querySelector(`aside.panel[data-position="${position}"]`);
+ if (existing && !existing.hidden) return existing;
+ if (existing?.hidden) {
+ showPanel(existing);
+ return existing;
+ }
+
+ const beforeMain = position === 'before';
+ const content = await getContent?.();
+ return mountPanel({ width, beforeMain, content });
}
diff --git a/nx2/utils/svg.js b/nx2/utils/svg.js
index 126a0bf9a..276a03376 100644
--- a/nx2/utils/svg.js
+++ b/nx2/utils/svg.js
@@ -2,6 +2,8 @@ import { getConfig } from '../scripts/nx.js';
const { codeBase, iconSize } = getConfig();
+export const ICONS_BASE = new URL('../img/icons/', import.meta.url).href;
+
export const loadHrefSvg = (() => {
const cache = {};
@@ -19,7 +21,6 @@ export const loadHrefSvg = (() => {
})();
export default function loadIcons({ paths, icons, size = iconSize }) {
- // Support legacy paths pattern
if (paths) return Promise.all(paths.map((path) => loadHrefSvg(path)));
for (const icon of icons) {
diff --git a/nx2/utils/utils.js b/nx2/utils/utils.js
index 7667049ac..f3e138dcb 100644
--- a/nx2/utils/utils.js
+++ b/nx2/utils/utils.js
@@ -1,6 +1,4 @@
-import { env, getConfig } from '../scripts/nx.js';
-
-const config = getConfig();
+import { env } from '../scripts/nx.js';
export const SUPPORTED_FILES = {
html: 'text/html',
@@ -93,11 +91,6 @@ const parseWindowPath = () => {
const pathView = window.location.pathname.slice(1);
const view = pathView === '' ? 'browse' : pathView;
- if (location.hash.endsWith('/index')) {
- const clean = location.hash.slice(0, -5);
- history.replaceState(null, '', clean);
- }
-
const cleanHash = stripImsHash(location.hash);
if (cleanHash !== location.hash) {
history.replaceState(null, '', `${location.pathname}${location.search}${cleanHash}`);
@@ -149,27 +142,4 @@ export const loadPageStyle = (href) => new Promise((resolve) => {
}
});
-export const loadStyle = (() => {
- const cache = {};
-
- return (supplied) => {
- // Convenience replacement for WCs
- const path = supplied.replace('.js', '.css');
-
- try {
- cache[path] ??= new Promise((resolve) => {
- (async () => {
- const resp = await fetch(path);
- const text = await resp.text();
- const sheet = new CSSStyleSheet();
- sheet.path = path;
- sheet.replaceSync(text);
- resolve(sheet);
- })();
- });
- } catch {
- config.log(`Could not load ${path}`);
- }
- return cache[path];
- };
-})();
+export { loadStyle } from '../scripts/nx.js';
diff --git a/package.json b/package.json
index 35ddcca2d..75c063866 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,8 @@
"nx2:test:file": "wtr --config ./nx2/test/wtr.config.mjs --node-resolve --port=2000 --coverage",
"nx2:test:file:watch": "wtr --config ./nx2/test/wtr.config.mjs --node-resolve --port=2000 --coverage --watch",
"nx2:build:da-lit": "esbuild --format=esm --minify ./nx2/deps/lit/src/index.js --bundle --outfile=./nx2/deps/lit/dist/index.js",
- "nx2:build:spectrum": "node nx2/deps/spectrum/build.js"
+ "nx2:build:spectrum": "node nx2/deps/spectrum/build.js",
+ "nx2:build:mdast": "esbuild --format=esm --minify ./nx2/deps/mdast/src/index.js --bundle --outfile=./nx2/deps/mdast/dist/index.js"
},
"repository": {
"type": "git",