@skhema/embed 0.1.9 → 0.1.11
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/README.md +55 -0
- package/dist/components/SkhemaComponent.d.ts +0 -1
- package/dist/components/SkhemaComponent.d.ts.map +1 -1
- package/dist/components/SkhemaElement.d.ts.map +1 -1
- package/dist/components/types.d.ts +1 -1
- package/dist/components/types.d.ts.map +1 -1
- package/dist/embed.min.js +1 -1
- package/dist/embed.min.js.map +1 -1
- package/dist/index-B8ZF3snD.cjs +215 -0
- package/dist/index-B8ZF3snD.cjs.map +1 -0
- package/dist/index-nkGvTyQR.js +216 -0
- package/dist/index-nkGvTyQR.js.map +1 -0
- package/dist/index.cjs +113 -474
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +88 -449
- package/dist/index.es.js.map +1 -1
- package/dist/method-schema.d.ts +244 -0
- package/dist/method-schema.d.ts.map +1 -0
- package/dist/render/index.d.ts +55 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render.cjs +7 -0
- package/dist/render.cjs.map +1 -0
- package/dist/render.d.ts +2 -0
- package/dist/render.es.js +7 -0
- package/dist/render.es.js.map +1 -0
- package/dist/styles/design-tokens.d.ts +32 -4
- package/dist/styles/design-tokens.d.ts.map +1 -1
- package/dist/tokens-BQf5zJ3j.js +333 -0
- package/dist/tokens-BQf5zJ3j.js.map +1 -0
- package/dist/tokens-BxpYMhDq.cjs +332 -0
- package/dist/tokens-BxpYMhDq.cjs.map +1 -0
- package/dist/tokens.cjs +13 -13
- package/dist/tokens.d.ts +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.es.js +8 -8
- package/dist/utils/color.d.ts +25 -0
- package/dist/utils/color.d.ts.map +1 -0
- package/dist/utils/component-validation.d.ts +1 -1
- package/dist/utils/component-validation.d.ts.map +1 -1
- package/dist/utils/sanitization.d.ts +7 -1
- package/dist/utils/sanitization.d.ts.map +1 -1
- package/dist/utils/seo.d.ts +1 -1
- package/dist/utils/seo.d.ts.map +1 -1
- package/dist/utils/validation.d.ts +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/package.json +11 -7
- package/dist/utils/author-attribution.d.ts +0 -16
- package/dist/utils/author-attribution.d.ts.map +0 -1
- package/dist/validation-CNWaQfh_.cjs +0 -231
- package/dist/validation-CNWaQfh_.cjs.map +0 -1
- package/dist/validation-MSiRF5Wt.js +0 -232
- package/dist/validation-MSiRF5Wt.js.map +0 -1
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const types = require("@skhema/types");
|
|
3
|
-
const COMPONENT_COLORS = {
|
|
4
|
-
diagnosis: {
|
|
5
|
-
bg: "oklch(0.65 0.06 25 / 0.15)",
|
|
6
|
-
text: "oklch(0.65 0.06 25)",
|
|
7
|
-
border: "oklch(0.65 0.06 25 / 0.3)"
|
|
8
|
-
},
|
|
9
|
-
method: {
|
|
10
|
-
bg: "oklch(0.6 0.06 250 / 0.15)",
|
|
11
|
-
text: "oklch(0.6 0.06 250)",
|
|
12
|
-
border: "oklch(0.6 0.06 250 / 0.3)"
|
|
13
|
-
},
|
|
14
|
-
initiatives: {
|
|
15
|
-
bg: "oklch(0.6 0.06 155 / 0.15)",
|
|
16
|
-
text: "oklch(0.6 0.06 155)",
|
|
17
|
-
border: "oklch(0.6 0.06 155 / 0.3)"
|
|
18
|
-
},
|
|
19
|
-
measures: {
|
|
20
|
-
bg: "oklch(0.6 0.06 300 / 0.15)",
|
|
21
|
-
text: "oklch(0.6 0.06 300)",
|
|
22
|
-
border: "oklch(0.6 0.06 300 / 0.3)"
|
|
23
|
-
},
|
|
24
|
-
support: {
|
|
25
|
-
bg: "oklch(0.65 0.06 65 / 0.15)",
|
|
26
|
-
text: "oklch(0.65 0.06 65)",
|
|
27
|
-
border: "oklch(0.65 0.06 65 / 0.3)"
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
const CARD_VARS = {
|
|
31
|
-
light: {
|
|
32
|
-
"--skhema-bg": "hsl(0 0% 100%)",
|
|
33
|
-
"--skhema-card": "hsl(0 0% 100%)",
|
|
34
|
-
"--skhema-border": "hsl(214.3 31.8% 91.4%)",
|
|
35
|
-
"--skhema-text": "hsl(222.2 84% 4.9%)",
|
|
36
|
-
"--skhema-text-muted": "hsl(215.4 16.3% 46.9%)",
|
|
37
|
-
"--skhema-accent": "hsl(210 40% 96%)",
|
|
38
|
-
"--skhema-surface-subtle": "hsl(210 40% 97%)"
|
|
39
|
-
},
|
|
40
|
-
dark: {
|
|
41
|
-
"--skhema-bg": "hsl(222.2 84% 4.9%)",
|
|
42
|
-
"--skhema-card": "hsl(222.2 84% 4.9%)",
|
|
43
|
-
"--skhema-border": "hsl(217.2 32.6% 17.5%)",
|
|
44
|
-
"--skhema-text": "hsl(210 40% 98%)",
|
|
45
|
-
"--skhema-text-muted": "hsl(215 20.2% 65.1%)",
|
|
46
|
-
"--skhema-accent": "hsl(217.2 32.6% 17.5%)",
|
|
47
|
-
"--skhema-surface-subtle": "hsl(217.2 32.6% 12%)"
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
const SHARED_CARD_STYLES = `
|
|
51
|
-
/* Monospace acronym badge */
|
|
52
|
-
.skhema-acronym-badge {
|
|
53
|
-
display: inline-flex;
|
|
54
|
-
align-items: center;
|
|
55
|
-
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
56
|
-
font-size: 10px;
|
|
57
|
-
font-weight: 600;
|
|
58
|
-
padding: 2px 6px;
|
|
59
|
-
border-radius: 2px;
|
|
60
|
-
letter-spacing: 0.02em;
|
|
61
|
-
flex-shrink: 0;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/* Footer attribution tagline */
|
|
65
|
-
.skhema-footer-attribution {
|
|
66
|
-
font-size: 11px;
|
|
67
|
-
color: var(--skhema-text-muted);
|
|
68
|
-
line-height: 1.4;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.skhema-footer-attribution a {
|
|
72
|
-
color: var(--skhema-text-muted);
|
|
73
|
-
text-decoration: underline;
|
|
74
|
-
text-decoration-color: var(--skhema-border);
|
|
75
|
-
text-underline-offset: 2px;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.skhema-footer-attribution a:hover {
|
|
79
|
-
color: var(--skhema-text);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* Contributor line in footer */
|
|
83
|
-
.skhema-contributor-line {
|
|
84
|
-
display: flex;
|
|
85
|
-
align-items: center;
|
|
86
|
-
gap: 6px;
|
|
87
|
-
font-size: 12px;
|
|
88
|
-
color: var(--skhema-text-muted);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.skhema-contributor-line svg {
|
|
92
|
-
width: 14px;
|
|
93
|
-
height: 14px;
|
|
94
|
-
flex-shrink: 0;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.skhema-author-link {
|
|
98
|
-
color: inherit;
|
|
99
|
-
text-decoration: none;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
.skhema-author-link:hover {
|
|
103
|
-
color: var(--skhema-text);
|
|
104
|
-
text-decoration: underline;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/* Save button — primary brand color */
|
|
108
|
-
.skhema-save-btn {
|
|
109
|
-
display: inline-flex;
|
|
110
|
-
align-items: center;
|
|
111
|
-
gap: 6px;
|
|
112
|
-
background: var(--skhema-primary);
|
|
113
|
-
color: white;
|
|
114
|
-
border: none;
|
|
115
|
-
padding: 6px 14px;
|
|
116
|
-
border-radius: calc(var(--skhema-radius) + 2px);
|
|
117
|
-
font-size: 12px;
|
|
118
|
-
font-weight: 500;
|
|
119
|
-
text-decoration: none;
|
|
120
|
-
cursor: pointer;
|
|
121
|
-
transition: background 0.15s ease;
|
|
122
|
-
white-space: nowrap;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.skhema-save-btn:hover {
|
|
126
|
-
background: var(--skhema-primary-hover);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.skhema-save-btn:active {
|
|
130
|
-
background: var(--skhema-primary-pressed);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.skhema-save-btn:focus {
|
|
134
|
-
outline: 2px solid var(--skhema-primary);
|
|
135
|
-
outline-offset: 2px;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
.skhema-save-btn::after {
|
|
139
|
-
content: '\\2192';
|
|
140
|
-
font-size: 13px;
|
|
141
|
-
}
|
|
142
|
-
`;
|
|
143
|
-
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>`;
|
|
144
|
-
function isValidComponentType(componentType) {
|
|
145
|
-
const validTypes = Object.values(types.COMPONENT_TYPES).map((t) => t.value);
|
|
146
|
-
return validTypes.includes(componentType);
|
|
147
|
-
}
|
|
148
|
-
function validateElementBelongsToComponent(elementType, componentType) {
|
|
149
|
-
if (!isValidComponentType(componentType)) return false;
|
|
150
|
-
const validElements = types.SKHEMA_MAPPING.elementFlow[componentType];
|
|
151
|
-
if (!validElements) return false;
|
|
152
|
-
return validElements.some((e) => e.value === elementType);
|
|
153
|
-
}
|
|
154
|
-
function getComponentTypeLabel(componentType) {
|
|
155
|
-
const type = Object.values(types.COMPONENT_TYPES).find(
|
|
156
|
-
(t) => t.value === componentType
|
|
157
|
-
);
|
|
158
|
-
return type?.label || componentType;
|
|
159
|
-
}
|
|
160
|
-
function getComponentTypeAcronym(componentType) {
|
|
161
|
-
const type = Object.values(types.COMPONENT_TYPES).find(
|
|
162
|
-
(t) => t.value === componentType
|
|
163
|
-
);
|
|
164
|
-
return type?.acronym || componentType.substring(0, 2).toUpperCase();
|
|
165
|
-
}
|
|
166
|
-
function resolveComponentType(elementType) {
|
|
167
|
-
for (const [componentValue, elements] of Object.entries(
|
|
168
|
-
types.SKHEMA_MAPPING.elementFlow
|
|
169
|
-
)) {
|
|
170
|
-
if (elements.some((e) => e.value === elementType)) {
|
|
171
|
-
return componentValue;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return "diagnosis";
|
|
175
|
-
}
|
|
176
|
-
function getElementTypesForComponent(componentType) {
|
|
177
|
-
if (!isValidComponentType(componentType)) return [];
|
|
178
|
-
const elements = types.SKHEMA_MAPPING.elementFlow[componentType];
|
|
179
|
-
return elements?.map((e) => e.value) || [];
|
|
180
|
-
}
|
|
181
|
-
function isValidElementType(elementType) {
|
|
182
|
-
const validTypes = Object.values(types.ELEMENT_TYPES).map((type) => type.value);
|
|
183
|
-
return validTypes.includes(elementType);
|
|
184
|
-
}
|
|
185
|
-
function validateAttributes(element) {
|
|
186
|
-
const errors = [];
|
|
187
|
-
const elementType = element.getAttribute("element-type");
|
|
188
|
-
const contributorId = element.getAttribute("contributor-id");
|
|
189
|
-
if (!elementType) {
|
|
190
|
-
errors.push("Missing required attribute: element-type");
|
|
191
|
-
} else if (!isValidElementType(elementType)) {
|
|
192
|
-
const validTypes = Object.values(types.ELEMENT_TYPES).map((t) => t.value).join(", ");
|
|
193
|
-
errors.push(
|
|
194
|
-
`Invalid element-type "${elementType}". Valid types: ${validTypes}`
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
if (!contributorId) {
|
|
198
|
-
errors.push("Missing required attribute: contributor-id");
|
|
199
|
-
} else if (contributorId.trim().length === 0) {
|
|
200
|
-
errors.push("contributor-id cannot be empty");
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
isValid: errors.length === 0,
|
|
204
|
-
errors,
|
|
205
|
-
elementType: isValidElementType(elementType || "") ? elementType : void 0,
|
|
206
|
-
contributorId: contributorId || void 0
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function getElementTypeLabel(elementType) {
|
|
210
|
-
const type = Object.values(types.ELEMENT_TYPES).find((t) => t.value === elementType);
|
|
211
|
-
return type?.label || elementType;
|
|
212
|
-
}
|
|
213
|
-
function getElementTypeAcronym(elementType) {
|
|
214
|
-
const type = Object.values(types.ELEMENT_TYPES).find((t) => t.value === elementType);
|
|
215
|
-
return type?.acronym || elementType.substring(0, 2).toUpperCase();
|
|
216
|
-
}
|
|
217
|
-
exports.CARD_VARS = CARD_VARS;
|
|
218
|
-
exports.COMPONENT_COLORS = COMPONENT_COLORS;
|
|
219
|
-
exports.SHARED_CARD_STYLES = SHARED_CARD_STYLES;
|
|
220
|
-
exports.USER_ICON_SVG = USER_ICON_SVG;
|
|
221
|
-
exports.getComponentTypeAcronym = getComponentTypeAcronym;
|
|
222
|
-
exports.getComponentTypeLabel = getComponentTypeLabel;
|
|
223
|
-
exports.getElementTypeAcronym = getElementTypeAcronym;
|
|
224
|
-
exports.getElementTypeLabel = getElementTypeLabel;
|
|
225
|
-
exports.getElementTypesForComponent = getElementTypesForComponent;
|
|
226
|
-
exports.isValidComponentType = isValidComponentType;
|
|
227
|
-
exports.isValidElementType = isValidElementType;
|
|
228
|
-
exports.resolveComponentType = resolveComponentType;
|
|
229
|
-
exports.validateAttributes = validateAttributes;
|
|
230
|
-
exports.validateElementBelongsToComponent = validateElementBelongsToComponent;
|
|
231
|
-
//# sourceMappingURL=validation-CNWaQfh_.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validation-CNWaQfh_.cjs","sources":["../src/styles/design-tokens.ts","../src/utils/component-validation.ts","../src/utils/validation.ts"],"sourcesContent":["/**\n * Hardcoded color tokens for component-type badge colors.\n * Embeds can't rely on host page CSS vars, so we use inline values.\n * Source: skhema-web/src/app/globals.css oklch tokens converted to HSL-ish for browser compat.\n */\n\nexport const COMPONENT_COLORS = {\n diagnosis: {\n bg: 'oklch(0.65 0.06 25 / 0.15)',\n text: 'oklch(0.65 0.06 25)',\n border: 'oklch(0.65 0.06 25 / 0.3)',\n },\n method: {\n bg: 'oklch(0.6 0.06 250 / 0.15)',\n text: 'oklch(0.6 0.06 250)',\n border: 'oklch(0.6 0.06 250 / 0.3)',\n },\n initiatives: {\n bg: 'oklch(0.6 0.06 155 / 0.15)',\n text: 'oklch(0.6 0.06 155)',\n border: 'oklch(0.6 0.06 155 / 0.3)',\n },\n measures: {\n bg: 'oklch(0.6 0.06 300 / 0.15)',\n text: 'oklch(0.6 0.06 300)',\n border: 'oklch(0.6 0.06 300 / 0.3)',\n },\n support: {\n bg: 'oklch(0.65 0.06 65 / 0.15)',\n text: 'oklch(0.65 0.06 65)',\n border: 'oklch(0.65 0.06 65 / 0.3)',\n },\n} as const\n\nexport type ComponentColorKey = keyof typeof COMPONENT_COLORS\n\n/**\n * Shared base card CSS variables for light and dark modes.\n */\nexport const CARD_VARS = {\n light: {\n '--skhema-bg': 'hsl(0 0% 100%)',\n '--skhema-card': 'hsl(0 0% 100%)',\n '--skhema-border': 'hsl(214.3 31.8% 91.4%)',\n '--skhema-text': 'hsl(222.2 84% 4.9%)',\n '--skhema-text-muted': 'hsl(215.4 16.3% 46.9%)',\n '--skhema-accent': 'hsl(210 40% 96%)',\n '--skhema-surface-subtle': 'hsl(210 40% 97%)',\n },\n dark: {\n '--skhema-bg': 'hsl(222.2 84% 4.9%)',\n '--skhema-card': 'hsl(222.2 84% 4.9%)',\n '--skhema-border': 'hsl(217.2 32.6% 17.5%)',\n '--skhema-text': 'hsl(210 40% 98%)',\n '--skhema-text-muted': 'hsl(215 20.2% 65.1%)',\n '--skhema-accent': 'hsl(217.2 32.6% 17.5%)',\n '--skhema-surface-subtle': 'hsl(217.2 32.6% 12%)',\n },\n} as const\n\n/**\n * Shared card structure styles (used by both SkhemaElement and SkhemaComponent).\n */\nexport const SHARED_CARD_STYLES = `\n/* Monospace acronym badge */\n.skhema-acronym-badge {\n display: inline-flex;\n align-items: center;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n font-size: 10px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 2px;\n letter-spacing: 0.02em;\n flex-shrink: 0;\n}\n\n/* Footer attribution tagline */\n.skhema-footer-attribution {\n font-size: 11px;\n color: var(--skhema-text-muted);\n line-height: 1.4;\n}\n\n.skhema-footer-attribution a {\n color: var(--skhema-text-muted);\n text-decoration: underline;\n text-decoration-color: var(--skhema-border);\n text-underline-offset: 2px;\n}\n\n.skhema-footer-attribution a:hover {\n color: var(--skhema-text);\n}\n\n/* Contributor line in footer */\n.skhema-contributor-line {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--skhema-text-muted);\n}\n\n.skhema-contributor-line svg {\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n}\n\n.skhema-author-link {\n color: inherit;\n text-decoration: none;\n}\n\n.skhema-author-link:hover {\n color: var(--skhema-text);\n text-decoration: underline;\n}\n\n/* Save button — primary brand color */\n.skhema-save-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--skhema-primary);\n color: white;\n border: none;\n padding: 6px 14px;\n border-radius: calc(var(--skhema-radius) + 2px);\n font-size: 12px;\n font-weight: 500;\n text-decoration: none;\n cursor: pointer;\n transition: background 0.15s ease;\n white-space: nowrap;\n}\n\n.skhema-save-btn:hover {\n background: var(--skhema-primary-hover);\n}\n\n.skhema-save-btn:active {\n background: var(--skhema-primary-pressed);\n}\n\n.skhema-save-btn:focus {\n outline: 2px solid var(--skhema-primary);\n outline-offset: 2px;\n}\n\n.skhema-save-btn::after {\n content: '\\\\2192';\n font-size: 13px;\n}\n`\n\n/**\n * User SVG icon (inline, no external deps).\n */\nexport 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>`\n","import type { ComponentValue, ElementValue } from '@skhema/types'\nimport { COMPONENT_TYPES, SKHEMA_MAPPING } from '@skhema/types'\n\n/**\n * Validate that a string is a valid component type.\n */\nexport function isValidComponentType(\n componentType: string\n): componentType is ComponentValue {\n const validTypes = Object.values(COMPONENT_TYPES).map((t) => t.value)\n return validTypes.includes(componentType as ComponentValue)\n}\n\n/**\n * Validate that an element type belongs to the given component type.\n */\nexport function validateElementBelongsToComponent(\n elementType: string,\n componentType: string\n): boolean {\n if (!isValidComponentType(componentType)) return false\n\n const validElements =\n SKHEMA_MAPPING.elementFlow[\n componentType as keyof typeof SKHEMA_MAPPING.elementFlow\n ]\n if (!validElements) return false\n\n return validElements.some((e) => e.value === elementType)\n}\n\n/**\n * Get the human-readable label for a component type.\n */\nexport function getComponentTypeLabel(componentType: string): string {\n const type = Object.values(COMPONENT_TYPES).find(\n (t) => t.value === componentType\n )\n return type?.label || componentType\n}\n\n/**\n * Get the acronym for a component type (e.g., 'BD' for diagnosis).\n */\nexport function getComponentTypeAcronym(componentType: string): string {\n const type = Object.values(COMPONENT_TYPES).find(\n (t) => t.value === componentType\n )\n return type?.acronym || componentType.substring(0, 2).toUpperCase()\n}\n\n/**\n * Resolve which component type an element belongs to.\n * Returns the component value, or 'diagnosis' as fallback.\n */\nexport function resolveComponentType(elementType: string): ComponentValue {\n for (const [componentValue, elements] of Object.entries(\n SKHEMA_MAPPING.elementFlow\n )) {\n if (elements.some((e) => e.value === elementType)) {\n return componentValue as ComponentValue\n }\n }\n return 'diagnosis'\n}\n\n/**\n * Get all valid element types for a given component, ordered per the mapping.\n */\nexport function getElementTypesForComponent(\n componentType: string\n): ElementValue[] {\n if (!isValidComponentType(componentType)) return []\n const elements =\n SKHEMA_MAPPING.elementFlow[\n componentType as keyof typeof SKHEMA_MAPPING.elementFlow\n ]\n return elements?.map((e) => e.value) || []\n}\n","import type { ElementValue } from '@skhema/types'\nimport { ELEMENT_TYPES } from '@skhema/types'\n\nexport function isValidElementType(\n elementType: string\n): elementType is ElementValue {\n const validTypes = Object.values(ELEMENT_TYPES).map((type) => type.value)\n return validTypes.includes(elementType as ElementValue)\n}\n\nexport function validateAttributes(element: HTMLElement): {\n isValid: boolean\n errors: string[]\n elementType?: ElementValue\n contributorId?: string\n} {\n const errors: string[] = []\n\n const elementType = element.getAttribute('element-type')\n const contributorId = element.getAttribute('contributor-id')\n\n if (!elementType) {\n errors.push('Missing required attribute: element-type')\n } else if (!isValidElementType(elementType)) {\n const validTypes = Object.values(ELEMENT_TYPES)\n .map((t) => t.value)\n .join(', ')\n errors.push(\n `Invalid element-type \"${elementType}\". Valid types: ${validTypes}`\n )\n }\n\n if (!contributorId) {\n errors.push('Missing required attribute: contributor-id')\n } else if (contributorId.trim().length === 0) {\n errors.push('contributor-id cannot be empty')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n elementType: isValidElementType(elementType || '')\n ? (elementType as ElementValue)\n : undefined,\n contributorId: contributorId || undefined,\n }\n}\n\nexport function getElementTypeLabel(elementType: ElementValue): string {\n const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType)\n return type?.label || elementType\n}\n\nexport function getElementTypeAcronym(elementType: ElementValue): string {\n const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType)\n return type?.acronym || elementType.substring(0, 2).toUpperCase()\n}\n"],"names":["COMPONENT_TYPES","SKHEMA_MAPPING","ELEMENT_TYPES"],"mappings":"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"}
|
|
@@ -1,232 +0,0 @@
|
|
|
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 CARD_VARS = {
|
|
30
|
-
light: {
|
|
31
|
-
"--skhema-bg": "hsl(0 0% 100%)",
|
|
32
|
-
"--skhema-card": "hsl(0 0% 100%)",
|
|
33
|
-
"--skhema-border": "hsl(214.3 31.8% 91.4%)",
|
|
34
|
-
"--skhema-text": "hsl(222.2 84% 4.9%)",
|
|
35
|
-
"--skhema-text-muted": "hsl(215.4 16.3% 46.9%)",
|
|
36
|
-
"--skhema-accent": "hsl(210 40% 96%)",
|
|
37
|
-
"--skhema-surface-subtle": "hsl(210 40% 97%)"
|
|
38
|
-
},
|
|
39
|
-
dark: {
|
|
40
|
-
"--skhema-bg": "hsl(222.2 84% 4.9%)",
|
|
41
|
-
"--skhema-card": "hsl(222.2 84% 4.9%)",
|
|
42
|
-
"--skhema-border": "hsl(217.2 32.6% 17.5%)",
|
|
43
|
-
"--skhema-text": "hsl(210 40% 98%)",
|
|
44
|
-
"--skhema-text-muted": "hsl(215 20.2% 65.1%)",
|
|
45
|
-
"--skhema-accent": "hsl(217.2 32.6% 17.5%)",
|
|
46
|
-
"--skhema-surface-subtle": "hsl(217.2 32.6% 12%)"
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
const SHARED_CARD_STYLES = `
|
|
50
|
-
/* Monospace acronym badge */
|
|
51
|
-
.skhema-acronym-badge {
|
|
52
|
-
display: inline-flex;
|
|
53
|
-
align-items: center;
|
|
54
|
-
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
|
|
55
|
-
font-size: 10px;
|
|
56
|
-
font-weight: 600;
|
|
57
|
-
padding: 2px 6px;
|
|
58
|
-
border-radius: 2px;
|
|
59
|
-
letter-spacing: 0.02em;
|
|
60
|
-
flex-shrink: 0;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/* Footer attribution tagline */
|
|
64
|
-
.skhema-footer-attribution {
|
|
65
|
-
font-size: 11px;
|
|
66
|
-
color: var(--skhema-text-muted);
|
|
67
|
-
line-height: 1.4;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.skhema-footer-attribution a {
|
|
71
|
-
color: var(--skhema-text-muted);
|
|
72
|
-
text-decoration: underline;
|
|
73
|
-
text-decoration-color: var(--skhema-border);
|
|
74
|
-
text-underline-offset: 2px;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.skhema-footer-attribution a:hover {
|
|
78
|
-
color: var(--skhema-text);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/* Contributor line in footer */
|
|
82
|
-
.skhema-contributor-line {
|
|
83
|
-
display: flex;
|
|
84
|
-
align-items: center;
|
|
85
|
-
gap: 6px;
|
|
86
|
-
font-size: 12px;
|
|
87
|
-
color: var(--skhema-text-muted);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.skhema-contributor-line svg {
|
|
91
|
-
width: 14px;
|
|
92
|
-
height: 14px;
|
|
93
|
-
flex-shrink: 0;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.skhema-author-link {
|
|
97
|
-
color: inherit;
|
|
98
|
-
text-decoration: none;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
.skhema-author-link:hover {
|
|
102
|
-
color: var(--skhema-text);
|
|
103
|
-
text-decoration: underline;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/* Save button — primary brand color */
|
|
107
|
-
.skhema-save-btn {
|
|
108
|
-
display: inline-flex;
|
|
109
|
-
align-items: center;
|
|
110
|
-
gap: 6px;
|
|
111
|
-
background: var(--skhema-primary);
|
|
112
|
-
color: white;
|
|
113
|
-
border: none;
|
|
114
|
-
padding: 6px 14px;
|
|
115
|
-
border-radius: calc(var(--skhema-radius) + 2px);
|
|
116
|
-
font-size: 12px;
|
|
117
|
-
font-weight: 500;
|
|
118
|
-
text-decoration: none;
|
|
119
|
-
cursor: pointer;
|
|
120
|
-
transition: background 0.15s ease;
|
|
121
|
-
white-space: nowrap;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
.skhema-save-btn:hover {
|
|
125
|
-
background: var(--skhema-primary-hover);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.skhema-save-btn:active {
|
|
129
|
-
background: var(--skhema-primary-pressed);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.skhema-save-btn:focus {
|
|
133
|
-
outline: 2px solid var(--skhema-primary);
|
|
134
|
-
outline-offset: 2px;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.skhema-save-btn::after {
|
|
138
|
-
content: '\\2192';
|
|
139
|
-
font-size: 13px;
|
|
140
|
-
}
|
|
141
|
-
`;
|
|
142
|
-
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>`;
|
|
143
|
-
function isValidComponentType(componentType) {
|
|
144
|
-
const validTypes = Object.values(COMPONENT_TYPES).map((t) => t.value);
|
|
145
|
-
return validTypes.includes(componentType);
|
|
146
|
-
}
|
|
147
|
-
function validateElementBelongsToComponent(elementType, componentType) {
|
|
148
|
-
if (!isValidComponentType(componentType)) return false;
|
|
149
|
-
const validElements = SKHEMA_MAPPING.elementFlow[componentType];
|
|
150
|
-
if (!validElements) return false;
|
|
151
|
-
return validElements.some((e) => e.value === elementType);
|
|
152
|
-
}
|
|
153
|
-
function getComponentTypeLabel(componentType) {
|
|
154
|
-
const type = Object.values(COMPONENT_TYPES).find(
|
|
155
|
-
(t) => t.value === componentType
|
|
156
|
-
);
|
|
157
|
-
return type?.label || componentType;
|
|
158
|
-
}
|
|
159
|
-
function getComponentTypeAcronym(componentType) {
|
|
160
|
-
const type = Object.values(COMPONENT_TYPES).find(
|
|
161
|
-
(t) => t.value === componentType
|
|
162
|
-
);
|
|
163
|
-
return type?.acronym || componentType.substring(0, 2).toUpperCase();
|
|
164
|
-
}
|
|
165
|
-
function resolveComponentType(elementType) {
|
|
166
|
-
for (const [componentValue, elements] of Object.entries(
|
|
167
|
-
SKHEMA_MAPPING.elementFlow
|
|
168
|
-
)) {
|
|
169
|
-
if (elements.some((e) => e.value === elementType)) {
|
|
170
|
-
return componentValue;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return "diagnosis";
|
|
174
|
-
}
|
|
175
|
-
function getElementTypesForComponent(componentType) {
|
|
176
|
-
if (!isValidComponentType(componentType)) return [];
|
|
177
|
-
const elements = SKHEMA_MAPPING.elementFlow[componentType];
|
|
178
|
-
return elements?.map((e) => e.value) || [];
|
|
179
|
-
}
|
|
180
|
-
function isValidElementType(elementType) {
|
|
181
|
-
const validTypes = Object.values(ELEMENT_TYPES).map((type) => type.value);
|
|
182
|
-
return validTypes.includes(elementType);
|
|
183
|
-
}
|
|
184
|
-
function validateAttributes(element) {
|
|
185
|
-
const errors = [];
|
|
186
|
-
const elementType = element.getAttribute("element-type");
|
|
187
|
-
const contributorId = element.getAttribute("contributor-id");
|
|
188
|
-
if (!elementType) {
|
|
189
|
-
errors.push("Missing required attribute: element-type");
|
|
190
|
-
} else if (!isValidElementType(elementType)) {
|
|
191
|
-
const validTypes = Object.values(ELEMENT_TYPES).map((t) => t.value).join(", ");
|
|
192
|
-
errors.push(
|
|
193
|
-
`Invalid element-type "${elementType}". Valid types: ${validTypes}`
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
if (!contributorId) {
|
|
197
|
-
errors.push("Missing required attribute: contributor-id");
|
|
198
|
-
} else if (contributorId.trim().length === 0) {
|
|
199
|
-
errors.push("contributor-id cannot be empty");
|
|
200
|
-
}
|
|
201
|
-
return {
|
|
202
|
-
isValid: errors.length === 0,
|
|
203
|
-
errors,
|
|
204
|
-
elementType: isValidElementType(elementType || "") ? elementType : void 0,
|
|
205
|
-
contributorId: contributorId || void 0
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
function getElementTypeLabel(elementType) {
|
|
209
|
-
const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType);
|
|
210
|
-
return type?.label || elementType;
|
|
211
|
-
}
|
|
212
|
-
function getElementTypeAcronym(elementType) {
|
|
213
|
-
const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType);
|
|
214
|
-
return type?.acronym || elementType.substring(0, 2).toUpperCase();
|
|
215
|
-
}
|
|
216
|
-
export {
|
|
217
|
-
CARD_VARS as C,
|
|
218
|
-
SHARED_CARD_STYLES as S,
|
|
219
|
-
USER_ICON_SVG as U,
|
|
220
|
-
COMPONENT_COLORS as a,
|
|
221
|
-
getComponentTypeLabel as b,
|
|
222
|
-
getElementTypeAcronym as c,
|
|
223
|
-
getElementTypeLabel as d,
|
|
224
|
-
getElementTypesForComponent as e,
|
|
225
|
-
isValidElementType as f,
|
|
226
|
-
getComponentTypeAcronym as g,
|
|
227
|
-
validateAttributes as h,
|
|
228
|
-
isValidComponentType as i,
|
|
229
|
-
resolveComponentType as r,
|
|
230
|
-
validateElementBelongsToComponent as v
|
|
231
|
-
};
|
|
232
|
-
//# sourceMappingURL=validation-MSiRF5Wt.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"validation-MSiRF5Wt.js","sources":["../src/styles/design-tokens.ts","../src/utils/component-validation.ts","../src/utils/validation.ts"],"sourcesContent":["/**\n * Hardcoded color tokens for component-type badge colors.\n * Embeds can't rely on host page CSS vars, so we use inline values.\n * Source: skhema-web/src/app/globals.css oklch tokens converted to HSL-ish for browser compat.\n */\n\nexport const COMPONENT_COLORS = {\n diagnosis: {\n bg: 'oklch(0.65 0.06 25 / 0.15)',\n text: 'oklch(0.65 0.06 25)',\n border: 'oklch(0.65 0.06 25 / 0.3)',\n },\n method: {\n bg: 'oklch(0.6 0.06 250 / 0.15)',\n text: 'oklch(0.6 0.06 250)',\n border: 'oklch(0.6 0.06 250 / 0.3)',\n },\n initiatives: {\n bg: 'oklch(0.6 0.06 155 / 0.15)',\n text: 'oklch(0.6 0.06 155)',\n border: 'oklch(0.6 0.06 155 / 0.3)',\n },\n measures: {\n bg: 'oklch(0.6 0.06 300 / 0.15)',\n text: 'oklch(0.6 0.06 300)',\n border: 'oklch(0.6 0.06 300 / 0.3)',\n },\n support: {\n bg: 'oklch(0.65 0.06 65 / 0.15)',\n text: 'oklch(0.65 0.06 65)',\n border: 'oklch(0.65 0.06 65 / 0.3)',\n },\n} as const\n\nexport type ComponentColorKey = keyof typeof COMPONENT_COLORS\n\n/**\n * Shared base card CSS variables for light and dark modes.\n */\nexport const CARD_VARS = {\n light: {\n '--skhema-bg': 'hsl(0 0% 100%)',\n '--skhema-card': 'hsl(0 0% 100%)',\n '--skhema-border': 'hsl(214.3 31.8% 91.4%)',\n '--skhema-text': 'hsl(222.2 84% 4.9%)',\n '--skhema-text-muted': 'hsl(215.4 16.3% 46.9%)',\n '--skhema-accent': 'hsl(210 40% 96%)',\n '--skhema-surface-subtle': 'hsl(210 40% 97%)',\n },\n dark: {\n '--skhema-bg': 'hsl(222.2 84% 4.9%)',\n '--skhema-card': 'hsl(222.2 84% 4.9%)',\n '--skhema-border': 'hsl(217.2 32.6% 17.5%)',\n '--skhema-text': 'hsl(210 40% 98%)',\n '--skhema-text-muted': 'hsl(215 20.2% 65.1%)',\n '--skhema-accent': 'hsl(217.2 32.6% 17.5%)',\n '--skhema-surface-subtle': 'hsl(217.2 32.6% 12%)',\n },\n} as const\n\n/**\n * Shared card structure styles (used by both SkhemaElement and SkhemaComponent).\n */\nexport const SHARED_CARD_STYLES = `\n/* Monospace acronym badge */\n.skhema-acronym-badge {\n display: inline-flex;\n align-items: center;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;\n font-size: 10px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: 2px;\n letter-spacing: 0.02em;\n flex-shrink: 0;\n}\n\n/* Footer attribution tagline */\n.skhema-footer-attribution {\n font-size: 11px;\n color: var(--skhema-text-muted);\n line-height: 1.4;\n}\n\n.skhema-footer-attribution a {\n color: var(--skhema-text-muted);\n text-decoration: underline;\n text-decoration-color: var(--skhema-border);\n text-underline-offset: 2px;\n}\n\n.skhema-footer-attribution a:hover {\n color: var(--skhema-text);\n}\n\n/* Contributor line in footer */\n.skhema-contributor-line {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n color: var(--skhema-text-muted);\n}\n\n.skhema-contributor-line svg {\n width: 14px;\n height: 14px;\n flex-shrink: 0;\n}\n\n.skhema-author-link {\n color: inherit;\n text-decoration: none;\n}\n\n.skhema-author-link:hover {\n color: var(--skhema-text);\n text-decoration: underline;\n}\n\n/* Save button — primary brand color */\n.skhema-save-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n background: var(--skhema-primary);\n color: white;\n border: none;\n padding: 6px 14px;\n border-radius: calc(var(--skhema-radius) + 2px);\n font-size: 12px;\n font-weight: 500;\n text-decoration: none;\n cursor: pointer;\n transition: background 0.15s ease;\n white-space: nowrap;\n}\n\n.skhema-save-btn:hover {\n background: var(--skhema-primary-hover);\n}\n\n.skhema-save-btn:active {\n background: var(--skhema-primary-pressed);\n}\n\n.skhema-save-btn:focus {\n outline: 2px solid var(--skhema-primary);\n outline-offset: 2px;\n}\n\n.skhema-save-btn::after {\n content: '\\\\2192';\n font-size: 13px;\n}\n`\n\n/**\n * User SVG icon (inline, no external deps).\n */\nexport 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>`\n","import type { ComponentValue, ElementValue } from '@skhema/types'\nimport { COMPONENT_TYPES, SKHEMA_MAPPING } from '@skhema/types'\n\n/**\n * Validate that a string is a valid component type.\n */\nexport function isValidComponentType(\n componentType: string\n): componentType is ComponentValue {\n const validTypes = Object.values(COMPONENT_TYPES).map((t) => t.value)\n return validTypes.includes(componentType as ComponentValue)\n}\n\n/**\n * Validate that an element type belongs to the given component type.\n */\nexport function validateElementBelongsToComponent(\n elementType: string,\n componentType: string\n): boolean {\n if (!isValidComponentType(componentType)) return false\n\n const validElements =\n SKHEMA_MAPPING.elementFlow[\n componentType as keyof typeof SKHEMA_MAPPING.elementFlow\n ]\n if (!validElements) return false\n\n return validElements.some((e) => e.value === elementType)\n}\n\n/**\n * Get the human-readable label for a component type.\n */\nexport function getComponentTypeLabel(componentType: string): string {\n const type = Object.values(COMPONENT_TYPES).find(\n (t) => t.value === componentType\n )\n return type?.label || componentType\n}\n\n/**\n * Get the acronym for a component type (e.g., 'BD' for diagnosis).\n */\nexport function getComponentTypeAcronym(componentType: string): string {\n const type = Object.values(COMPONENT_TYPES).find(\n (t) => t.value === componentType\n )\n return type?.acronym || componentType.substring(0, 2).toUpperCase()\n}\n\n/**\n * Resolve which component type an element belongs to.\n * Returns the component value, or 'diagnosis' as fallback.\n */\nexport function resolveComponentType(elementType: string): ComponentValue {\n for (const [componentValue, elements] of Object.entries(\n SKHEMA_MAPPING.elementFlow\n )) {\n if (elements.some((e) => e.value === elementType)) {\n return componentValue as ComponentValue\n }\n }\n return 'diagnosis'\n}\n\n/**\n * Get all valid element types for a given component, ordered per the mapping.\n */\nexport function getElementTypesForComponent(\n componentType: string\n): ElementValue[] {\n if (!isValidComponentType(componentType)) return []\n const elements =\n SKHEMA_MAPPING.elementFlow[\n componentType as keyof typeof SKHEMA_MAPPING.elementFlow\n ]\n return elements?.map((e) => e.value) || []\n}\n","import type { ElementValue } from '@skhema/types'\nimport { ELEMENT_TYPES } from '@skhema/types'\n\nexport function isValidElementType(\n elementType: string\n): elementType is ElementValue {\n const validTypes = Object.values(ELEMENT_TYPES).map((type) => type.value)\n return validTypes.includes(elementType as ElementValue)\n}\n\nexport function validateAttributes(element: HTMLElement): {\n isValid: boolean\n errors: string[]\n elementType?: ElementValue\n contributorId?: string\n} {\n const errors: string[] = []\n\n const elementType = element.getAttribute('element-type')\n const contributorId = element.getAttribute('contributor-id')\n\n if (!elementType) {\n errors.push('Missing required attribute: element-type')\n } else if (!isValidElementType(elementType)) {\n const validTypes = Object.values(ELEMENT_TYPES)\n .map((t) => t.value)\n .join(', ')\n errors.push(\n `Invalid element-type \"${elementType}\". Valid types: ${validTypes}`\n )\n }\n\n if (!contributorId) {\n errors.push('Missing required attribute: contributor-id')\n } else if (contributorId.trim().length === 0) {\n errors.push('contributor-id cannot be empty')\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n elementType: isValidElementType(elementType || '')\n ? (elementType as ElementValue)\n : undefined,\n contributorId: contributorId || undefined,\n }\n}\n\nexport function getElementTypeLabel(elementType: ElementValue): string {\n const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType)\n return type?.label || elementType\n}\n\nexport function getElementTypeAcronym(elementType: ElementValue): string {\n const type = Object.values(ELEMENT_TYPES).find((t) => t.value === elementType)\n return type?.acronym || elementType.substring(0, 2).toUpperCase()\n}\n"],"names":[],"mappings":"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"}
|