@typecaast/capture 0.0.8 → 0.1.0
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/dist/{chunk-MJRHEKPU.js → chunk-4JQY5NEW.js} +112 -13
- package/dist/chunk-4JQY5NEW.js.map +1 -0
- package/dist/{chunk-2UORYZUZ.js → chunk-TBLY4L5G.js} +21 -4
- package/dist/chunk-TBLY4L5G.js.map +1 -0
- package/dist/{distill-kGI5Nt9q.d.cts → distill-CVvHwBKY.d.cts} +17 -0
- package/dist/{distill-DviDU75P.d.ts → distill-Cs7bOU8G.d.ts} +17 -0
- package/dist/draft.cjs +19 -2
- package/dist/draft.cjs.map +1 -1
- package/dist/draft.d.cts +6 -0
- package/dist/draft.d.ts +6 -0
- package/dist/draft.js +1 -1
- package/dist/import-page.cjs +109 -10
- package/dist/import-page.cjs.map +1 -1
- package/dist/import-page.d.cts +1 -1
- package/dist/import-page.d.ts +1 -1
- package/dist/import-page.js +1 -1
- package/dist/index.cjs +226 -161
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -6
- package/dist/index.d.ts +71 -6
- package/dist/index.js +99 -152
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-2UORYZUZ.js.map +0 -1
- package/dist/chunk-MJRHEKPU.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var createDOMPurify = require('dompurify');
|
|
4
4
|
var zod = require('zod');
|
|
5
|
-
var
|
|
6
|
-
var reactDom = require('react-dom');
|
|
7
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
var skinKit = require('@typecaast/skin-kit');
|
|
8
6
|
|
|
9
7
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
8
|
|
|
@@ -72,6 +70,7 @@ var ALLOWED_ATTR = [
|
|
|
72
70
|
"class",
|
|
73
71
|
"style",
|
|
74
72
|
"role",
|
|
73
|
+
"contenteditable",
|
|
75
74
|
"alt",
|
|
76
75
|
"src",
|
|
77
76
|
"srcset",
|
|
@@ -153,8 +152,11 @@ function sanitizeHtml(html, opts = {}) {
|
|
|
153
152
|
return purify.sanitize(html, {
|
|
154
153
|
ALLOWED_TAGS,
|
|
155
154
|
ALLOWED_ATTR,
|
|
156
|
-
// Keep our slot marker even though data-* is otherwise dropped
|
|
157
|
-
|
|
155
|
+
// Keep our slot marker even though data-* is otherwise dropped, and
|
|
156
|
+
// keep `contenteditable` so the composer-detection heuristic can find
|
|
157
|
+
// div-based composers (Slack/PostHog/etc. fake the textarea with a
|
|
158
|
+
// contenteditable div). The distiller strips it later, after marking.
|
|
159
|
+
ADD_ATTR: ["data-tc-slot", "contenteditable"],
|
|
158
160
|
ALLOW_DATA_ATTR: false,
|
|
159
161
|
ALLOW_ARIA_ATTR: true,
|
|
160
162
|
FORBID_TAGS: [
|
|
@@ -210,7 +212,18 @@ var skinDraftSchema = zod.z.object({
|
|
|
210
212
|
/** Theme the capture was taken under, when known. */
|
|
211
213
|
theme: zod.z.enum(["light", "dark"]).optional(),
|
|
212
214
|
/** Suggested canvas from the captured element's box. */
|
|
213
|
-
canvas: zod.z.object({ width: zod.z.number().int(), height: zod.z.number().int() }).optional()
|
|
215
|
+
canvas: zod.z.object({ width: zod.z.number().int(), height: zod.z.number().int() }).optional(),
|
|
216
|
+
/**
|
|
217
|
+
* Snapshot of the page context the draft was taken from. Used by the
|
|
218
|
+
* slot-template renderer to expose a `--captured-viewport-width` CSS
|
|
219
|
+
* variable so authored CSS can ratio-scale against the original
|
|
220
|
+
* viewport instead of the (much smaller) playback canvas.
|
|
221
|
+
*/
|
|
222
|
+
capturedAt: zod.z.object({
|
|
223
|
+
viewportWidth: zod.z.number().int().optional(),
|
|
224
|
+
viewportHeight: zod.z.number().int().optional(),
|
|
225
|
+
pixelRatio: zod.z.number().optional()
|
|
226
|
+
}).optional()
|
|
214
227
|
}),
|
|
215
228
|
/**
|
|
216
229
|
* Slotted, sanitized HTML per region. Elements carry inline `style`
|
|
@@ -239,7 +252,13 @@ var skinDraftSchema = zod.z.object({
|
|
|
239
252
|
typing: slotReportSchema
|
|
240
253
|
}),
|
|
241
254
|
/** Human-readable warnings (hidden content dropped, slots missing, …). */
|
|
242
|
-
warnings: zod.z.array(zod.z.string())
|
|
255
|
+
warnings: zod.z.array(zod.z.string()),
|
|
256
|
+
/**
|
|
257
|
+
* Stylesheets the matched-CSS capture couldn't read (typically blocked by
|
|
258
|
+
* CORS). Informational — the slot template still renders, just without
|
|
259
|
+
* the rules those sheets defined.
|
|
260
|
+
*/
|
|
261
|
+
cssSkipped: zod.z.array(zod.z.string()).optional()
|
|
243
262
|
});
|
|
244
263
|
function detectionScore(draft) {
|
|
245
264
|
const checks = [
|
|
@@ -337,10 +356,36 @@ var INLINE_PROPS = [
|
|
|
337
356
|
"gap",
|
|
338
357
|
"display",
|
|
339
358
|
"flex-direction",
|
|
359
|
+
"flex-wrap",
|
|
340
360
|
"align-items",
|
|
361
|
+
"align-self",
|
|
341
362
|
"justify-content",
|
|
363
|
+
"justify-self",
|
|
342
364
|
"box-shadow",
|
|
343
|
-
"opacity"
|
|
365
|
+
"opacity",
|
|
366
|
+
// Layout-bearing properties (added 2026-Q2 to fix Tailwind-style captures):
|
|
367
|
+
"width",
|
|
368
|
+
"max-width",
|
|
369
|
+
"min-width",
|
|
370
|
+
"height",
|
|
371
|
+
"max-height",
|
|
372
|
+
"min-height",
|
|
373
|
+
"flex",
|
|
374
|
+
"flex-grow",
|
|
375
|
+
"flex-shrink",
|
|
376
|
+
"flex-basis",
|
|
377
|
+
"position",
|
|
378
|
+
"top",
|
|
379
|
+
"right",
|
|
380
|
+
"bottom",
|
|
381
|
+
"left",
|
|
382
|
+
"z-index",
|
|
383
|
+
"overflow",
|
|
384
|
+
"overflow-x",
|
|
385
|
+
"overflow-y",
|
|
386
|
+
"white-space",
|
|
387
|
+
"overflow-wrap",
|
|
388
|
+
"word-break"
|
|
344
389
|
];
|
|
345
390
|
var HIDDEN_CLASS_RE = /\b(sr-only|visually-hidden|hidden)\b/;
|
|
346
391
|
function getWindow2(win) {
|
|
@@ -363,6 +408,25 @@ function isHidden(el, win) {
|
|
|
363
408
|
}
|
|
364
409
|
return false;
|
|
365
410
|
}
|
|
411
|
+
function pxValue(v) {
|
|
412
|
+
if (!v) return null;
|
|
413
|
+
const m = /^(-?\d+(?:\.\d+)?)px$/.exec(v.trim());
|
|
414
|
+
return m ? Number(m[1]) : null;
|
|
415
|
+
}
|
|
416
|
+
function normaliseDesktopMargins(decls, cs) {
|
|
417
|
+
const left = pxValue(cs.getPropertyValue("margin-left"));
|
|
418
|
+
const right = pxValue(cs.getPropertyValue("margin-right"));
|
|
419
|
+
if (left == null || right == null) return;
|
|
420
|
+
if (left < 24 || right < 24) return;
|
|
421
|
+
const ratio = Math.max(left, right) / Math.min(left, right);
|
|
422
|
+
if (ratio > 1.1) return;
|
|
423
|
+
const top = pxValue(cs.getPropertyValue("margin-top")) ?? 0;
|
|
424
|
+
const bot = pxValue(cs.getPropertyValue("margin-bottom")) ?? 0;
|
|
425
|
+
for (let i = decls.length - 1; i >= 0; i--) {
|
|
426
|
+
if (decls[i]?.startsWith("margin:")) decls.splice(i, 1);
|
|
427
|
+
}
|
|
428
|
+
decls.push(`margin: ${top}px auto ${bot}px`);
|
|
429
|
+
}
|
|
366
430
|
function inlineStyles(orig, clone, win) {
|
|
367
431
|
const cs = win.getComputedStyle?.(orig);
|
|
368
432
|
if (!cs) return;
|
|
@@ -372,6 +436,7 @@ function inlineStyles(orig, clone, win) {
|
|
|
372
436
|
if (v && v !== "none" && v !== "normal" && v.trim() !== "")
|
|
373
437
|
decls.push(`${prop}: ${v}`);
|
|
374
438
|
}
|
|
439
|
+
normaliseDesktopMargins(decls, cs);
|
|
375
440
|
if (decls.length) clone.setAttribute("style", decls.join("; "));
|
|
376
441
|
}
|
|
377
442
|
function pruneAndInline(orig, clone, win, inline, dropped) {
|
|
@@ -437,6 +502,10 @@ var TIME_TEXT_RE = /^\s*(\d{1,2}:\d{2}(\s?[ap]\.?m\.?)?|\d+\s*(m|min|h|hr|d|days
|
|
|
437
502
|
function classOf(el) {
|
|
438
503
|
return el.getAttribute("class") ?? "";
|
|
439
504
|
}
|
|
505
|
+
function stripContenteditable(el) {
|
|
506
|
+
if (el.hasAttribute("contenteditable")) el.removeAttribute("contenteditable");
|
|
507
|
+
for (const child of el.children) stripContenteditable(child);
|
|
508
|
+
}
|
|
440
509
|
function markSlot(el, slot, token) {
|
|
441
510
|
el.setAttribute(SLOT_ATTR, slot);
|
|
442
511
|
el.textContent = token;
|
|
@@ -508,12 +577,51 @@ function slotifyRow(row) {
|
|
|
508
577
|
}
|
|
509
578
|
return { html: row.outerHTML, detected };
|
|
510
579
|
}
|
|
511
|
-
var COMPOSER_RE = /\b(composer|compose|reply|message-?box|message-?input|input-?box|textbox|editor|prompt)\b/i;
|
|
580
|
+
var COMPOSER_RE = /\b(composer|compose|reply|message-?box|message-?input|input-?box|textbox|editor|prompt|chat-?input)\b/i;
|
|
581
|
+
var COMPOSER_ARIA_RE = /\b(compose|reply|message|prompt|input|chat)\b/i;
|
|
582
|
+
function blockAncestor(node, root) {
|
|
583
|
+
let cur = node;
|
|
584
|
+
while (cur.parentElement && cur.parentElement !== root) {
|
|
585
|
+
const parent = cur.parentElement;
|
|
586
|
+
const style = (parent.getAttribute("style") ?? "").toLowerCase();
|
|
587
|
+
if (/display\s*:\s*(flex|grid)/.test(style)) return parent;
|
|
588
|
+
cur = parent;
|
|
589
|
+
}
|
|
590
|
+
return cur;
|
|
591
|
+
}
|
|
512
592
|
function findComposer(root, exclude) {
|
|
513
|
-
const
|
|
593
|
+
const all = [...root.querySelectorAll("*")].filter(
|
|
514
594
|
(e) => !exclude.contains(e) && !e.contains(exclude)
|
|
515
595
|
);
|
|
516
|
-
|
|
596
|
+
const byAria = all.find((e) => {
|
|
597
|
+
const label = e.getAttribute("aria-label");
|
|
598
|
+
return label && COMPOSER_ARIA_RE.test(label);
|
|
599
|
+
});
|
|
600
|
+
if (byAria) return blockAncestor(byAria, root);
|
|
601
|
+
const byRole = all.find((e) => e.getAttribute("role") === "textbox");
|
|
602
|
+
if (byRole) return blockAncestor(byRole, root);
|
|
603
|
+
const byCe = all.find((e) => e.hasAttribute("contenteditable"));
|
|
604
|
+
if (byCe) return blockAncestor(byCe, root);
|
|
605
|
+
const byClass = all.find((e) => COMPOSER_RE.test(classOf(e)));
|
|
606
|
+
if (byClass) return byClass;
|
|
607
|
+
let cursor = exclude;
|
|
608
|
+
while (cursor && cursor !== root) {
|
|
609
|
+
let sib = cursor.nextElementSibling;
|
|
610
|
+
while (sib) {
|
|
611
|
+
const style = (sib.getAttribute("style") ?? "").toLowerCase();
|
|
612
|
+
const isBlock = !/display\s*:\s*inline/.test(style);
|
|
613
|
+
const hasButtonish = sib.querySelector(
|
|
614
|
+
"[role='button'], [contenteditable]"
|
|
615
|
+
);
|
|
616
|
+
const txt = (sib.textContent ?? "").trim();
|
|
617
|
+
if (isBlock && (hasButtonish || txt.length === 0 || txt.length < 200)) {
|
|
618
|
+
return sib;
|
|
619
|
+
}
|
|
620
|
+
sib = sib.nextElementSibling;
|
|
621
|
+
}
|
|
622
|
+
cursor = cursor.parentElement;
|
|
623
|
+
}
|
|
624
|
+
return null;
|
|
517
625
|
}
|
|
518
626
|
function emptyReport() {
|
|
519
627
|
return { found: false, detected: [], confidence: 0 };
|
|
@@ -585,7 +693,7 @@ function distill(root, opts = {}) {
|
|
|
585
693
|
if (composer) {
|
|
586
694
|
const c = composer.cloneNode(true);
|
|
587
695
|
c.setAttribute(SLOT_ATTR, "composer");
|
|
588
|
-
c
|
|
696
|
+
stripContenteditable(c);
|
|
589
697
|
c.textContent = "{{composer}}";
|
|
590
698
|
composerHtml = c.outerHTML;
|
|
591
699
|
detection.composer = {
|
|
@@ -598,6 +706,10 @@ function distill(root, opts = {}) {
|
|
|
598
706
|
"No composer detected \u2014 add one by hand if the skin needs it."
|
|
599
707
|
);
|
|
600
708
|
}
|
|
709
|
+
if (frameHtml)
|
|
710
|
+
frameHtml = frameHtml.replace(/\s+contenteditable="[^"]*"/g, "");
|
|
711
|
+
if (messageHtml)
|
|
712
|
+
messageHtml = messageHtml.replace(/\s+contenteditable="[^"]*"/g, "");
|
|
601
713
|
} else {
|
|
602
714
|
warnings.push(
|
|
603
715
|
"No repeating message row found \u2014 capture a tighter subtree around the thread."
|
|
@@ -615,17 +727,19 @@ function distill(root, opts = {}) {
|
|
|
615
727
|
name: opts.name ?? "Captured skin",
|
|
616
728
|
...opts.sourceUrl ? { sourceUrl: opts.sourceUrl } : {},
|
|
617
729
|
...opts.theme ? { theme: opts.theme } : {},
|
|
618
|
-
...canvas ? { canvas } : {}
|
|
730
|
+
...canvas ? { canvas } : {},
|
|
731
|
+
...opts.capturedAt ? { capturedAt: opts.capturedAt } : {}
|
|
619
732
|
},
|
|
620
733
|
slots: {
|
|
621
734
|
...frameHtml ? { frame: frameHtml } : {},
|
|
622
735
|
...messageHtml ? { message: messageHtml } : {},
|
|
623
736
|
...composerHtml ? { composer: composerHtml } : {}
|
|
624
737
|
},
|
|
625
|
-
css: "",
|
|
738
|
+
css: opts.css ?? "",
|
|
626
739
|
tokens,
|
|
627
740
|
detection,
|
|
628
|
-
warnings
|
|
741
|
+
warnings,
|
|
742
|
+
...opts.cssSkipped?.length ? { cssSkipped: opts.cssSkipped } : {}
|
|
629
743
|
};
|
|
630
744
|
}
|
|
631
745
|
function pathTo(root, target) {
|
|
@@ -646,52 +760,94 @@ function nodeAtPath(root, path) {
|
|
|
646
760
|
}
|
|
647
761
|
return node;
|
|
648
762
|
}
|
|
649
|
-
|
|
650
|
-
|
|
763
|
+
|
|
764
|
+
// src/css-capture.ts
|
|
765
|
+
function subtreeMatches(root, selector) {
|
|
766
|
+
if (selector === ":root" || selector === "html" || selector === "body")
|
|
767
|
+
return false;
|
|
768
|
+
try {
|
|
769
|
+
return root.querySelector(selector) != null;
|
|
770
|
+
} catch {
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
651
773
|
}
|
|
652
|
-
function
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
774
|
+
function walkRules(rules, root, out, size) {
|
|
775
|
+
for (let i = 0; i < rules.length; i++) {
|
|
776
|
+
if (size.bytes >= size.cap) return;
|
|
777
|
+
const rule = rules[i];
|
|
778
|
+
if (rule.type === 1) {
|
|
779
|
+
const r = rule;
|
|
780
|
+
const sel = r.selectorText;
|
|
781
|
+
if (sel && sel.split(",").some((s) => subtreeMatches(root, s.trim()))) {
|
|
782
|
+
const text = r.cssText;
|
|
783
|
+
out.push(text);
|
|
784
|
+
size.bytes += text.length + 1;
|
|
785
|
+
}
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
const grouping = rule;
|
|
789
|
+
if (grouping.cssRules) {
|
|
790
|
+
const inner = [];
|
|
791
|
+
const innerSize = { bytes: 0, cap: size.cap - size.bytes };
|
|
792
|
+
walkRules(grouping.cssRules, root, inner, innerSize);
|
|
793
|
+
if (inner.length > 0) {
|
|
794
|
+
const prelude = preludeFor(rule);
|
|
795
|
+
const wrapped = `${prelude} { ${inner.join(" ")} }`;
|
|
796
|
+
out.push(wrapped);
|
|
797
|
+
size.bytes += wrapped.length + 1;
|
|
658
798
|
}
|
|
659
|
-
|
|
660
|
-
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if (rule.type === 5 || rule.type === 7) {
|
|
802
|
+
const text = rule.cssText;
|
|
803
|
+
out.push(text);
|
|
804
|
+
size.bytes += text.length + 1;
|
|
661
805
|
}
|
|
662
806
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
*{box-sizing:border-box;}
|
|
678
|
-
${css}`;
|
|
679
|
-
}
|
|
680
|
-
function fillInto(host, templateHtml, values) {
|
|
681
|
-
host.innerHTML = templateHtml;
|
|
682
|
-
for (const node of host.querySelectorAll(`[${SLOT_ATTR}]`)) {
|
|
683
|
-
const slot = node.getAttribute(SLOT_ATTR);
|
|
684
|
-
if (slot && slot in values) node.textContent = values[slot] ?? "";
|
|
807
|
+
}
|
|
808
|
+
function preludeFor(rule) {
|
|
809
|
+
const r = rule;
|
|
810
|
+
switch (r.type) {
|
|
811
|
+
case 4:
|
|
812
|
+
return `@media ${r.media?.mediaText ?? "all"}`;
|
|
813
|
+
case 12:
|
|
814
|
+
return `@supports ${r.conditionText ?? "all"}`;
|
|
815
|
+
case 13:
|
|
816
|
+
return `@container ${r.conditionText ?? ""}`;
|
|
817
|
+
case 15:
|
|
818
|
+
return `@layer ${r.name ?? ""}`;
|
|
819
|
+
default:
|
|
820
|
+
return rule.cssText.replace(/\{[\s\S]*$/, "").trim();
|
|
685
821
|
}
|
|
686
822
|
}
|
|
687
|
-
function
|
|
688
|
-
const
|
|
823
|
+
function captureMatchedCss(root, doc, opts = {}) {
|
|
824
|
+
const cap = opts.maxBytes ?? 256 * 1024;
|
|
825
|
+
const out = [];
|
|
826
|
+
const size = { bytes: 0, cap };
|
|
827
|
+
const skipped = [];
|
|
828
|
+
const sheets = doc.styleSheets;
|
|
829
|
+
for (let i = 0; i < sheets.length; i++) {
|
|
830
|
+
if (size.bytes >= cap) break;
|
|
831
|
+
const sheet = sheets[i];
|
|
832
|
+
let rules;
|
|
833
|
+
try {
|
|
834
|
+
rules = sheet.cssRules;
|
|
835
|
+
} catch {
|
|
836
|
+
skipped.push(sheet.href ?? "(inline)");
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (!rules) continue;
|
|
840
|
+
walkRules(rules, root, out, size);
|
|
841
|
+
}
|
|
689
842
|
return {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
843
|
+
css: out.join("\n"),
|
|
844
|
+
skipped,
|
|
845
|
+
truncated: size.bytes >= cap
|
|
693
846
|
};
|
|
694
847
|
}
|
|
848
|
+
function slug(s) {
|
|
849
|
+
return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "captured";
|
|
850
|
+
}
|
|
695
851
|
var DEFAULT_CAPS = {
|
|
696
852
|
events: {},
|
|
697
853
|
content: {},
|
|
@@ -700,119 +856,27 @@ var DEFAULT_CAPS = {
|
|
|
700
856
|
readReceipts: false
|
|
701
857
|
};
|
|
702
858
|
function templateSkinFromDraft(draft, opts = {}) {
|
|
703
|
-
const
|
|
704
|
-
frame: draft.slots.frame ? sanitizeHtml(draft.slots.frame) : void 0,
|
|
705
|
-
message: draft.slots.message ? sanitizeHtml(draft.slots.message) : void 0,
|
|
706
|
-
composer: draft.slots.composer ? sanitizeHtml(draft.slots.composer) : void 0
|
|
707
|
-
};
|
|
708
|
-
const lightTokens = { colors: draft.tokens.colors ?? {} };
|
|
709
|
-
const darkTokens = draft.darkTokens ? { colors: draft.darkTokens.colors ?? {} } : lightTokens;
|
|
710
|
-
const cssByTheme = {
|
|
711
|
-
light: styleText(lightTokens, draft.css ?? ""),
|
|
712
|
-
dark: styleText(darkTokens, draft.css ?? "")
|
|
713
|
-
};
|
|
714
|
-
const supportsThemes = draft.darkTokens ? ["light", "dark"] : [draft.meta.theme ?? "light"];
|
|
715
|
-
const Frame = ({
|
|
716
|
-
theme,
|
|
717
|
-
children
|
|
718
|
-
}) => {
|
|
719
|
-
const hostRef = react.useRef(null);
|
|
720
|
-
const [mount, setMount] = react.useState(null);
|
|
721
|
-
react.useLayoutEffect(() => {
|
|
722
|
-
const host = hostRef.current;
|
|
723
|
-
if (!host) return;
|
|
724
|
-
const shadow = host.shadowRoot ?? host.attachShadow({ mode: "open" });
|
|
725
|
-
shadow.innerHTML = "";
|
|
726
|
-
const style = host.ownerDocument.createElement("style");
|
|
727
|
-
style.textContent = theme === "dark" ? cssByTheme.dark : cssByTheme.light;
|
|
728
|
-
shadow.appendChild(style);
|
|
729
|
-
const wrapper = host.ownerDocument.createElement("div");
|
|
730
|
-
wrapper.style.width = "100%";
|
|
731
|
-
wrapper.style.height = "100%";
|
|
732
|
-
wrapper.innerHTML = safe.frame ?? `<div ${SLOT_ATTR}="messages"></div>`;
|
|
733
|
-
shadow.appendChild(wrapper);
|
|
734
|
-
const slot = wrapper.querySelector(`[${SLOT_ATTR}="messages"]`) ?? wrapper;
|
|
735
|
-
slot.textContent = "";
|
|
736
|
-
setMount(slot);
|
|
737
|
-
}, [theme]);
|
|
738
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: hostRef, style: { width: "100%", height: "100%" }, children: mount ? reactDom.createPortal(children, mount) : null });
|
|
739
|
-
};
|
|
740
|
-
const Message = ({ message, author }) => {
|
|
741
|
-
const ref = react.useRef(null);
|
|
742
|
-
react.useLayoutEffect(() => {
|
|
743
|
-
const el = ref.current;
|
|
744
|
-
if (!el || !safe.message) return;
|
|
745
|
-
fillInto(el, safe.message, {
|
|
746
|
-
author: author.name,
|
|
747
|
-
avatar: initials(author.name),
|
|
748
|
-
body: contentToText(message.content),
|
|
749
|
-
time: fmtTime(message.atMs)
|
|
750
|
-
});
|
|
751
|
-
}, [message, author]);
|
|
752
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, style: revealStyle(message.revealProgress) });
|
|
753
|
-
};
|
|
754
|
-
const SystemMessage = ({ message }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
755
|
-
"div",
|
|
756
|
-
{
|
|
757
|
-
style: { ...revealStyle(message.revealProgress), textAlign: "center" },
|
|
758
|
-
children: contentToText(message.content)
|
|
759
|
-
}
|
|
760
|
-
);
|
|
761
|
-
const TypingIndicator = ({ author }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { opacity: 0.7, fontStyle: "italic" }, children: [
|
|
762
|
-
author.name,
|
|
763
|
-
" is typing\u2026"
|
|
764
|
-
] });
|
|
765
|
-
const Composer = ({ composer }) => {
|
|
766
|
-
const ref = react.useRef(null);
|
|
767
|
-
react.useLayoutEffect(() => {
|
|
768
|
-
const el = ref.current;
|
|
769
|
-
if (!el) return;
|
|
770
|
-
if (safe.composer) {
|
|
771
|
-
fillInto(el, safe.composer, { composer: composer.text });
|
|
772
|
-
} else {
|
|
773
|
-
el.textContent = composer.text;
|
|
774
|
-
}
|
|
775
|
-
}, [composer]);
|
|
776
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { ref });
|
|
777
|
-
};
|
|
778
|
-
const Avatar = ({
|
|
779
|
-
participant,
|
|
780
|
-
size = 36
|
|
781
|
-
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
782
|
-
"div",
|
|
783
|
-
{
|
|
784
|
-
style: {
|
|
785
|
-
width: size,
|
|
786
|
-
height: size,
|
|
787
|
-
borderRadius: "50%",
|
|
788
|
-
display: "grid",
|
|
789
|
-
placeItems: "center",
|
|
790
|
-
background: "var(--color-1, #ccc)",
|
|
791
|
-
fontSize: size * 0.4
|
|
792
|
-
},
|
|
793
|
-
children: initials(participant.name)
|
|
794
|
-
}
|
|
795
|
-
);
|
|
796
|
-
const components = {
|
|
797
|
-
Frame,
|
|
798
|
-
Message,
|
|
799
|
-
SystemMessage,
|
|
800
|
-
TypingIndicator,
|
|
801
|
-
Reaction: () => null,
|
|
802
|
-
Composer,
|
|
803
|
-
Avatar
|
|
804
|
-
};
|
|
805
|
-
return {
|
|
806
|
-
id: opts.id ?? slug(draft.meta.name),
|
|
859
|
+
const safeDraft = {
|
|
807
860
|
meta: {
|
|
808
861
|
name: draft.meta.name,
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
862
|
+
theme: draft.meta.theme,
|
|
863
|
+
canvas: draft.meta.canvas,
|
|
864
|
+
capturedAt: draft.meta.capturedAt
|
|
865
|
+
},
|
|
866
|
+
slots: {
|
|
867
|
+
frame: draft.slots.frame ? sanitizeHtml(draft.slots.frame) : void 0,
|
|
868
|
+
message: draft.slots.message ? sanitizeHtml(draft.slots.message) : void 0,
|
|
869
|
+
composer: draft.slots.composer ? sanitizeHtml(draft.slots.composer) : void 0,
|
|
870
|
+
typing: draft.slots.typing ? sanitizeHtml(draft.slots.typing) : void 0
|
|
812
871
|
},
|
|
813
|
-
|
|
814
|
-
tokens: {
|
|
872
|
+
css: draft.css ?? "",
|
|
873
|
+
tokens: { colors: draft.tokens.colors ?? {} },
|
|
874
|
+
darkTokens: draft.darkTokens ? { colors: draft.darkTokens.colors ?? {} } : void 0
|
|
815
875
|
};
|
|
876
|
+
return skinKit.slotSkinFromDraft(safeDraft, {
|
|
877
|
+
id: opts.id ?? slug(draft.meta.name),
|
|
878
|
+
capabilities: opts.capabilities ?? DEFAULT_CAPS
|
|
879
|
+
});
|
|
816
880
|
}
|
|
817
881
|
|
|
818
882
|
// src/merge.ts
|
|
@@ -830,6 +894,7 @@ function mergeThemeDrafts(light, dark) {
|
|
|
830
894
|
}
|
|
831
895
|
|
|
832
896
|
exports.SLOT_TOKENS = SLOT_TOKENS;
|
|
897
|
+
exports.captureMatchedCss = captureMatchedCss;
|
|
833
898
|
exports.detectionScore = detectionScore;
|
|
834
899
|
exports.distill = distill;
|
|
835
900
|
exports.mergeThemeDrafts = mergeThemeDrafts;
|