mnfst 0.5.83 → 0.5.84

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.
@@ -256,8 +256,12 @@
256
256
  display: flex;
257
257
  flex-direction: row;
258
258
  overflow-x: auto;
259
- -ms-overflow-style: none;
260
259
  scrollbar-width: none;
260
+ -ms-overflow-style: none;
261
+
262
+ &::-webkit-scrollbar {
263
+ display: none
264
+ }
261
265
 
262
266
  /* Tab buttons */
263
267
  & button[role="tab"] {
@@ -286,10 +290,10 @@
286
290
 
287
291
  /* Copy code button */
288
292
  & button.copy {
289
- font-size: 75%;
290
293
  position: absolute;
291
294
  top: 0;
292
295
  inset-inline-end: 0;
296
+ font-size: 75%;
293
297
  background-color: transparent;
294
298
 
295
299
  &:hover {
@@ -199,11 +199,13 @@ function resolveLanguage(hljs, langAttr) {
199
199
 
200
200
  // ─── Content prep helpers ────────────────────────────────────────────────────
201
201
 
202
- // Drop one leading and one trailing newline if present. Authors typically write
203
- // <pre x-code> on its own line, which leaves stray newlines that throw off line
204
- // numbering and the collapse threshold.
202
+ // Drop any leading/trailing blank lines (including pure-whitespace lines).
203
+ // Authors typically write <pre x-code> on its own line, leaving stray newlines
204
+ // that throw off line numbering and the collapse threshold. Stripping multiple
205
+ // also covers the case where authors leave a blank line before </pre> for
206
+ // readability, which would otherwise render as a visible trailing gap.
205
207
  function trimWrappingNewlines(text) {
206
- return text.replace(/^\n/, '').replace(/\n$/, '');
208
+ return text.replace(/^(?:[ \t]*\n)+/, '').replace(/(?:\n[ \t]*)+$/, '');
207
209
  }
208
210
 
209
211
  // Remove the smallest common leading-whitespace block from every non-empty
@@ -248,18 +250,23 @@ function resolveSource(el) {
248
250
  if (target) return prepSource(target.innerHTML);
249
251
  }
250
252
  const lang = (el.getAttribute('x-code') || el.getAttribute('language') || '').toLowerCase();
251
- // Markdown plugin emits <pre x-code="lang"><code>source</code></pre>. When
252
- // a child <code> exists, read from it directly so we don't pick up the
253
- // wrapping element itself when innerHTML reading is required.
253
+ // Markdown plugin emits <pre x-code="lang"><code>source</code></pre>. The
254
+ // <code> body is text-only (entity-escaped HTML decoded by the browser
255
+ // into a text node), so textContent returns the exact source the author
256
+ // wrote — preserving valueless attributes (data-tailwind, not
257
+ // data-tailwind=""), multi-line attribute lists, and other formatting
258
+ // that an innerHTML round-trip would normalise away.
254
259
  const childCode = el.querySelector(':scope > code');
255
260
  if (childCode) {
256
- if (HTML_LIKE_LANGS.has(lang)) {
257
- const decoder = document.createElement('textarea');
258
- decoder.innerHTML = childCode.innerHTML;
259
- return prepSource(decoder.value);
260
- }
261
261
  return prepSource(childCode.textContent);
262
262
  }
263
+ // Raw HTML case: the author wrote `<pre x-code="html"><div…></div></pre>`
264
+ // directly in an .html file. Read innerHTML and decode any entities so
265
+ // mixed-style authoring still works — but valueless attributes will pick
266
+ // up the browser's `=""` normalisation here, which is unavoidable once
267
+ // the markup has been parsed into real DOM nodes. Authors who want exact
268
+ // source preservation should use a fenced markdown block or write the
269
+ // body as entity-escaped text inside the pre.
263
270
  if (HTML_LIKE_LANGS.has(lang)) {
264
271
  const decoder = document.createElement('textarea');
265
272
  decoder.innerHTML = el.innerHTML;
@@ -867,12 +874,27 @@ function processOne(el) {
867
874
 
868
875
  // Start observing every candidate in `root` that hasn't already been
869
876
  // processed. Idempotent — re-observation of an already-observed element
870
- // is a no-op per the IntersectionObserver spec.
877
+ // is a no-op per the IntersectionObserver spec. Elements that are already
878
+ // visible at call time are processed immediately, then unobserved — this
879
+ // makes markdown-plugin-injected blocks render synchronously instead of
880
+ // waiting for the next IO callback (which can stall in headless tests
881
+ // and feels laggy for live markdown updates).
871
882
  function observeAll(root = document) {
872
883
  const io = ensureObserver();
873
- root.querySelectorAll('[x-code]:not([data-code-processed])').forEach(el => io.observe(el));
874
- root.querySelectorAll('[x-code-group]:not([data-group-processed])').forEach(el => io.observe(el));
875
- root.querySelectorAll('pre:not([x-code]):not([data-code-processed]) > code[class*="language-"]').forEach(el => io.observe(el));
884
+ const candidates = [
885
+ ...root.querySelectorAll('[x-code]:not([data-code-processed])'),
886
+ ...root.querySelectorAll('[x-code-group]:not([data-group-processed])'),
887
+ ...root.querySelectorAll('pre:not([x-code]):not([data-code-processed]) > code[class*="language-"]')
888
+ ];
889
+ for (const el of candidates) {
890
+ io.observe(el);
891
+ // Skip the immediate-process shortcut for elements that are hidden
892
+ // (display:none route panels, off-screen drawers): keep them observed
893
+ // so they highlight on the next intersection callback when revealed.
894
+ if (typeof el.checkVisibility === 'function' && !el.checkVisibility()) continue;
895
+ io.unobserve(el);
896
+ processOne(el);
897
+ }
876
898
  }
877
899
 
878
900
  // Re-scan after markdown injections (the markdown plugin dispatches this
@@ -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;-ms-overflow-style:none;scrollbar-width: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){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)}}}
package/lib/manifest.css CHANGED
@@ -174,7 +174,7 @@
174
174
  :where(hr) {
175
175
  height: 1px;
176
176
  color: inherit;
177
- background-color: var(--color-line, color-mix(darkslategray 10%, transparent));
177
+ background-color: var(--color-line, color-mix(darkslategray 12%, transparent));
178
178
  border: 0 none
179
179
  }
180
180
 
@@ -2912,7 +2912,7 @@
2912
2912
  @layer base {
2913
2913
 
2914
2914
  /* All text elements */
2915
- :where(a, abbr, address, blockquote, code, del, figcaption, h1, h2, h3, h4, h5, h6, ins, label:not(.avatar, :has([type=file], [type=search])), legend, p, small, cite, q):not(.unstyle),
2915
+ :where(abbr, address, blockquote, code, del, figcaption, h1, h2, h3, h4, h5, h6, ins, label:not(.avatar, :has([type=file], [type=search])), legend, p, small, cite, q):not(.unstyle),
2916
2916
  .h1,
2917
2917
  .h2,
2918
2918
  .h3,
@@ -2922,10 +2922,17 @@
2922
2922
  .paragraph,
2923
2923
  .small,
2924
2924
  .caption,
2925
- .label,
2926
- .link {
2925
+ .label {
2927
2926
  color: var(--color-content-stark, darkslategray);
2928
2927
 
2928
+ /* Inline links */
2929
+ & a:not(.unstyle),
2930
+ & .link {
2931
+ text-decoration: underline;
2932
+ text-underline-offset: 2px;
2933
+ color: inherit
2934
+ }
2935
+
2929
2936
  /* Support inline icons */
2930
2937
  &:where(:has([x-icon])) {
2931
2938
  display: inline-flex;
@@ -2938,7 +2945,7 @@
2938
2945
  }
2939
2946
 
2940
2947
  /* Inline span alignment */
2941
- & span {
2948
+ :where(span) {
2942
2949
  vertical-align: inherit
2943
2950
  }
2944
2951
  }
@@ -3020,11 +3027,6 @@
3020
3027
  }
3021
3028
  }
3022
3029
 
3023
- :where(abbr, address, blockquote, code, del, figcaption, h1, h2, h3, h4, h5, h6, ins, legend, p, small, cite, q, .h1, .h2, .h3, .h4, .h5, .h6, .paragraph, .small, .caption, .label):not(.unstyle)>a {
3024
- text-decoration: underline;
3025
- text-underline-offset: 2px
3026
- }
3027
-
3028
3030
  /* Asides */
3029
3031
  :where(aside):not(.unstyle) {
3030
3032
  padding: calc(var(--spacing, 0.25rem) * 4);
@@ -3279,18 +3281,26 @@
3279
3281
  tab-size: 4;
3280
3282
  white-space: pre;
3281
3283
  white-space-collapse: preserve;
3282
- overflow-x: auto;
3284
+ overflow: hidden;
3283
3285
 
3284
3286
  & code {
3285
3287
  flex-grow: 1;
3286
3288
  display: inline-block;
3287
3289
  height: auto;
3288
3290
  padding: calc(var(--spacing, 0.25rem) * 4);
3291
+ padding-inline-end: calc(var(--spacing, 0.25rem) * 12);
3289
3292
  font-size: inherit;
3290
3293
  line-height: inherit;
3291
3294
  background-color: transparent;
3292
3295
  border: 0 none;
3293
- box-shadow: none
3296
+ box-shadow: none;
3297
+ overflow-x: auto;
3298
+ scrollbar-width: none;
3299
+ -ms-overflow-style: none;
3300
+
3301
+ &::-webkit-scrollbar {
3302
+ display: none
3303
+ }
3294
3304
  }
3295
3305
 
3296
3306
  &:not(:has(code)) {
@@ -2,25 +2,23 @@
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-zjtiVXsPoPK4H/2gN4Xe4Bd0iKUwnv4MbteX9AmTWqNz49E+a3eCuONYHpSneStZ",
5
+ "manifest.code.js": "sha384-zxu52NMTOIu+wd0b4YITLmGMR637VakyN9rBVRYfHA1qQW4hGTi2j7/NbqzPT3j1",
6
6
  "manifest.color.js": "sha384-Z9G/lzt0vVMxjz4wkPuGG1X9mmQAJR15aOoGX3ephf7r2wnlUWet5GLgkUMtT4vt",
7
7
  "manifest.colorpicker.js": "sha384-0EVn+Ha06h7FIvOxc6WjZYnKYXzi+zba08yKvczSEGTRkWRxyKN2TFrZHI1SDCXu",
8
- "manifest.colors.js": "sha384-u8iD6kapVj4OjeCILxBkYQKgXtDQ7LdEodILkQuknzPMwzSMBmDHN25UuzxepHby",
9
8
  "manifest.components.js": "sha384-3dCTD5EwCZTiX+1obYtDNM3WWwPh2JDQUQQsdRUUK3gs6FXjse1ShkKaT/2jsNaI",
10
9
  "manifest.data.js": "sha384-+wfMPBlMsmLJ7EJWGJMTKGAhLaLCyOVX+Nq+ps3Lly58QC9Dp3XRHK5yangII0yq",
11
10
  "manifest.dropdowns.js": "sha384-WMrFoSpKfJuo81dyrwhVrDO8rq+rDwh2x8x4nH01BY5ZHkvjE+/SaT2gWCI0zOn+",
12
11
  "manifest.icons.js": "sha384-uOkboYrovjCpl22eey3Jaxpey+pOnot5NDnRRumcRxiR7IOVaRh1i20gYnWXR5dW",
13
12
  "manifest.localization.js": "sha384-eKdBIMEAwsugPP2p2fuPzQUkU44f1+Y0JgukMJ1KXLQY1/AYvpcGsEiritVDElsN",
14
- "manifest.markdown.js": "sha384-5dpYLup1j/JLmoVvE1Qn47rwYwtU6kH2PvuiNzU1nqa0LMRWZhdnKhKO7I3I0cyI",
13
+ "manifest.markdown.js": "sha384-gxRxl0QFZc66KfpRNG5bj1mHGD61lLg4+zN77IWclVw6nLW+SIw1ciNqQa187+fK",
15
14
  "manifest.resize.js": "sha384-Ak5gf44ERfh9pOSAD1qZzJSysslpwBCkevIlz7R3dszTUyzUKGKGF4pn5arOtgG0",
16
15
  "manifest.router.js": "sha384-n6xmIfWnYzd/0kkVTFuHhFzHuxiDgZ1Lg1W0yB6/w3Myw5pQ6PgE6SJBHfVsO7/D",
17
16
  "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
18
17
  "manifest.svg.js": "sha384-nc+3spSGNS2l+82maL/OFz2iOGUhLZ0kqeooj28CEcdElM4OZa34e0tbnokZHVI6",
19
18
  "manifest.tabs.js": "sha384-v6Ti0zHfdLhkFHbTMg0FH6uMrThuBvZrL2PQgVBeeXhDjuN7x4MtoNWogPbAQTaD",
20
- "manifest.tailwind.js": "sha384-aHLvl2oSuUgy06VaBqhhByn5wWxqvnqxw6KCwehakKUS00F/s/Nb62umeASS6Y4P",
21
19
  "manifest.toasts.js": "sha384-ytd5rDbax/Ou9z23uedFXPZbxDPsk2E/pxCTq4WLvfv+os1qTI6kELp0kPp07g24",
22
20
  "manifest.tooltips.js": "sha384-Hhip5ZN66xhDw3m0XBrKLKLpcVRz3Z9RszPKqo6xvFF0mrUgQBVZ+mZjZsXgOOjS",
23
21
  "manifest.url.parameters.js": "sha384-FIufiClqDx1rJpU/QUc9z/D43qClQ6Qm8rBahipbJl9BDHUvhrOsUDegmTWW7Tuf",
24
22
  "manifest.utilities.js": "sha384-Q98oZClq/iRKFmuwHolisLgEitsTZiEPHxUW29liKlnL1Gx+YGq8MMivYbDlGDD6",
25
- "manifest.js": "sha384-AjAnd61543ANko2XUK2ZZA7lWQH6DPoV3CSXt/Y4rJnxIOMA+5jx0sgU5U7+sEg6"
23
+ "manifest.js": "sha384-+VqLvjMEa28w5p6vlp4OEI6w/wr9Wpuzgk4rJcWsg1aDodhEB6E30yDpgs/6zRsR"
26
24
  }
package/lib/manifest.js CHANGED
@@ -175,41 +175,6 @@
175
175
  const DEFAULT_VERSION = 'latest';
176
176
  const ALPINE_CDN_URL = 'https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js';
177
177
 
178
- // SRI integrity map: { 'manifest.foo.min.js': 'sha384-...', ... }
179
- // Inlined by build.mjs's emitIntegrityMap() step. Empty in source so the
180
- // unbuilt loader works in dev (where files are served from the local
181
- // project, same-origin, no SRI needed). Built artifact in lib/ carries
182
- // the populated map. Looked up by the filename suffix of every script URL
183
- // addScript() injects — when the file is in the map, the matching
184
- // integrity + crossorigin attributes are set, and the browser refuses to
185
- // execute the script if the bytes don't match (defends against CDN
186
- // poisoning / npm hijack).
187
- const INTEGRITY = {
188
- "manifest.appwrite.auth.js": "sha384-to37ssZJXGeOS6+rf2VI47ox2mEqgsi5oQ1E5vv8XU/lDspbDFE1KHEMm8TxBhxW",
189
- "manifest.appwrite.data.js": "sha384-00ulLT+GAIuPHA/rRT9p98vYlsyDzkyKXtg86BDQ6FGQa5vVVN+W6kuforniBAsz",
190
- "manifest.appwrite.presence.js": "sha384-uxRpx9/Jj0kGtklH5QmUlAzD3zdSvFRfK6bcJQqxl+Bsf5tOo4zgwqJTQgtZoHQP",
191
- "manifest.code.js": "sha384-zjtiVXsPoPK4H/2gN4Xe4Bd0iKUwnv4MbteX9AmTWqNz49E+a3eCuONYHpSneStZ",
192
- "manifest.color.js": "sha384-Z9G/lzt0vVMxjz4wkPuGG1X9mmQAJR15aOoGX3ephf7r2wnlUWet5GLgkUMtT4vt",
193
- "manifest.colorpicker.js": "sha384-0EVn+Ha06h7FIvOxc6WjZYnKYXzi+zba08yKvczSEGTRkWRxyKN2TFrZHI1SDCXu",
194
- "manifest.colors.js": "sha384-u8iD6kapVj4OjeCILxBkYQKgXtDQ7LdEodILkQuknzPMwzSMBmDHN25UuzxepHby",
195
- "manifest.components.js": "sha384-3dCTD5EwCZTiX+1obYtDNM3WWwPh2JDQUQQsdRUUK3gs6FXjse1ShkKaT/2jsNaI",
196
- "manifest.data.js": "sha384-+wfMPBlMsmLJ7EJWGJMTKGAhLaLCyOVX+Nq+ps3Lly58QC9Dp3XRHK5yangII0yq",
197
- "manifest.dropdowns.js": "sha384-WMrFoSpKfJuo81dyrwhVrDO8rq+rDwh2x8x4nH01BY5ZHkvjE+/SaT2gWCI0zOn+",
198
- "manifest.icons.js": "sha384-uOkboYrovjCpl22eey3Jaxpey+pOnot5NDnRRumcRxiR7IOVaRh1i20gYnWXR5dW",
199
- "manifest.localization.js": "sha384-eKdBIMEAwsugPP2p2fuPzQUkU44f1+Y0JgukMJ1KXLQY1/AYvpcGsEiritVDElsN",
200
- "manifest.markdown.js": "sha384-5dpYLup1j/JLmoVvE1Qn47rwYwtU6kH2PvuiNzU1nqa0LMRWZhdnKhKO7I3I0cyI",
201
- "manifest.resize.js": "sha384-Ak5gf44ERfh9pOSAD1qZzJSysslpwBCkevIlz7R3dszTUyzUKGKGF4pn5arOtgG0",
202
- "manifest.router.js": "sha384-n6xmIfWnYzd/0kkVTFuHhFzHuxiDgZ1Lg1W0yB6/w3Myw5pQ6PgE6SJBHfVsO7/D",
203
- "manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
204
- "manifest.svg.js": "sha384-nc+3spSGNS2l+82maL/OFz2iOGUhLZ0kqeooj28CEcdElM4OZa34e0tbnokZHVI6",
205
- "manifest.tabs.js": "sha384-v6Ti0zHfdLhkFHbTMg0FH6uMrThuBvZrL2PQgVBeeXhDjuN7x4MtoNWogPbAQTaD",
206
- "manifest.tailwind.js": "sha384-aHLvl2oSuUgy06VaBqhhByn5wWxqvnqxw6KCwehakKUS00F/s/Nb62umeASS6Y4P",
207
- "manifest.toasts.js": "sha384-ytd5rDbax/Ou9z23uedFXPZbxDPsk2E/pxCTq4WLvfv+os1qTI6kELp0kPp07g24",
208
- "manifest.tooltips.js": "sha384-Hhip5ZN66xhDw3m0XBrKLKLpcVRz3Z9RszPKqo6xvFF0mrUgQBVZ+mZjZsXgOOjS",
209
- "manifest.url.parameters.js": "sha384-FIufiClqDx1rJpU/QUc9z/D43qClQ6Qm8rBahipbJl9BDHUvhrOsUDegmTWW7Tuf",
210
- "manifest.utilities.js": "sha384-Q98oZClq/iRKFmuwHolisLgEitsTZiEPHxUW29liKlnL1Gx+YGq8MMivYbDlGDD6"
211
- };
212
-
213
178
  // Get base URL for a given version
214
179
  function getBaseUrl(version = DEFAULT_VERSION) {
215
180
  return `https://cdn.jsdelivr.net/npm/mnfst@${version}/lib`;
@@ -244,16 +209,10 @@
244
209
  'appwrite-presence'
245
210
  ];
246
211
 
247
- // Plugin dependencies: plugins that require other plugins to be loaded first.
248
- // All Appwrite plugins depend on `data` for env-var interpolation at
249
- // manifest-load time (window.ManifestDataConfig.interpolateManifest); auth
250
- // and presence also share the data plugin's manifest-fetch plumbing.
251
- // Localization reads data-source URLs that may carry ${VAR} references.
212
+ // Plugin dependencies: plugins that require other plugins to be loaded first
252
213
  const PLUGIN_DEPENDENCIES = {
253
- 'appwrite-auth': ['data'],
254
214
  'appwrite-data': ['data'],
255
- 'appwrite-presence': ['data'],
256
- 'localization': ['data']
215
+ 'appwrite-presence': ['data']
257
216
  };
258
217
 
259
218
  // Derive default plugin list from manifest (only load data/localization/components when manifest needs them)
@@ -354,15 +313,6 @@
354
313
  const script = document.createElement('script');
355
314
  script.src = url;
356
315
  script.async = false; // Ensure scripts execute in order
357
- // Apply SRI when the file is in the inlined integrity map. The map
358
- // keys files by basename, so it works whether the URL points at
359
- // jsDelivr or a user-configured pluginBase mirror — as long as the
360
- // served bytes match what this loader version was built against.
361
- const fileName = url.split('/').pop().split('?')[0];
362
- if (INTEGRITY[fileName]) {
363
- script.integrity = INTEGRITY[fileName];
364
- script.crossOrigin = 'anonymous';
365
- }
366
316
  script.onload = () => resolve();
367
317
  script.onerror = () => reject(new Error(`Failed to load ${pluginName} from ${url}`));
368
318
  document.head.appendChild(script);
@@ -570,13 +520,6 @@
570
520
  manifest = await manifestPromise;
571
521
  }
572
522
  if (manifest && typeof window !== 'undefined') {
573
- // Resolve ${VAR} env-var references once, at the canonical load
574
- // point, so downstream plugins (auth, data, appwrite) read
575
- // already-interpolated values instead of each handling env-var
576
- // substitution themselves.
577
- if (window.ManifestDataConfig?.interpolateManifest) {
578
- window.ManifestDataConfig.interpolateManifest(manifest);
579
- }
580
523
  window.__manifestLoaded = manifest;
581
524
  if (window.ManifestComponentsRegistry) {
582
525
  window.ManifestComponentsRegistry.manifest = manifest;
@@ -169,10 +169,14 @@ async function configureMarked(marked) {
169
169
  }
170
170
  if (attrs.from) preAttrs += ` from="${escapeForAttribute(attrs.from)}"`;
171
171
 
172
- // marked has already HTML-escaped `text`. It's safe to drop
173
- // it inside <code> and let the code plugin read .textContent
174
- // as the highlight source.
175
- return `<pre${preAttrs}><code>${text}</code></pre>\n`;
172
+ // Escape the fence body before injection. Newer marked versions
173
+ // pass `token.text` raw, and if we leave it unescaped an HTML
174
+ // fence like ```html <script>…</script>``` becomes a live
175
+ // element in the document instead of source text. Escaping
176
+ // here keeps the <code> body as pure text — the code plugin's
177
+ // resolveSource reads textContent which decodes the entities
178
+ // back to the original source.
179
+ return `<pre${preAttrs}><code>${escapeForText(text)}</code></pre>\n`;
176
180
  }
177
181
  },
178
182
  // Configure marked to allow custom HTML tags
@@ -306,6 +310,56 @@ function enableCheckboxes(html) {
306
310
  return temp.innerHTML;
307
311
  }
308
312
 
313
+ // Apply trailing `{…}` attribute lists to inline `<code>` elements emitted
314
+ // by marked. Authors write ``` `npm i mnfst`{copy} ``` or ``` `code`{bash copy} ```
315
+ // in markdown; marked's default codespan handling drops the trailing brace
316
+ // block as literal text. We rewrite it post-parse so the code plugin's
317
+ // directive sees the attributes and wires copy / syntax highlighting.
318
+ //
319
+ // Supported tokens inside the braces:
320
+ // copy → adds the `copy` attribute (click-to-copy)
321
+ // <language> → bareword like `bash`, `js`, `html` becomes
322
+ // the `x-code="…"` value (drives highlighting)
323
+ // .class → appended to the element's class list
324
+ // key=value, key="quoted" → arbitrary attribute (rarely needed)
325
+ //
326
+ // Multiple tokens separate by whitespace: `cmd`{bash copy} works.
327
+ function applyInlineCodeAttributes(html) {
328
+ // marked emits `<code>…</code>` for codespans (no attributes). When we see
329
+ // `<code>X</code>{tokens}` we rewrite into `<code x-code[=lang] tokens>X</code>`.
330
+ // Be conservative: only rewrite when the brace block immediately follows
331
+ // a `<code>` close tag (no whitespace), so prose like "foo `bar` {note}" is
332
+ // untouched.
333
+ return html.replace(
334
+ /<code>([\s\S]*?)<\/code>\{([^}\n]+)\}/g,
335
+ (_, body, attrString) => {
336
+ const tokens = attrString.trim().split(/\s+/).filter(Boolean);
337
+ let language = '';
338
+ const classes = [];
339
+ const flags = new Set();
340
+ const kv = [];
341
+ for (const tok of tokens) {
342
+ if (tok === 'copy' || tok === 'lines' || tok === 'edit') {
343
+ flags.add(tok);
344
+ } else if (tok.startsWith('.')) {
345
+ classes.push(tok.slice(1));
346
+ } else if (tok.includes('=')) {
347
+ const [k, ...rest] = tok.split('=');
348
+ const v = rest.join('=').replace(/^["']|["']$/g, '');
349
+ kv.push([k, v]);
350
+ } else if (/^[a-z][\w-]*$/i.test(tok) && !language) {
351
+ language = tok;
352
+ }
353
+ }
354
+ let attrs = ` x-code="${escapeForAttribute(language)}"`;
355
+ for (const flag of flags) attrs += ` ${flag}`;
356
+ if (classes.length) attrs += ` class="${escapeForAttribute(classes.join(' '))}"`;
357
+ for (const [k, v] of kv) attrs += ` ${k}="${escapeForAttribute(v)}"`;
358
+ return `<code${attrs}>${body}</code>`;
359
+ }
360
+ );
361
+ }
362
+
309
363
  // Check if highlight.js is available
310
364
  function isHighlightJsAvailable() {
311
365
  return typeof window.hljs !== 'undefined';
@@ -498,6 +552,10 @@ async function initializeMarkdownPlugin() {
498
552
  // Post-process HTML to enable checkboxes (remove disabled attribute)
499
553
  html = enableCheckboxes(html);
500
554
 
555
+ // Promote inline code attribute blocks (`foo`{copy}) to
556
+ // real attributes so the code plugin can wire copy/highlight.
557
+ html = applyInlineCodeAttributes(html);
558
+
501
559
  // Apply opt-in DOMPurify sanitization for x-markdown.safe
502
560
  html = await maybeSanitizeMarkdownHtml(html, safe);
503
561
 
@@ -513,6 +571,18 @@ async function initializeMarkdownPlugin() {
513
571
  element.appendChild(temp.firstChild);
514
572
  }
515
573
 
574
+ // Notify the code plugin to scan the new subtree —
575
+ // fenced blocks and `inline`{copy} elements are added
576
+ // outside Alpine's initial walk and won't otherwise
577
+ // be picked up by the IntersectionObserver.
578
+ if (window.ManifestCode?.observeAll) {
579
+ window.ManifestCode.observeAll(element);
580
+ }
581
+ document.dispatchEvent(new CustomEvent('manifest:code-blocks-converted', {
582
+ bubbles: true,
583
+ detail: { root: element }
584
+ }));
585
+
516
586
  // Show element with content
517
587
  hasContent = true;
518
588
  element.style.opacity = '1';
@@ -674,6 +744,10 @@ async function initializeMarkdownPlugin() {
674
744
  // Post-process HTML to enable checkboxes (remove disabled attribute)
675
745
  html = enableCheckboxes(html);
676
746
 
747
+ // Promote inline code attribute blocks (`foo`{copy}) to
748
+ // real attributes so the code plugin can wire copy/highlight.
749
+ html = applyInlineCodeAttributes(html);
750
+
677
751
  // Apply opt-in DOMPurify sanitization for x-markdown.safe
678
752
  html = await maybeSanitizeMarkdownHtml(html, safe);
679
753