mnfst 0.5.213 → 0.5.215
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.
- package/lib/manifest.appwrite.auth.js +6 -90
- package/lib/manifest.appwrite.data.js +1 -1
- package/lib/manifest.avatar.css +10 -5
- package/lib/manifest.button.css +18 -0
- package/lib/manifest.charts.js +1 -1
- package/lib/manifest.colorpicker.js +2 -2
- package/lib/manifest.combobox.js +1 -1
- package/lib/manifest.components.js +32 -11
- package/lib/manifest.css +61 -14
- package/lib/manifest.d.ts +13 -1
- package/lib/manifest.data.js +1 -1
- package/lib/manifest.datepicker.js +1 -1
- package/lib/manifest.export.js +249 -1
- package/lib/manifest.form.css +6 -1
- package/lib/manifest.integrity.json +13 -13
- package/lib/manifest.js +13 -4
- package/lib/manifest.markdown.js +12 -4
- package/lib/manifest.min.css +1 -1
- package/lib/manifest.schema.json +5 -0
- package/lib/manifest.status.js +1 -1
- package/lib/manifest.sw.js +5 -3
- package/lib/manifest.typography.css +21 -7
- package/lib/manifest.utilities.css +6 -1
- package/package.json +1 -1
package/lib/manifest.export.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/* Manifest Export — x-export / $export download of pages, regions, or data (pdf/png/jpeg/webp/csv/json)
|
|
1
|
+
/* Manifest Export — x-export / $export download of pages, regions, or data (pdf/png/jpeg/webp/csv/json),
|
|
2
|
+
and its mirror x-import / $import: pick a local file and parse it into data (json/csv) */
|
|
2
3
|
|
|
3
4
|
(function () {
|
|
4
5
|
|
|
@@ -523,4 +524,251 @@ if (window.Alpine && typeof window.Alpine.directive === 'function') {
|
|
|
523
524
|
}
|
|
524
525
|
|
|
525
526
|
|
|
527
|
+
})();
|
|
528
|
+
|
|
529
|
+
// ----- Import (x-import / $import) ------------------------------------------
|
|
530
|
+
|
|
531
|
+
(function () {
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
function initializeImportPlugin() {
|
|
535
|
+
|
|
536
|
+
Alpine.directive('import', (el, { modifiers, expression }, { evaluate, cleanup }) => {
|
|
537
|
+
|
|
538
|
+
const onClick = async (e) => {
|
|
539
|
+
if (e && typeof e.preventDefault === 'function') e.preventDefault();
|
|
540
|
+
const opts = resolveOptions(expression, modifiers, evaluate);
|
|
541
|
+
try {
|
|
542
|
+
await runImport(el, opts);
|
|
543
|
+
} catch (err) {
|
|
544
|
+
if (!err || !err._mnfstReported) emitError(el, opts.format, err);
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
el.addEventListener('click', onClick);
|
|
548
|
+
cleanup(() => el.removeEventListener('click', onClick));
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
// ----- $import magic — programmatic file pick, resolves parsed data -----
|
|
552
|
+
Alpine.magic('import', (el) => async (opts = {}) => {
|
|
553
|
+
return runImport(el || null, { ...opts });
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// ------- Options + format helpers -----------------------------------
|
|
557
|
+
|
|
558
|
+
function resolveOptions(expression, modifiers, evaluate) {
|
|
559
|
+
let opts = {};
|
|
560
|
+
if (expression && expression.trim()) {
|
|
561
|
+
try {
|
|
562
|
+
const v = evaluate(expression);
|
|
563
|
+
if (v && typeof v === 'object') opts = { ...v };
|
|
564
|
+
else if (typeof v === 'string') opts = isKnownFormat(v.toLowerCase()) ? { format: v.toLowerCase() } : { source: v };
|
|
565
|
+
} catch (err) {
|
|
566
|
+
console.warn('[x-import] could not evaluate options expression:', err.message);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
// Modifier shorthand for format only (.json, .csv).
|
|
570
|
+
if (!opts.format && Array.isArray(modifiers) && modifiers.length) {
|
|
571
|
+
const found = modifiers.find((m) => isKnownFormat(String(m).toLowerCase()));
|
|
572
|
+
if (found) opts.format = String(found).toLowerCase();
|
|
573
|
+
}
|
|
574
|
+
return opts;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function isKnownFormat(f) {
|
|
578
|
+
return f === 'json' || f === 'csv';
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function acceptFor(format) {
|
|
582
|
+
if (format === 'json') return '.json,application/json';
|
|
583
|
+
if (format === 'csv') return '.csv,text/csv,.tsv';
|
|
584
|
+
return '.json,application/json,.csv,text/csv,.tsv';
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function formatOf(file, declared) {
|
|
588
|
+
if (declared && isKnownFormat(declared)) return declared;
|
|
589
|
+
const name = (file && file.name || '').toLowerCase();
|
|
590
|
+
if (name.endsWith('.csv') || name.endsWith('.tsv')) return 'csv';
|
|
591
|
+
return 'json';
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// ------- File picking ------------------------------------------------
|
|
595
|
+
|
|
596
|
+
// Hidden input per pick; resolves the chosen File, or null when the dialog
|
|
597
|
+
// is dismissed (the input 'cancel' event, where supported).
|
|
598
|
+
function pickFile(accept) {
|
|
599
|
+
return new Promise((resolve) => {
|
|
600
|
+
const input = document.createElement('input');
|
|
601
|
+
input.type = 'file';
|
|
602
|
+
if (accept) input.accept = accept;
|
|
603
|
+
input.style.display = 'none';
|
|
604
|
+
const done = (file) => { input.remove(); resolve(file); };
|
|
605
|
+
input.addEventListener('change', () => done(input.files && input.files[0] || null), { once: true });
|
|
606
|
+
input.addEventListener('cancel', () => done(null), { once: true });
|
|
607
|
+
document.body.appendChild(input);
|
|
608
|
+
input.click();
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function readAsText(file) {
|
|
613
|
+
if (typeof file.text === 'function') return file.text();
|
|
614
|
+
return new Promise((resolve, reject) => {
|
|
615
|
+
const reader = new FileReader();
|
|
616
|
+
reader.onload = () => resolve(String(reader.result || ''));
|
|
617
|
+
reader.onerror = () => reject(reader.error || new Error('could not read file'));
|
|
618
|
+
reader.readAsText(file);
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// ------- Parsing -------------------------------------------------------
|
|
623
|
+
|
|
624
|
+
function parseContent(text, format) {
|
|
625
|
+
if (format === 'json') {
|
|
626
|
+
return JSON.parse(text);
|
|
627
|
+
}
|
|
628
|
+
return parseCsv(text);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// RFC 4180-style CSV: quoted fields, embedded commas/newlines/quotes.
|
|
632
|
+
// Header row → array of objects. Delimiter sniffed from the header line.
|
|
633
|
+
function parseCsv(text) {
|
|
634
|
+
const src = String(text).replace(/^/, '');
|
|
635
|
+
if (!src.trim()) return [];
|
|
636
|
+
const delimiter = sniffDelimiter(src);
|
|
637
|
+
const rows = [];
|
|
638
|
+
let row = [];
|
|
639
|
+
let cell = '';
|
|
640
|
+
let inQuotes = false;
|
|
641
|
+
for (let i = 0; i < src.length; i++) {
|
|
642
|
+
const ch = src[i];
|
|
643
|
+
if (inQuotes) {
|
|
644
|
+
if (ch === '"') {
|
|
645
|
+
if (src[i + 1] === '"') { cell += '"'; i++; }
|
|
646
|
+
else inQuotes = false;
|
|
647
|
+
} else cell += ch;
|
|
648
|
+
} else if (ch === '"') {
|
|
649
|
+
inQuotes = true;
|
|
650
|
+
} else if (ch === delimiter) {
|
|
651
|
+
row.push(cell); cell = '';
|
|
652
|
+
} else if (ch === '\n' || ch === '\r') {
|
|
653
|
+
if (ch === '\r' && src[i + 1] === '\n') i++;
|
|
654
|
+
row.push(cell); cell = '';
|
|
655
|
+
rows.push(row); row = [];
|
|
656
|
+
} else {
|
|
657
|
+
cell += ch;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (cell !== '' || row.length) { row.push(cell); rows.push(row); }
|
|
661
|
+
while (rows.length && rows[rows.length - 1].every((c) => c === '')) rows.pop();
|
|
662
|
+
if (!rows.length) return [];
|
|
663
|
+
const headers = rows[0].map((h) => String(h).trim());
|
|
664
|
+
return rows.slice(1).map((cells) => {
|
|
665
|
+
const out = {};
|
|
666
|
+
headers.forEach((h, i) => { if (h) out[h] = coerceCell(cells[i]); });
|
|
667
|
+
return out;
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function sniffDelimiter(src) {
|
|
672
|
+
const header = src.slice(0, src.indexOf('\n') < 0 ? src.length : src.indexOf('\n'));
|
|
673
|
+
let best = ',', bestCount = 0;
|
|
674
|
+
for (const d of [',', ';', '\t']) {
|
|
675
|
+
const count = header.split(d).length - 1;
|
|
676
|
+
if (count > bestCount) { best = d; bestCount = count; }
|
|
677
|
+
}
|
|
678
|
+
return best;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// Undo x-export's CSV serialization: JSON-looking cells parse back to
|
|
682
|
+
// objects/arrays; numbers, booleans and null coerce; everything else stays a string.
|
|
683
|
+
function coerceCell(v) {
|
|
684
|
+
if (v == null) return '';
|
|
685
|
+
const s = String(v);
|
|
686
|
+
const t = s.trim();
|
|
687
|
+
if (t === '') return '';
|
|
688
|
+
if (t === 'true') return true;
|
|
689
|
+
if (t === 'false') return false;
|
|
690
|
+
if (t === 'null') return null;
|
|
691
|
+
if (/^-?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i.test(t)) {
|
|
692
|
+
const n = Number(t);
|
|
693
|
+
if (Number.isFinite(n)) return n;
|
|
694
|
+
}
|
|
695
|
+
if (t[0] === '{' || t[0] === '[') {
|
|
696
|
+
try { return JSON.parse(t); } catch { /* keep string */ }
|
|
697
|
+
}
|
|
698
|
+
return s;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// ------- Delivery ------------------------------------------------------
|
|
702
|
+
|
|
703
|
+
function applyToSource(source, data) {
|
|
704
|
+
const store = window.ManifestDataStore;
|
|
705
|
+
if (!store || typeof store.updateStore !== 'function') {
|
|
706
|
+
throw new Error(`cannot write to source "${source}" — the data plugin is not loaded`);
|
|
707
|
+
}
|
|
708
|
+
store.updateStore(source, data, { loading: false, error: null, ready: true, fresh: true, allowDuringInit: true });
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
async function runImport(el, opts) {
|
|
712
|
+
const accept = opts.accept || acceptFor(opts.format);
|
|
713
|
+
const file = await pickFile(accept);
|
|
714
|
+
if (!file) return null;
|
|
715
|
+
const format = formatOf(file, opts.format);
|
|
716
|
+
let data;
|
|
717
|
+
try {
|
|
718
|
+
data = parseContent(await readAsText(file), format);
|
|
719
|
+
} catch (err) {
|
|
720
|
+
const e = new Error(`could not parse ${file.name} as ${format}: ${err.message}`);
|
|
721
|
+
emitError(el, format, e);
|
|
722
|
+
e._mnfstReported = true;
|
|
723
|
+
throw e;
|
|
724
|
+
}
|
|
725
|
+
if (opts.source) applyToSource(String(opts.source), data);
|
|
726
|
+
emit(el, 'manifest:import', {
|
|
727
|
+
data,
|
|
728
|
+
format,
|
|
729
|
+
source: opts.source || null,
|
|
730
|
+
file: { name: file.name, size: file.size, type: file.type }
|
|
731
|
+
});
|
|
732
|
+
return data;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function emit(el, name, detail) {
|
|
736
|
+
try {
|
|
737
|
+
const event = new CustomEvent(name, { detail, bubbles: true, composed: true });
|
|
738
|
+
(el && el.dispatchEvent ? el : window).dispatchEvent(event);
|
|
739
|
+
} catch { /* ignore */ }
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function emitError(el, format, err) {
|
|
743
|
+
console.error('[x-import] import failed:', err && err.message || err);
|
|
744
|
+
emit(el, 'manifest:import-error', { format: format || null, error: err && err.message || String(err) });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Standard plugin init lifecycle.
|
|
749
|
+
let importPluginInitialized = false;
|
|
750
|
+
function ensureImportPluginInitialized() {
|
|
751
|
+
if (importPluginInitialized) return;
|
|
752
|
+
if (!window.Alpine || typeof window.Alpine.directive !== 'function') return;
|
|
753
|
+
importPluginInitialized = true;
|
|
754
|
+
initializeImportPlugin();
|
|
755
|
+
}
|
|
756
|
+
window.ensureImportPluginInitialized = ensureImportPluginInitialized;
|
|
757
|
+
if (document.readyState === 'loading') {
|
|
758
|
+
document.addEventListener('DOMContentLoaded', ensureImportPluginInitialized);
|
|
759
|
+
}
|
|
760
|
+
document.addEventListener('alpine:init', ensureImportPluginInitialized);
|
|
761
|
+
if (window.Alpine && typeof window.Alpine.directive === 'function') {
|
|
762
|
+
setTimeout(ensureImportPluginInitialized, 0);
|
|
763
|
+
} else {
|
|
764
|
+
const checkAlpine = setInterval(() => {
|
|
765
|
+
if (window.Alpine && typeof window.Alpine.directive === 'function') {
|
|
766
|
+
clearInterval(checkAlpine);
|
|
767
|
+
ensureImportPluginInitialized();
|
|
768
|
+
}
|
|
769
|
+
}, 10);
|
|
770
|
+
setTimeout(() => clearInterval(checkAlpine), 5000);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
|
|
526
774
|
})();
|
package/lib/manifest.form.css
CHANGED
|
@@ -25,7 +25,12 @@
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
& input,
|
|
28
|
-
& .combobox
|
|
28
|
+
& .combobox {
|
|
29
|
+
width: fit-content
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Own rule: a selector list is not forgiving, so a browser that can't
|
|
33
|
+
parse the :has() would drop the two plain selectors above with it. */
|
|
29
34
|
& :where(label, .label):has(input:not([type=checkbox], [type=radio]), select, textarea) {
|
|
30
35
|
width: fit-content
|
|
31
36
|
}
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
{
|
|
2
|
-
"manifest.appwrite.auth.js": "sha384-
|
|
3
|
-
"manifest.appwrite.data.js": "sha384-
|
|
2
|
+
"manifest.appwrite.auth.js": "sha384-X30RFvbwT9qEIaMsMUumnW54ZzcnY5aY9QQeXPG8/0sI6VAbAw3+FZ6iuZb6b4cu",
|
|
3
|
+
"manifest.appwrite.data.js": "sha384-cfOicas38L58E3CLRnXIdHR5FFFBPuzesQHl5HgqXARZq29XMBlqOmITteI35d5a",
|
|
4
4
|
"manifest.appwrite.presences.js": "sha384-JyVDWFT69b7Svq8J8p9tNWe2v9PZNQBY5UyhtOl1vnuHFAX0CRQ3MA9SiKq5wzNh",
|
|
5
5
|
"manifest.bindings.js": "sha384-lPtMf8Ykaic9gNVZKq8Scia1881ZeMAyxer3WCIkUNwwrgzGBWto1OXADUwjvORF",
|
|
6
|
-
"manifest.charts.js": "sha384-
|
|
6
|
+
"manifest.charts.js": "sha384-gEAv/98+29RbhvNkiNWTNyBPbLTFr2Ld15wLu29FLCL57j1cwGChRrL9Pfk7dBtT",
|
|
7
7
|
"manifest.chat.js": "sha384-88DfMfJLoi2oxclVD2iIMDkIGAExMXq5k47mekUredlicLaP3oZY5bwtwqs+TqDF",
|
|
8
8
|
"manifest.code.js": "sha384-aoQ4E26vrhNe/+tL7K0h90c8/0ies7e8oIR6Ay92he2X3dnWZ+OPcn2Dna8fmc+c",
|
|
9
9
|
"manifest.color.js": "sha384-6Rv3LxyTcZNjrhtayQfqRdCx0uSZ4BiEbgEI98I62eTvp8Aw7LBIoNJ0Je1oktwL",
|
|
10
|
-
"manifest.colorpicker.js": "sha384-
|
|
11
|
-
"manifest.combobox.js": "sha384-
|
|
12
|
-
"manifest.components.js": "sha384-
|
|
10
|
+
"manifest.colorpicker.js": "sha384-BhcCe8cGcd0DTJNwzXWWY+FQNRfSFFl6XqfHIFP8yHg/aXFks1NIC6lVx0Sr1VkM",
|
|
11
|
+
"manifest.combobox.js": "sha384-MILTkrgsTzsYUjGo4ZuADqV85Xs4GmfB4xexfe9vbJMIoQTZ42W4zs+DTaVSPEsx",
|
|
12
|
+
"manifest.components.js": "sha384-27RMMNmXRgFLysdvJgZjDxfXvpIGlQUoWzmwaYCwdfNA+78LARTbOt3nKAPfXwvA",
|
|
13
13
|
"manifest.computed.js": "sha384-GYML/e81gQfN+iDbqhPUgny8bINCyL+SrbTofoGk4hlCnLLjv51mCv42JMqq+ku5",
|
|
14
|
-
"manifest.data.js": "sha384-
|
|
15
|
-
"manifest.datepicker.js": "sha384-
|
|
14
|
+
"manifest.data.js": "sha384-rZO5eUSHAFhSWqcGTqgQibrThpKZcTTgvaI+x1HYa2oepLjDGwhbjgNeYCWqZNM+",
|
|
15
|
+
"manifest.datepicker.js": "sha384-XnbeURni2X91ozVzatQcSjYYIS4MoW12dBRJnYAGBr0nCQ624heH5s+xtQSpLZX6",
|
|
16
16
|
"manifest.defer.js": "sha384-wXa60M2OHigi5rBurXQ7Oa8eyr5WTKs7EUn2GOru62KMdfs/LDyNegxCc9MNJFSe",
|
|
17
17
|
"manifest.device.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
|
|
18
18
|
"manifest.dropdowns.js": "sha384-0ilj9AvRph0LacIBJSqwpu1fvPRn5gcWbhIG1SOpaoM/Q8idMbKBLupwM+5O0UaX",
|
|
19
19
|
"manifest.edit.js": "sha384-oLsrH7/UkJ/KEl3UO77hexcf2rYCGgzXZd0fe2EMq3hKRE7aUqLiEnLJhSV7zTaY",
|
|
20
|
-
"manifest.export.js": "sha384-
|
|
20
|
+
"manifest.export.js": "sha384-Rx6XvqBZc4apzP/FwcDr6yIln4/zNa08Q4ZqBZcEqUz1aNZQfl4h0+Ae7mqCQyuG",
|
|
21
21
|
"manifest.icons.js": "sha384-kKOINpX70YzNO3FvkV6umrmO3f0KIu0DNbwB+ej55a7y/lhU7v/myQtXL0bwCTbt",
|
|
22
22
|
"manifest.localization.js": "sha384-EImsIw8Z3l13V5eH9ZJF/yfsOfIvR1oPRNSArANkPji7V8O3IRf6zMwsNiAGj3Hk",
|
|
23
|
-
"manifest.markdown.js": "sha384-
|
|
23
|
+
"manifest.markdown.js": "sha384-fzyRaD7LwPOYcUeJZCMh6aWgk1ChxIVgy3eUTEjSwZ3DvAHrsl5EMvfxlvOKXD5U",
|
|
24
24
|
"manifest.native.js": "sha384-g+txmrEszYtuOvRelTp/c1wSNR2AtllVI9rx5NPPD7DzhVUnMNughonZykv2xlBW",
|
|
25
25
|
"manifest.payments.js": "sha384-8pofk2zhxtapPMpwnFyTelPMrYhShNhDCE722uQh/EYFvZFuPLmSdgunqsarVQjv",
|
|
26
26
|
"manifest.resize.js": "sha384-+0IsyrGP/4gZmNbD6oU8dCgEyhpBsXMm74NIenYeUGuGRWyJDurcG27xE/CA0/aA",
|
|
27
27
|
"manifest.router.js": "sha384-4qmGEkRpgAo9xoQkW6+hHIhowvcyAggKB2J3Wf8tJtXp+L0hufoVAqzfhnqw4cNR",
|
|
28
28
|
"manifest.slides.js": "sha384-3uRTkyK9XPLmnxI2+igZlpi4EyPlU/7IHj5j3BZJJ2KN455vXyk99fiXV3feO/XY",
|
|
29
|
-
"manifest.status.js": "sha384-
|
|
29
|
+
"manifest.status.js": "sha384-MBO345ab7H6Uq1X2dZLt4Ge1Gcra+Z2qYApvlAeIV8CJhDDJrz2FPXpp+Kcp8zcM",
|
|
30
30
|
"manifest.svg.js": "sha384-qr/8H9+2cIhSXNbRWRtIdrDlTOxKlNJqwEefHvCYoiBNW/kKhPTFa109Orv/EBnb",
|
|
31
|
-
"manifest.sw.js": "sha384-
|
|
31
|
+
"manifest.sw.js": "sha384-Kh0gcISFTnkgXmmn2ykDjYsGw7NFDFaorDt5ZYVfg5Xs1Uy8H0ll0E98MMPqdBPy",
|
|
32
32
|
"manifest.tabs.js": "sha384-OIIcyaQnGS0eNYk4zgAFEDboNYect7THvUd9D0V6nqCeDOxQcoiqLrBEmmWVTTet",
|
|
33
33
|
"manifest.tailwind.js": "sha384-r/Jvqb+Piz21jUts10BbVAJoKkShOdJRVFpywyi4HPIi/OfOJq5CtPyWAPbdkMgt",
|
|
34
34
|
"manifest.text.edit.js": "sha384-pfhc4IPKNJokj6ldlgD+KwGLN4UoM21CNeVf2sVq/34X1Un2PUhEuyTg5itmtsXS",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
"manifest.url.parameters.js": "sha384-J2aSVBOPsoqsbafHndrWLHEDpKb4AKJAoQnVmKA4gZh5P/8HHjFWHO/lkusmG84l",
|
|
38
38
|
"manifest.utilities.js": "sha384-pIKcTvk9lrlXZK0OXld6SA6wrmlaFTCmTpLn67nOCE+XhNw4fuGSDX4Z3C2eR6CJ",
|
|
39
39
|
"manifest.virtual.js": "sha384-YwwmAsZNddajnAkLhBWtrK8edHMJOhD7cJkQi3qmm5o1Thg0wbtDU0dsfrCHKTIQ",
|
|
40
|
-
"manifest.js": "sha384-
|
|
40
|
+
"manifest.js": "sha384-OMEyd7TUB5EfAhabW58OgEn6AkZgOuH5IQ85dHZf7ciI0jmXXrsCp2Eh74iVnJ62"
|
|
41
41
|
}
|
package/lib/manifest.js
CHANGED
|
@@ -461,7 +461,7 @@
|
|
|
461
461
|
const resolved = [];
|
|
462
462
|
const added = new Set();
|
|
463
463
|
|
|
464
|
-
const PLUGIN_ALIASES = { native: 'device' }; // renamed 0.5.199;
|
|
464
|
+
const PLUGIN_ALIASES = { native: 'device', import: 'export' }; // native renamed 0.5.199; import ships inside export
|
|
465
465
|
function addPluginWithDeps(plugin) {
|
|
466
466
|
plugin = PLUGIN_ALIASES[plugin] || plugin;
|
|
467
467
|
if (added.has(plugin)) return;
|
|
@@ -540,11 +540,16 @@
|
|
|
540
540
|
{ magic: '$presence', plugin: 'appwrite-presences' },
|
|
541
541
|
...['$share', '$secure', '$links', '$push', '$app', '$haptics', '$biometric', '$camera'].map(magic => ({ magic, plugin: 'device' }))
|
|
542
542
|
];
|
|
543
|
+
// Framework web components — <x-*> tags that are NOT project components.
|
|
544
|
+
const FRAMEWORK_TAG_RE = /^x-(code|code-group)$/;
|
|
543
545
|
function detectUsagePlugins(alreadyLoaded) {
|
|
544
546
|
const wanted = USAGE_PLUGINS.filter(u => !alreadyLoaded.includes(u.plugin));
|
|
545
|
-
|
|
547
|
+
// <x-*> tags load the components plugin even with nothing registered in
|
|
548
|
+
// manifest.json (the loader's components/<name>.html convention).
|
|
549
|
+
const wantComponents = !alreadyLoaded.includes('components');
|
|
550
|
+
if ((wanted.length === 0 && !wantComponents) || typeof document === 'undefined') return [];
|
|
546
551
|
const found = new Set();
|
|
547
|
-
const done = () => found.size === wanted.length;
|
|
552
|
+
const done = () => found.size === wanted.length + (wantComponents ? 1 : 0);
|
|
548
553
|
|
|
549
554
|
for (const s of document.querySelectorAll('script:not([src])')) {
|
|
550
555
|
const t = s.textContent || '';
|
|
@@ -554,6 +559,10 @@
|
|
|
554
559
|
|
|
555
560
|
const scan = (root) => {
|
|
556
561
|
for (const el of root.querySelectorAll('*')) {
|
|
562
|
+
if (wantComponents && !found.has('components')) {
|
|
563
|
+
const tag = el.tagName.toLowerCase();
|
|
564
|
+
if (tag.startsWith('x-') && !FRAMEWORK_TAG_RE.test(tag)) found.add('components');
|
|
565
|
+
}
|
|
557
566
|
for (const attr of el.attributes || []) {
|
|
558
567
|
const v = attr.value;
|
|
559
568
|
if (!v || v.indexOf('$') === -1) continue;
|
|
@@ -690,7 +699,7 @@
|
|
|
690
699
|
// browser engine (lib/manifest.tailwind.js) — but the utilities plugin's
|
|
691
700
|
// uncovered-class watcher lazily loads it this way (see
|
|
692
701
|
// manifest.utilities.static.js setupUncoveredClassWatcher).
|
|
693
|
-
const allPlugins = [...AVAILABLE_PLUGINS, ...APPWRITE_PLUGINS, ...AUTHORING_PLUGINS, 'payments', 'chat', 'device', 'tailwind'];
|
|
702
|
+
const allPlugins = [...AVAILABLE_PLUGINS, ...APPWRITE_PLUGINS, ...AUTHORING_PLUGINS, 'payments', 'chat', 'device', 'native', 'import', 'tailwind'];
|
|
694
703
|
if (!allPlugins.includes(pluginName)) {
|
|
695
704
|
console.warn(`[Manifest Loader] Unknown plugin: ${pluginName}`);
|
|
696
705
|
return Promise.reject(new Error(`Unknown plugin: ${pluginName}`));
|
package/lib/manifest.markdown.js
CHANGED
|
@@ -463,16 +463,17 @@ async function initializeMarkdownPlugin() {
|
|
|
463
463
|
};
|
|
464
464
|
|
|
465
465
|
Alpine.directive('markdown', (el, { expression, modifiers }, { effect, evaluateLater }) => {
|
|
466
|
+
// `.safe` runs parsed HTML through DOMPurify before injection. Default
|
|
467
|
+
// is unsanitized (raw HTML + custom-element extensions); use .safe for
|
|
468
|
+
// untrusted source (Appwrite, API responses, crowdsourced translations).
|
|
469
|
+
const safe = Array.isArray(modifiers) && modifiers.includes('safe');
|
|
470
|
+
|
|
466
471
|
|
|
467
472
|
// Handle null/undefined expressions gracefully
|
|
468
473
|
if (!expression) {
|
|
469
474
|
return;
|
|
470
475
|
}
|
|
471
476
|
|
|
472
|
-
// `.safe` runs parsed HTML through DOMPurify before injection. Default
|
|
473
|
-
// is unsanitized (raw HTML + custom-element extensions); use .safe for
|
|
474
|
-
// untrusted source (Appwrite, API responses, crowdsourced translations).
|
|
475
|
-
const safe = Array.isArray(modifiers) && modifiers.includes('safe');
|
|
476
477
|
|
|
477
478
|
// Prerender idempotency: baked HTML children are build-time authoritative
|
|
478
479
|
// — skip the initial hide-and-re-render, but still register the effect
|
|
@@ -677,6 +678,10 @@ async function initializeMarkdownPlugin() {
|
|
|
677
678
|
resolvedPath = (basePath ? basePath + '/' : '/') + pathOrContent;
|
|
678
679
|
}
|
|
679
680
|
|
|
681
|
+
// Prerender classification signal: the resolved
|
|
682
|
+
// per-locale source, stamped before the fetch so the
|
|
683
|
+
// renderer sees it even if capture races the request.
|
|
684
|
+
try { el.setAttribute('data-mnfst-md-src', resolvedPath); } catch (_) { }
|
|
680
685
|
// Check cache first
|
|
681
686
|
if (markdownCache.has(resolvedPath)) {
|
|
682
687
|
markdownContent = markdownCache.get(resolvedPath);
|
|
@@ -799,6 +804,9 @@ async function initializeMarkdownPlugin() {
|
|
|
799
804
|
existingMarkdownElements.forEach(el => {
|
|
800
805
|
const expression = el.getAttribute('x-markdown');
|
|
801
806
|
|
|
807
|
+
// Modifiers live in the attribute name in this pre-registration path.
|
|
808
|
+
const safe = el.getAttributeNames().some(n => n.startsWith('x-markdown.') && n.split('.').includes('safe'));
|
|
809
|
+
|
|
802
810
|
// Create a temporary Alpine component context for this element
|
|
803
811
|
const tempComponent = Alpine.$data(el) || {};
|
|
804
812
|
|