@skhema/embed 0.1.6 → 0.1.9

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/index.es.js CHANGED
@@ -1,115 +1,39 @@
1
- import { SKHEMA_MAPPING, COMPONENT_TYPES, ELEMENT_TYPES } from "@skhema/types";
2
- const COMPONENT_COLORS = {
3
- diagnosis: {
4
- bg: "oklch(0.65 0.06 25 / 0.15)",
5
- text: "oklch(0.65 0.06 25)",
6
- border: "oklch(0.65 0.06 25 / 0.3)"
7
- },
8
- method: {
9
- bg: "oklch(0.6 0.06 250 / 0.15)",
10
- text: "oklch(0.6 0.06 250)",
11
- border: "oklch(0.6 0.06 250 / 0.3)"
12
- },
13
- initiatives: {
14
- bg: "oklch(0.6 0.06 155 / 0.15)",
15
- text: "oklch(0.6 0.06 155)",
16
- border: "oklch(0.6 0.06 155 / 0.3)"
17
- },
18
- measures: {
19
- bg: "oklch(0.6 0.06 300 / 0.15)",
20
- text: "oklch(0.6 0.06 300)",
21
- border: "oklch(0.6 0.06 300 / 0.3)"
22
- },
23
- support: {
24
- bg: "oklch(0.65 0.06 65 / 0.15)",
25
- text: "oklch(0.65 0.06 65)",
26
- border: "oklch(0.65 0.06 65 / 0.3)"
27
- }
28
- };
29
- const SHARED_CARD_STYLES = `
30
- /* Monospace acronym badge */
31
- .skhema-acronym-badge {
32
- display: inline-flex;
33
- align-items: center;
34
- font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
35
- font-size: 10px;
36
- font-weight: 600;
37
- padding: 2px 6px;
38
- border-radius: 2px;
39
- letter-spacing: 0.02em;
40
- flex-shrink: 0;
41
- }
42
-
43
- /* Footer attribution tagline */
44
- .skhema-footer-attribution {
45
- font-size: 11px;
46
- color: var(--skhema-text-muted);
47
- line-height: 1.4;
48
- }
49
-
50
- .skhema-footer-attribution a {
51
- color: var(--skhema-text-muted);
52
- text-decoration: underline;
53
- text-decoration-color: var(--skhema-border);
54
- text-underline-offset: 2px;
55
- }
56
-
57
- .skhema-footer-attribution a:hover {
58
- color: var(--skhema-text);
59
- }
60
-
61
- /* Contributor line in footer */
62
- .skhema-contributor-line {
63
- display: flex;
64
- align-items: center;
65
- gap: 6px;
66
- font-size: 12px;
67
- color: var(--skhema-text-muted);
68
- }
69
-
70
- .skhema-contributor-line svg {
71
- width: 14px;
72
- height: 14px;
73
- flex-shrink: 0;
74
- }
75
-
76
- /* Save button — primary brand color */
77
- .skhema-save-btn {
78
- display: inline-flex;
79
- align-items: center;
80
- gap: 6px;
81
- background: var(--skhema-primary);
82
- color: white;
83
- border: none;
84
- padding: 6px 14px;
85
- border-radius: calc(var(--skhema-radius) + 2px);
86
- font-size: 12px;
87
- font-weight: 500;
88
- text-decoration: none;
89
- cursor: pointer;
90
- transition: background 0.15s ease;
91
- white-space: nowrap;
1
+ import { d as getElementTypeLabel, b as getComponentTypeLabel, i as isValidComponentType, v as validateElementBelongsToComponent, e as getElementTypesForComponent, g as getComponentTypeAcronym, a as COMPONENT_COLORS, S as SHARED_CARD_STYLES, U as USER_ICON_SVG, h as validateAttributes, r as resolveComponentType } from "./validation-MSiRF5Wt.js";
2
+ import { c, f } from "./validation-MSiRF5Wt.js";
3
+ function readAuthorAttribution(element) {
4
+ const authorName = element.getAttribute("author-name") || element.getAttribute("contributor-name");
5
+ if (!authorName?.trim()) {
6
+ return null;
7
+ }
8
+ const authorSlug = element.getAttribute("author-slug")?.trim();
9
+ return {
10
+ authorName: authorName.trim(),
11
+ authorSlug: authorSlug || void 0
12
+ };
92
13
  }
93
-
94
- .skhema-save-btn:hover {
95
- background: var(--skhema-primary-hover);
14
+ function escapeHtml(text) {
15
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
96
16
  }
97
-
98
- .skhema-save-btn:active {
99
- background: var(--skhema-primary-pressed);
17
+ function formatAuthorAttributionHtml(authorName, authorSlug) {
18
+ const label = `By ${escapeHtml(authorName)}`;
19
+ if (authorSlug) {
20
+ return `<a href="https://skhema.com/contributors/${encodeURIComponent(authorSlug)}" class="skhema-author-link" target="_blank" rel="noopener noreferrer">${label}</a>`;
21
+ }
22
+ return `<span class="skhema-author-text">${label}</span>`;
100
23
  }
101
-
102
- .skhema-save-btn:focus {
103
- outline: 2px solid var(--skhema-primary);
104
- outline-offset: 2px;
24
+ function formatContributorIdFallback(contributorId) {
25
+ return contributorId.split(/[_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
105
26
  }
106
-
107
- .skhema-save-btn::after {
108
- content: '\\2192';
109
- font-size: 13px;
27
+ function resolveAuthorFooterHtml(element, contributorId) {
28
+ const attribution = readAuthorAttribution(element);
29
+ if (attribution) {
30
+ return formatAuthorAttributionHtml(
31
+ attribution.authorName,
32
+ attribution.authorSlug
33
+ );
34
+ }
35
+ return `<span class="skhema-author-text">By ${escapeHtml(formatContributorIdFallback(contributorId))}</span>`;
110
36
  }
111
- `;
112
- const USER_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>`;
113
37
  const ANALYTICS_ENDPOINT = "https://analytics.skhema.com/functions/v1/embed-manage";
114
38
  const TRACKING_COOKIE_NAME = "_sk";
115
39
  const TRACKING_EXPIRY_HOURS = 24;
@@ -483,43 +407,6 @@ function shouldTrackAnalytics(element) {
483
407
  const trackAnalytics = element.getAttribute("track-analytics");
484
408
  return trackAnalytics !== "false";
485
409
  }
486
- function isValidComponentType(componentType) {
487
- const validTypes = Object.values(COMPONENT_TYPES).map((t) => t.value);
488
- return validTypes.includes(componentType);
489
- }
490
- function validateElementBelongsToComponent(elementType, componentType) {
491
- if (!isValidComponentType(componentType)) return false;
492
- const validElements = SKHEMA_MAPPING.elementFlow[componentType];
493
- if (!validElements) return false;
494
- return validElements.some((e) => e.value === elementType);
495
- }
496
- function getComponentTypeLabel(componentType) {
497
- const type = Object.values(COMPONENT_TYPES).find(
498
- (t) => t.value === componentType
499
- );
500
- return type?.label || componentType;
501
- }
502
- function getComponentTypeAcronym(componentType) {
503
- const type = Object.values(COMPONENT_TYPES).find(
504
- (t) => t.value === componentType
505
- );
506
- return type?.acronym || componentType.substring(0, 2).toUpperCase();
507
- }
508
- function resolveComponentType(elementType) {
509
- for (const [componentValue, elements] of Object.entries(
510
- SKHEMA_MAPPING.elementFlow
511
- )) {
512
- if (elements.some((e) => e.value === elementType)) {
513
- return componentValue;
514
- }
515
- }
516
- return "diagnosis";
517
- }
518
- function getElementTypesForComponent(componentType) {
519
- if (!isValidComponentType(componentType)) return [];
520
- const elements = SKHEMA_MAPPING.elementFlow[componentType];
521
- return elements?.map((e) => e.value) || [];
522
- }
523
410
  function generateContentHash(content) {
524
411
  let hash = 0;
525
412
  const cleanContent = content.trim().substring(0, 200);
@@ -608,42 +495,6 @@ function validateContentSecurity(content) {
608
495
  issues
609
496
  };
610
497
  }
611
- function isValidElementType(elementType) {
612
- const validTypes = Object.values(ELEMENT_TYPES).map((type) => type.value);
613
- return validTypes.includes(elementType);
614
- }
615
- function validateAttributes(element) {
616
- const errors = [];
617
- const elementType = element.getAttribute("element-type");
618
- const contributorId = element.getAttribute("contributor-id");
619
- if (!elementType) {
620
- errors.push("Missing required attribute: element-type");
621
- } else if (!isValidElementType(elementType)) {
622
- const validTypes = Object.values(ELEMENT_TYPES).map((t) => t.value).join(", ");
623
- errors.push(
624
- `Invalid element-type "${elementType}". Valid types: ${validTypes}`
625
- );
626
- }
627
- if (!contributorId) {
628
- errors.push("Missing required attribute: contributor-id");
629
- } else if (contributorId.trim().length === 0) {
630
- errors.push("contributor-id cannot be empty");
631
- }
632
- return {
633
- isValid: errors.length === 0,
634
- errors,
635
- elementType: isValidElementType(elementType || "") ? elementType : void 0,
636
- contributorId: contributorId || void 0
637
- };
638
- }
639
- function getElementTypeLabel(elementType) {
640
- const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType);
641
- return type?.label || elementType;
642
- }
643
- function getElementTypeAcronym(elementType) {
644
- const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType);
645
- return type?.acronym || elementType.substring(0, 2).toUpperCase();
646
- }
647
498
  function generateStructuredData(content, elementType, contributorId, sourceUrl) {
648
499
  return {
649
500
  "@context": "https://schema.org",
@@ -1029,6 +880,8 @@ class SkhemaComponent extends HTMLElement {
1029
880
  return [
1030
881
  "component-type",
1031
882
  "contributor-id",
883
+ "author-name",
884
+ "author-slug",
1032
885
  "contributor-name",
1033
886
  "title",
1034
887
  "theme",
@@ -1169,8 +1022,7 @@ class SkhemaComponent extends HTMLElement {
1169
1022
  const componentLabel = getComponentTypeLabel(component_type);
1170
1023
  const acronym = getComponentTypeAcronym(component_type);
1171
1024
  const colors = COMPONENT_COLORS[component_type];
1172
- const contributorName = this.getAttribute("contributor-name");
1173
- const displayName = contributorName || this.formatContributorName(contributor_id);
1025
+ const authorFooterHtml = resolveAuthorFooterHtml(this, contributor_id);
1174
1026
  const themeAttribute = this.getAttribute("theme") || "auto";
1175
1027
  const actualTheme = this.getActualTheme(themeAttribute);
1176
1028
  const redirectUrl = generateComponentRedirectUrl(
@@ -1219,7 +1071,7 @@ class SkhemaComponent extends HTMLElement {
1219
1071
  <div class="skhema-footer-row">
1220
1072
  <span class="skhema-contributor-line">
1221
1073
  ${USER_ICON_SVG}
1222
- ${displayName}
1074
+ ${authorFooterHtml}
1223
1075
  </span>
1224
1076
  <a href="${redirectUrl}"
1225
1077
  class="skhema-save-btn"
@@ -1283,9 +1135,6 @@ class SkhemaComponent extends HTMLElement {
1283
1135
  }
1284
1136
  return "light";
1285
1137
  }
1286
- formatContributorName(contributorId) {
1287
- return contributorId.split(/[_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1288
- }
1289
1138
  addPreconnectHints() {
1290
1139
  if (document.querySelector(
1291
1140
  'link[rel="preconnect"][href*="analytics.skhema.com"]'
@@ -1728,6 +1577,8 @@ class SkhemaElement extends HTMLElement {
1728
1577
  return [
1729
1578
  "element-type",
1730
1579
  "contributor-id",
1580
+ "author-name",
1581
+ "author-slug",
1731
1582
  "contributor-name",
1732
1583
  "content",
1733
1584
  "source-url",
@@ -1849,8 +1700,7 @@ class SkhemaElement extends HTMLElement {
1849
1700
  );
1850
1701
  const themeAttribute = this.getAttribute("theme") || "auto";
1851
1702
  const actualTheme = this.getActualTheme(themeAttribute);
1852
- const contributorName = this.getAttribute("contributor-name");
1853
- const displayName = contributorName || this.formatContributorName(contributor_id);
1703
+ const authorFooterHtml = resolveAuthorFooterHtml(this, contributor_id);
1854
1704
  const componentType = resolveComponentType(element_type);
1855
1705
  const acronym = getComponentTypeAcronym(componentType);
1856
1706
  const colors = COMPONENT_COLORS[componentType];
@@ -1878,7 +1728,7 @@ class SkhemaElement extends HTMLElement {
1878
1728
  <div class="skhema-footer-row">
1879
1729
  <span class="skhema-contributor-line">
1880
1730
  ${USER_ICON_SVG}
1881
- ${displayName}
1731
+ ${authorFooterHtml}
1882
1732
  </span>
1883
1733
  <a href="${redirectUrl}"
1884
1734
  class="skhema-save-btn"
@@ -1925,9 +1775,6 @@ class SkhemaElement extends HTMLElement {
1925
1775
  }
1926
1776
  return "light";
1927
1777
  }
1928
- formatContributorName(contributorId) {
1929
- return contributorId.split(/[_-]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1930
- }
1931
1778
  addPreconnectHints() {
1932
1779
  if (document.querySelector(
1933
1780
  'link[rel="preconnect"][href*="analytics.skhema.com"]'
@@ -2134,10 +1981,10 @@ export {
2134
1981
  generateStructuredData,
2135
1982
  getComponentTypeAcronym,
2136
1983
  getComponentTypeLabel,
2137
- getElementTypeAcronym,
1984
+ c as getElementTypeAcronym,
2138
1985
  getElementTypeLabel,
2139
1986
  isValidComponentType,
2140
- isValidElementType,
1987
+ f as isValidElementType,
2141
1988
  registerSkhemaComponent,
2142
1989
  registerSkhemaElement,
2143
1990
  resolveComponentType,