mnfst 0.5.87 → 0.5.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -234,7 +234,8 @@
234
234
  position: relative;
235
235
 
236
236
  /* Header */
237
- :where(header) {
237
+ :where(header):not(:where(:is(aside.frame) header)) {
238
+
238
239
  display: flex;
239
240
  flex-direction: row;
240
241
  align-items: center;
@@ -346,7 +347,7 @@
346
347
  }
347
348
 
348
349
  /* Code */
349
- :where(code) {
350
+ :where(code):not(:where(:is(aside.frame) code)) {
350
351
  flex: 1 1 100%;
351
352
  min-width: 0;
352
353
  white-space: pre;
@@ -587,18 +587,26 @@ const GROUP_INHERITABLE_ATTRS = ['lines', 'edit', 'collapse'];
587
587
  async function setupCodeGroup(group) {
588
588
  if (group.dataset.groupProcessed === 'yes') return;
589
589
 
590
+ // A "panel" is any direct child with a [name] — these are tab panels.
591
+ // Children without [name] are ambient (always visible alongside whichever
592
+ // panel is active). When nothing is named, the group has no tabs at all
593
+ // and renders as a borderless wrapper around its (always-visible) kids —
594
+ // a frame + code pair, for instance, with no title overhead.
590
595
  const sourcePanels = Array.from(group.children).filter(c => c.hasAttribute('name'));
591
- if (sourcePanels.length === 0) return;
596
+ const ambientChildren = Array.from(group.children).filter(c => !c.hasAttribute('name'));
597
+ if (sourcePanels.length === 0 && ambientChildren.length === 0) return;
592
598
  // Claim the group synchronously so re-entrant callers (the directive +
593
599
  // observer can both arrive before the first call's `await` resolves)
594
600
  // bail out — otherwise the wrapper accumulates duplicate tab strips.
595
601
  group.dataset.groupProcessed = 'yes';
596
602
 
597
- // Inherit feature attributes from wrapper to child <pre x-code> panels
598
- // that don't set them. Run BEFORE setupBlock so the inherited attrs
603
+ // Inherit feature attributes from wrapper to child <pre x-code> elements
604
+ // that don't set them. Ambient (unnamed) pres inherit too, so a frame +
605
+ // code pair in an unnamed group still benefits from group-level [lines]
606
+ // / [edit] / [collapse]. Run BEFORE setupBlock so the inherited attrs
599
607
  // drive that block's setup.
600
- for (const panel of sourcePanels) {
601
- if (panel.tagName !== 'PRE') continue;
608
+ const allCodeChildren = [...sourcePanels, ...ambientChildren].filter(c => c.tagName === 'PRE');
609
+ for (const panel of allCodeChildren) {
602
610
  for (const attr of GROUP_INHERITABLE_ATTRS) {
603
611
  if (group.hasAttribute(attr) && !panel.hasAttribute(attr)) {
604
612
  panel.setAttribute(attr, group.getAttribute(attr));
@@ -617,10 +625,11 @@ async function setupCodeGroup(group) {
617
625
  const active = tabNames[0];
618
626
  const slugify = s => s.replace(/\s+/g, '-').toLowerCase();
619
627
 
620
- // Preload hljs + every language needed across the group so each panel's
621
- // setupBlock can synchronously highlight without re-loading.
622
- const codeLangs = sourcePanels
623
- .filter(p => p.tagName === 'PRE' && p.hasAttribute('x-code'))
628
+ // Preload hljs + every language needed across the group (named panels +
629
+ // ambient pres) so each block's setupBlock can synchronously highlight
630
+ // without re-loading.
631
+ const codeLangs = allCodeChildren
632
+ .filter(p => p.hasAttribute('x-code'))
624
633
  .map(p => p.getAttribute('x-code'))
625
634
  .filter(Boolean);
626
635
  let hljs = null;
@@ -647,34 +656,36 @@ async function setupCodeGroup(group) {
647
656
  pre.dataset.groupProcessed = 'yes';
648
657
  if (!pre.hasAttribute('role')) pre.setAttribute('role', 'region');
649
658
 
650
- // Run each code panel through setupBlock so it gets its full feature
651
- // treatment (line numbers, copy button, collapse, editor). Frames stay
652
- // as-is. setupBlock detects [x-code-group] ancestor and suppresses the
653
- // per-panel title bar — the tab strip header serves that role.
654
- for (const panel of sourcePanels) {
655
- if (panel.tagName === 'PRE' && panel.hasAttribute('x-code')) {
659
+ // Run each code child (named or ambient) through setupBlock so it gets
660
+ // its full feature treatment (line numbers, copy button, collapse,
661
+ // editor). Frames stay as-is. setupBlock detects the [x-code-group]
662
+ // ancestor and suppresses the per-panel title bar — the group header
663
+ // (when present) serves that role; for headerless groups, the pre is
664
+ // simply rendered without its own title.
665
+ for (const panel of allCodeChildren) {
666
+ if (panel.hasAttribute('x-code')) {
656
667
  await setupBlock(panel, hljs);
657
668
  panel.dataset.codeProcessed = 'yes';
658
669
  }
659
670
  }
660
671
 
661
672
  // Header: tab strip when there are multiple named panels, plain title bar
662
- // when there's just one. The single-panel form avoids creating a degenerate
663
- // role="tablist" / role="tab" pair that screen readers announce as a
664
- // selectable tab even though there's nothing to switch to same UX and
665
- // ARIA shape as a standalone <pre x-code name="…">, but kept on the group
666
- // wrapper so the wrapper-level copy button and grouped frame siblings still
667
- // work uniformly.
668
- const header = document.createElement('header');
673
+ // when there's just one, no header at all when nothing is named. Skipping
674
+ // the header for headerless groups lets authors pair a frame + code block
675
+ // inside <div x-code-group> with no repetitive "HTML" titleboth render
676
+ // as ambient siblings inside the wrapper.
669
677
  const isSingleTab = tabNames.length === 1;
678
+ const isHeaderless = tabNames.length === 0;
679
+ let header = null;
670
680
  let tablist = null;
671
681
  let tabButtons = [];
682
+ if (!isHeaderless) header = document.createElement('header');
672
683
  if (isSingleTab) {
673
684
  const titleEl = document.createElement('div');
674
685
  titleEl.textContent = active;
675
686
  header.appendChild(titleEl);
676
687
  if (!pre.hasAttribute('aria-label')) pre.setAttribute('aria-label', active);
677
- } else {
688
+ } else if (!isHeaderless) {
678
689
  // The role=tablist sits on an inner <div> that holds the tab buttons.
679
690
  // That way the tablist can have its own overflow-x scrolling (when
680
691
  // there are too many tabs to fit) without dragging sibling header
@@ -714,12 +725,13 @@ async function setupCodeGroup(group) {
714
725
  });
715
726
  });
716
727
  }
717
- pre.insertBefore(header, pre.firstChild);
728
+ if (header) pre.insertBefore(header, pre.firstChild);
718
729
 
719
730
  // Wire ARIA / IDs on each panel. Multi-tab groups get role="tabpanel" with
720
731
  // aria-labelledby pointing at its tab button; single-tab groups keep the
721
732
  // standalone role="region" + aria-label shape (label set on the wrapper
722
- // above) so there's no orphan tabpanel without a tablist.
733
+ // above) so there's no orphan tabpanel without a tablist. Headerless
734
+ // groups have no tabpanels — every child is ambient.
723
735
  sourcePanels.forEach((panel, i) => {
724
736
  const name = panel.getAttribute('name');
725
737
  panel.id = panel.id || `${slugify(name)}-panel-${i}`;
@@ -743,7 +755,7 @@ async function setupCodeGroup(group) {
743
755
  // competing with the header's own overflow-scroll region. Same
744
756
  // positioning rule as for a standalone <pre x-code copy>.
745
757
  const wrapperHasCopy = pre.hasAttribute('copy');
746
- const anyPanelCopy = sourcePanels.some(p => p.hasAttribute('copy'));
758
+ const anyPanelCopy = [...sourcePanels, ...ambientChildren].some(p => p.hasAttribute('copy'));
747
759
  let copyBtn = null;
748
760
  if (wrapperHasCopy || anyPanelCopy) {
749
761
  copyBtn = document.createElement('button');
@@ -754,9 +766,15 @@ async function setupCodeGroup(group) {
754
766
  // When multiple panels share the active name (e.g. a paired
755
767
  // frame + code), prefer the <pre x-code> for copy — the source
756
768
  // is what an author wants to take, not the rendered frame's
757
- // text content.
758
- const sameName = sourcePanels.filter(p => p.getAttribute('name') === activeName);
759
- const activePanel = sameName.find(p => p.tagName === 'PRE' && p.hasAttribute('x-code')) || sameName[0];
769
+ // text content. Headerless groups have no active name, so pick
770
+ // the first ambient <pre x-code> child instead.
771
+ let activePanel;
772
+ if (isHeaderless) {
773
+ activePanel = ambientChildren.find(p => p.tagName === 'PRE' && p.hasAttribute('x-code'));
774
+ } else {
775
+ const sameName = sourcePanels.filter(p => p.getAttribute('name') === activeName);
776
+ activePanel = sameName.find(p => p.tagName === 'PRE' && p.hasAttribute('x-code')) || sameName[0];
777
+ }
760
778
  if (!activePanel) return;
761
779
  const code = activePanel.querySelector(':scope > code') || activePanel;
762
780
  try {
@@ -774,6 +792,7 @@ async function setupCodeGroup(group) {
774
792
  // visibility per-tab: when [copy] sits on the wrapper it stays visible
775
793
  // for every tab; when it sits only on individual panels, the button
776
794
  // shows for tabs whose panels carry [copy] and hides for the rest.
795
+ // Headerless groups have no tabs to switch, so activate() is a no-op.
777
796
  let activeName = active;
778
797
  function activate(name) {
779
798
  activeName = name;
@@ -792,7 +811,7 @@ async function setupCodeGroup(group) {
792
811
  copyBtn.style.display = activeCanCopy ? '' : 'none';
793
812
  }
794
813
  }
795
- activate(active);
814
+ if (!isHeaderless) activate(active);
796
815
  }
797
816
 
798
817
  // ─── Page scan + observation ─────────────────────────────────────────────────
@@ -1 +1 @@
1
- @import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap");:root{--icon-code-copy:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Crect width='14' height='14' x='8' y='8' rx='2' ry='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/g%3E%3C/svg%3E");--icon-code-copied:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1jb3B5LWNoZWNrLWljb24gbHVjaWRlLWNvcHktY2hlY2siIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0ibTEyIDE1IDIgMiA0LTQiLz48cmVjdCB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHg9IjgiIHk9IjgiIHJ4PSIyIiByeT0iMiIvPjxwYXRoIGQ9Ik00IDE2Yy0xLjEgMC0yLS45LTItMlY0YzAtMS4xLjktMiAyLTJoMTBjMS4xIDAgMiAuOSAyIDIiLz48L3N2Zz4=");--icon-code-expand:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 9 6 6 6-6'/%3E%3C/svg%3E");--color-code-keyword:#b8860b;--color-code-string:#8b4513;--color-code-comment:gray;--color-code-function:peru;--color-code-number:sienna;--color-code-operator:#2f4f4f;--color-code-class-name:#daa520;--color-code-tag:#4682b4;--color-code-attr-name:#ff8c00;--color-code-attr-value:#8b4513;--color-code-property:sienna;--color-code-selector:#4682b4;--color-code-punctuation:#2f4f4f;--color-code-builtin:#b8860b;--color-code-constant:sienna;--color-code-boolean:sienna;--color-code-regex:#8b4513;--color-code-symbol:#daa520;--color-code-entity:#daa520;--color-code-url:sienna;--color-code-atrule:#b8860b;--color-code-rule:#4682b4;--color-code-doctype:gray;--color-code-cdata:gray;--color-code-prolog:gray;--color-code-namespace:gray;--color-code-important:#b8860b;--color-code-inserted:#228b22;--color-code-deleted:#dc143c;--color-code-char:#8b4513}.dark{--color-code-keyword:#f4a460;--color-code-string:#deb887;--color-code-comment:#8b8b8b;--color-code-function:#daa520;--color-code-number:tan;--color-code-operator:wheat;--color-code-class-name:peru;--color-code-tag:#87ceeb;--color-code-attr-name:gold;--color-code-attr-value:#deb887;--color-code-property:tan;--color-code-selector:#87ceeb;--color-code-punctuation:wheat;--color-code-builtin:#f4a460;--color-code-constant:tan;--color-code-boolean:tan;--color-code-regex:#deb887;--color-code-symbol:peru;--color-code-entity:peru;--color-code-url:tan;--color-code-atrule:#f4a460;--color-code-rule:#98fb98;--color-code-doctype:#8b8b8b;--color-code-cdata:#8b8b8b;--color-code-prolog:#8b8b8b;--color-code-namespace:#8b8b8b;--color-code-important:#f4a460;--color-code-inserted:#98fb98;--color-code-deleted:#f08080;--color-code-char:#deb887}@layer utilities{.hljs-comment{color:var(--color-code-comment)!important}.hljs-keyword{color:var(--color-code-keyword)!important}.hljs-string{color:var(--color-code-string)!important}.hljs-number{color:var(--color-code-number)!important}.hljs-literal{color:var(--color-code-constant)!important}.hljs-type{color:var(--color-code-class-name)!important}.hljs-variable{color:var(--color-code-property)!important}.hljs-variable.language_{color:var(--color-code-keyword)!important}.hljs-variable.constant_{color:var(--color-code-constant)!important}.hljs-title{color:var(--color-code-function)!important}.hljs-title.class_.inherited__{color:var(--color-code-class-name)!important}.hljs-title.function_.invoke__{color:var(--color-code-function)!important}.hljs-params{color:var(--color-code-property)!important}.hljs-doctag{color:var(--color-code-keyword)!important;font-weight:600!important}.hljs-meta{color:var(--color-code-comment)!important}.hljs-meta.keyword_,.hljs-meta.prompt_{color:var(--color-code-keyword)!important}.hljs-meta.string_{color:var(--color-code-string)!important}.hljs-section{color:var(--color-code-keyword)!important;font-weight:600!important}.hljs-name{color:var(--color-code-tag)!important}.hljs-attribute{color:var(--color-code-attr-name)!important}.hljs-bullet{color:var(--color-code-punctuation)!important}.hljs-code{color:var(--color-code-property)!important}.hljs-formula{color:var(--color-code-number)!important}.hljs-quote{color:var(--color-code-string)!important}.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo{color:var(--color-code-selector)!important}.hljs-template-tag{color:var(--color-code-tag)!important}.hljs-template-variable{color:var(--color-code-property)!important}.hljs-subst{color:var(--color-code-string)!important}}@layer components{:where(code[role=button]){height:fit-content}:where(.code-copied-icon){background-color:currentColor;display:inline-block;height:1em;mask-image:var(--icon-code-copied);mask-position:center;mask-repeat:no-repeat;mask-size:contain;width:1em}:where([x-code],[x-code-group]):not(.unstyle){position:relative;:where(header){align-items:center;background-color:color-mix(in oklch,var(--color-content-stark,#2f4f4f) 8%,transparent);color:var(--color-content-neutral,gray);display:flex;flex-direction:row;font-family:var(--font-sans,sans-serif);font-size:inherit;min-height:var(--spacing-field-height,2.25rem);padding-inline-end:var(--spacing-field-height);padding-inline-start:calc(var(--spacing, .25rem)*4);width:100%;&:has(button[role=tab]){padding-inline-start:calc(var(--spacing, .25rem)*2)}& [role=tablist]{display:flex;flex-direction:row;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none;&::-webkit-scrollbar{display:none}& button[role=tab]{background:transparent;border:0;color:inherit;flex-shrink:0;font-family:inherit;font-size:inherit;height:fit-content;justify-content:start;&:hover{background-color:var(--color-field-surface,color-mix(#2f4f4f 10%,transparent));color:var(--color-content-stark,#2f4f4f)}&[aria-selected=true]{color:var(--color-brand-content,#daa520);pointer-events:none;position:relative}}}}& button.copy{background-color:transparent;font-size:75%;inset-inline-end:0;position:absolute;top:0;&:hover{background-color:transparent;&:after{background-color:var(--color-field-inverse,#2f4f4f)}}&:after{background-color:var(--color-content-neutral,gray);content:"";display:block;height:.8125rem;mask-image:var(--icon-code-copy);mask-repeat:no-repeat;mask-size:contain;width:.8125rem}&.copied:after{mask-image:var(--icon-code-copied)}}:where(header)::-webkit-scrollbar{display:none}& .lines{color:var(--color-content-subtle,#a9a9a9);display:inline-flex;flex-direction:column;font-family:inherit;font-size:inherit;inset-inline-start:0;line-height:inherit;padding:calc(var(--spacing, .25rem)*4) 0 calc(var(--spacing, .25rem)*4) calc(var(--spacing, .25rem)*4);pointer-events:none;position:absolute;text-align:end;user-select:none}&:has(.lines) code{padding-inline-start:calc(var(--spacing, .25rem)*6 + 2ch)}:where(code){flex:1 1 100%;min-width:0;white-space:pre;white-space-collapse:preserve}& button.expand{align-items:center;background:transparent;border:0;border-radius:0;color:var(--color-content-subtle,#a9a9a9);cursor:pointer;display:flex;font-family:var(--font-sans,sans-serif);font-size:inherit;height:var(--spacing-field-height,2.25rem);justify-content:center;order:99;padding:0 calc(var(--spacing, .25rem)*2);transition:var(--transition,all .05s ease-in-out);width:100%;&:hover{background:color-mix(in oklch,var(--color-content-stark,#2f4f4f) 8%,transparent);color:var(--color-content-neutral,gray)}}&[data-collapsed] .lines,&[data-collapsed] code{mask-image:linear-gradient(180deg,#000 0,#000 calc(100% - 3em),transparent calc(100% - .75em),transparent);max-height:calc(var(--collapse-lines, 20)*1.7em + var(--spacing, .25rem)*8);overflow:hidden}:where(aside.frame){border:0;order:1;white-space:normal;white-space-collapse:collapse}}:where([x-code-group]){flex-flow:column;:where([x-code]){background:transparent;border:0;border-radius:0}}:where([x-code]):has(>header){& .lines{top:var(--spacing-field-height,2.25rem)}}}
1
+ @import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap");:root{--icon-code-copy:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2'%3E%3Crect width='14' height='14' x='8' y='8' rx='2' ry='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/g%3E%3C/svg%3E");--icon-code-copied:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1jb3B5LWNoZWNrLWljb24gbHVjaWRlLWNvcHktY2hlY2siIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0ibTEyIDE1IDIgMiA0LTQiLz48cmVjdCB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHg9IjgiIHk9IjgiIHJ4PSIyIiByeT0iMiIvPjxwYXRoIGQ9Ik00IDE2Yy0xLjEgMC0yLS45LTItMlY0YzAtMS4xLjktMiAyLTJoMTBjMS4xIDAgMiAuOSAyIDIiLz48L3N2Zz4=");--icon-code-expand:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m6 9 6 6 6-6'/%3E%3C/svg%3E");--color-code-keyword:#b8860b;--color-code-string:#8b4513;--color-code-comment:gray;--color-code-function:peru;--color-code-number:sienna;--color-code-operator:#2f4f4f;--color-code-class-name:#daa520;--color-code-tag:#4682b4;--color-code-attr-name:#ff8c00;--color-code-attr-value:#8b4513;--color-code-property:sienna;--color-code-selector:#4682b4;--color-code-punctuation:#2f4f4f;--color-code-builtin:#b8860b;--color-code-constant:sienna;--color-code-boolean:sienna;--color-code-regex:#8b4513;--color-code-symbol:#daa520;--color-code-entity:#daa520;--color-code-url:sienna;--color-code-atrule:#b8860b;--color-code-rule:#4682b4;--color-code-doctype:gray;--color-code-cdata:gray;--color-code-prolog:gray;--color-code-namespace:gray;--color-code-important:#b8860b;--color-code-inserted:#228b22;--color-code-deleted:#dc143c;--color-code-char:#8b4513}.dark{--color-code-keyword:#f4a460;--color-code-string:#deb887;--color-code-comment:#8b8b8b;--color-code-function:#daa520;--color-code-number:tan;--color-code-operator:wheat;--color-code-class-name:peru;--color-code-tag:#87ceeb;--color-code-attr-name:gold;--color-code-attr-value:#deb887;--color-code-property:tan;--color-code-selector:#87ceeb;--color-code-punctuation:wheat;--color-code-builtin:#f4a460;--color-code-constant:tan;--color-code-boolean:tan;--color-code-regex:#deb887;--color-code-symbol:peru;--color-code-entity:peru;--color-code-url:tan;--color-code-atrule:#f4a460;--color-code-rule:#98fb98;--color-code-doctype:#8b8b8b;--color-code-cdata:#8b8b8b;--color-code-prolog:#8b8b8b;--color-code-namespace:#8b8b8b;--color-code-important:#f4a460;--color-code-inserted:#98fb98;--color-code-deleted:#f08080;--color-code-char:#deb887}@layer utilities{.hljs-comment{color:var(--color-code-comment)!important}.hljs-keyword{color:var(--color-code-keyword)!important}.hljs-string{color:var(--color-code-string)!important}.hljs-number{color:var(--color-code-number)!important}.hljs-literal{color:var(--color-code-constant)!important}.hljs-type{color:var(--color-code-class-name)!important}.hljs-variable{color:var(--color-code-property)!important}.hljs-variable.language_{color:var(--color-code-keyword)!important}.hljs-variable.constant_{color:var(--color-code-constant)!important}.hljs-title{color:var(--color-code-function)!important}.hljs-title.class_.inherited__{color:var(--color-code-class-name)!important}.hljs-title.function_.invoke__{color:var(--color-code-function)!important}.hljs-params{color:var(--color-code-property)!important}.hljs-doctag{color:var(--color-code-keyword)!important;font-weight:600!important}.hljs-meta{color:var(--color-code-comment)!important}.hljs-meta.keyword_,.hljs-meta.prompt_{color:var(--color-code-keyword)!important}.hljs-meta.string_{color:var(--color-code-string)!important}.hljs-section{color:var(--color-code-keyword)!important;font-weight:600!important}.hljs-name{color:var(--color-code-tag)!important}.hljs-attribute{color:var(--color-code-attr-name)!important}.hljs-bullet{color:var(--color-code-punctuation)!important}.hljs-code{color:var(--color-code-property)!important}.hljs-formula{color:var(--color-code-number)!important}.hljs-quote{color:var(--color-code-string)!important}.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo{color:var(--color-code-selector)!important}.hljs-template-tag{color:var(--color-code-tag)!important}.hljs-template-variable{color:var(--color-code-property)!important}.hljs-subst{color:var(--color-code-string)!important}}@layer components{:where(code[role=button]){height:fit-content}:where(.code-copied-icon){background-color:currentColor;display:inline-block;height:1em;mask-image:var(--icon-code-copied);mask-position:center;mask-repeat:no-repeat;mask-size:contain;width:1em}:where([x-code],[x-code-group]):not(.unstyle){position:relative;:where(header):not(:where(:is(aside.frame) header)){align-items:center;background-color:color-mix(in oklch,var(--color-content-stark,#2f4f4f) 8%,transparent);color:var(--color-content-neutral,gray);display:flex;flex-direction:row;font-family:var(--font-sans,sans-serif);font-size:inherit;min-height:var(--spacing-field-height,2.25rem);padding-inline-end:var(--spacing-field-height);padding-inline-start:calc(var(--spacing, .25rem)*4);width:100%;&:has(button[role=tab]){padding-inline-start:calc(var(--spacing, .25rem)*2)}& [role=tablist]{display:flex;flex-direction:row;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none;&::-webkit-scrollbar{display:none}& button[role=tab]{background:transparent;border:0;color:inherit;flex-shrink:0;font-family:inherit;font-size:inherit;height:fit-content;justify-content:start;&:hover{background-color:var(--color-field-surface,color-mix(#2f4f4f 10%,transparent));color:var(--color-content-stark,#2f4f4f)}&[aria-selected=true]{color:var(--color-brand-content,#daa520);pointer-events:none;position:relative}}}}& button.copy{background-color:transparent;font-size:75%;inset-inline-end:0;position:absolute;top:0;&:hover{background-color:transparent;&:after{background-color:var(--color-field-inverse,#2f4f4f)}}&:after{background-color:var(--color-content-neutral,gray);content:"";display:block;height:.8125rem;mask-image:var(--icon-code-copy);mask-repeat:no-repeat;mask-size:contain;width:.8125rem}&.copied:after{mask-image:var(--icon-code-copied)}}:where(header)::-webkit-scrollbar{display:none}& .lines{color:var(--color-content-subtle,#a9a9a9);display:inline-flex;flex-direction:column;font-family:inherit;font-size:inherit;inset-inline-start:0;line-height:inherit;padding:calc(var(--spacing, .25rem)*4) 0 calc(var(--spacing, .25rem)*4) calc(var(--spacing, .25rem)*4);pointer-events:none;position:absolute;text-align:end;user-select:none}&:has(.lines) code{padding-inline-start:calc(var(--spacing, .25rem)*6 + 2ch)}:where(code):not(:where(:is(aside.frame) code)){flex:1 1 100%;min-width:0;white-space:pre;white-space-collapse:preserve}& button.expand{align-items:center;background:transparent;border:0;border-radius:0;color:var(--color-content-subtle,#a9a9a9);cursor:pointer;display:flex;font-family:var(--font-sans,sans-serif);font-size:inherit;height:var(--spacing-field-height,2.25rem);justify-content:center;order:99;padding:0 calc(var(--spacing, .25rem)*2);transition:var(--transition,all .05s ease-in-out);width:100%;&:hover{background:color-mix(in oklch,var(--color-content-stark,#2f4f4f) 8%,transparent);color:var(--color-content-neutral,gray)}}&[data-collapsed] .lines,&[data-collapsed] code{mask-image:linear-gradient(180deg,#000 0,#000 calc(100% - 3em),transparent calc(100% - .75em),transparent);max-height:calc(var(--collapse-lines, 20)*1.7em + var(--spacing, .25rem)*8);overflow:hidden}:where(aside.frame){border:0;order:1;white-space:normal;white-space-collapse:collapse}}:where([x-code-group]){flex-flow:column;:where([x-code]){background:transparent;border:0;border-radius:0}}:where([x-code]):has(>header){& .lines{top:var(--spacing-field-height,2.25rem)}}}
@@ -2,7 +2,7 @@
2
2
  "manifest.appwrite.auth.js": "sha384-to37ssZJXGeOS6+rf2VI47ox2mEqgsi5oQ1E5vv8XU/lDspbDFE1KHEMm8TxBhxW",
3
3
  "manifest.appwrite.data.js": "sha384-00ulLT+GAIuPHA/rRT9p98vYlsyDzkyKXtg86BDQ6FGQa5vVVN+W6kuforniBAsz",
4
4
  "manifest.appwrite.presence.js": "sha384-uxRpx9/Jj0kGtklH5QmUlAzD3zdSvFRfK6bcJQqxl+Bsf5tOo4zgwqJTQgtZoHQP",
5
- "manifest.code.js": "sha384-jO0j5UsOTYYlZs5x/NSzegJQqrEolLEXJZKV29Dxl6tD6qNQwLai1pifsBKh4xWw",
5
+ "manifest.code.js": "sha384-r0cbKtg4/bih6lzNiIdnwiCurW2ohbC8E8zNljSSoO5r+5ewPQcWP4QT+jZp3Oec",
6
6
  "manifest.color.js": "sha384-Z9G/lzt0vVMxjz4wkPuGG1X9mmQAJR15aOoGX3ephf7r2wnlUWet5GLgkUMtT4vt",
7
7
  "manifest.colorpicker.js": "sha384-0EVn+Ha06h7FIvOxc6WjZYnKYXzi+zba08yKvczSEGTRkWRxyKN2TFrZHI1SDCXu",
8
8
  "manifest.components.js": "sha384-3dCTD5EwCZTiX+1obYtDNM3WWwPh2JDQUQQsdRUUK3gs6FXjse1ShkKaT/2jsNaI",
@@ -10,7 +10,7 @@
10
10
  "manifest.dropdowns.js": "sha384-WMrFoSpKfJuo81dyrwhVrDO8rq+rDwh2x8x4nH01BY5ZHkvjE+/SaT2gWCI0zOn+",
11
11
  "manifest.icons.js": "sha384-uOkboYrovjCpl22eey3Jaxpey+pOnot5NDnRRumcRxiR7IOVaRh1i20gYnWXR5dW",
12
12
  "manifest.localization.js": "sha384-eKdBIMEAwsugPP2p2fuPzQUkU44f1+Y0JgukMJ1KXLQY1/AYvpcGsEiritVDElsN",
13
- "manifest.markdown.js": "sha384-gxRxl0QFZc66KfpRNG5bj1mHGD61lLg4+zN77IWclVw6nLW+SIw1ciNqQa187+fK",
13
+ "manifest.markdown.js": "sha384-3LgPiHrftPqAIJGhxi87C0TtfJXbsH0Qj4JmfmYgV4y5UjSx7nVSP+ppsRUWT0Xs",
14
14
  "manifest.resize.js": "sha384-Ak5gf44ERfh9pOSAD1qZzJSysslpwBCkevIlz7R3dszTUyzUKGKGF4pn5arOtgG0",
15
15
  "manifest.router.js": "sha384-n6xmIfWnYzd/0kkVTFuHhFzHuxiDgZ1Lg1W0yB6/w3Myw5pQ6PgE6SJBHfVsO7/D",
16
16
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
@@ -197,10 +197,16 @@ async function configureMarked(marked) {
197
197
  const openMatch = src.match(/^:::(.*?)(?:\n|$)/);
198
198
  if (!openMatch) return;
199
199
 
200
- // Parse the opening line for classes and icon
200
+ // Parse the opening line for classes, icon, and an optional
201
+ // quoted name. The name follows the same convention as the
202
+ // fenced-code info-string (`::: frame "header.html"`) — it
203
+ // becomes the `name` attribute on the rendered <aside>, which
204
+ // lets the code plugin pair the frame with a fenced block
205
+ // sharing the same name inside an <x-code-group>.
201
206
  const openingLine = openMatch[1].trim();
202
207
  let classes = '';
203
208
  let iconValue = '';
209
+ let nameValue = '';
204
210
 
205
211
  // Match icon="value" pattern
206
212
  const iconMatch = openingLine.match(/icon="([^"]+)"/);
@@ -208,8 +214,15 @@ async function configureMarked(marked) {
208
214
  iconValue = iconMatch[1];
209
215
  }
210
216
 
211
- // Get all class names (remove icon attribute first)
212
- classes = openingLine.replace(/\s*icon="[^"]+"\s*/, '').trim();
217
+ // Match the first quoted string (skipping the icon="…" pair).
218
+ const withoutIcon = openingLine.replace(/\s*icon="[^"]+"\s*/, ' ');
219
+ const nameMatch = withoutIcon.match(/"([^"]+)"/);
220
+ if (nameMatch) {
221
+ nameValue = nameMatch[1];
222
+ }
223
+
224
+ // Get all class names (remove icon attribute and quoted name first)
225
+ classes = withoutIcon.replace(/\s*"[^"]+"\s*/, ' ').replace(/\s+/g, ' ').trim();
213
226
 
214
227
  const startPos = openMatch[0].length;
215
228
 
@@ -226,6 +239,7 @@ async function configureMarked(marked) {
226
239
  raw: raw,
227
240
  classes: classes,
228
241
  iconValue: iconValue,
242
+ nameValue: nameValue,
229
243
  text: content.trim()
230
244
  };
231
245
  }
@@ -233,6 +247,7 @@ async function configureMarked(marked) {
233
247
  renderer(token) {
234
248
  let classes = token.classes || '';
235
249
  const iconValue = token.iconValue || '';
250
+ const nameValue = token.nameValue || '';
236
251
 
237
252
  // `::: frame demo` — render the frame contents live AND emit
238
253
  // a sibling <pre x-code="html"> showing the same source. Lets
@@ -267,9 +282,10 @@ async function configureMarked(marked) {
267
282
  `<div>${parsedContent}</div>` :
268
283
  parsedContent;
269
284
 
270
- const aside = `<aside${classes ? ` class="${classes}"` : ''}>${iconHtml}${wrappedContent}</aside>`;
285
+ const nameAttr = nameValue ? ` name="${escapeForAttribute(nameValue)}"` : '';
286
+ const aside = `<aside${classes ? ` class="${classes}"` : ''}${nameAttr}>${iconHtml}${wrappedContent}</aside>`;
271
287
  if (isDemo) {
272
- return `${aside}\n<pre x-code="html" copy><code>${escapeForText(token.text.trim())}</code></pre>\n`;
288
+ return `${aside}\n<pre x-code="html" copy${nameAttr}><code>${escapeForText(token.text.trim())}</code></pre>\n`;
273
289
  }
274
290
  return `${aside}\n`;
275
291
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.87",
3
+ "version": "0.5.89",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",