@webmate-studio/builder 0.2.82 → 0.2.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webmate-studio/builder",
3
- "version": "0.2.82",
3
+ "version": "0.2.84",
4
4
  "type": "module",
5
5
  "description": "Webmate Studio Component Builder",
6
6
  "keywords": [
@@ -7,6 +7,7 @@ function generateSemanticColorUtilities(tokens) {
7
7
  if (!tokens.colors) return '';
8
8
 
9
9
  let utilities = '\n/* Color Utilities */';
10
+ utilities += '\n@layer components {';
10
11
 
11
12
  // Map of all colors: token name -> utility class base name
12
13
  const colorMap = {
@@ -120,6 +121,8 @@ function generateSemanticColorUtilities(tokens) {
120
121
  }
121
122
  }
122
123
 
124
+ utilities += '\n}'; // Close @layer components
125
+
123
126
  return utilities;
124
127
  }
125
128
 
@@ -1091,9 +1094,11 @@ export function generateCSSFromTokens(tokens) {
1091
1094
  lines.push('}');
1092
1095
 
1093
1096
  // Generate utility classes for text styles
1097
+ // Wrap in @layer components so Tailwind utilities (@layer utilities) can override them
1094
1098
  if (tokens.textStyles) {
1095
1099
  lines.push('');
1096
1100
  lines.push('/* Text Style Utilities */');
1101
+ lines.push('@layer components {');
1097
1102
  for (const [styleName, style] of Object.entries(tokens.textStyles)) {
1098
1103
  const kebabName = styleName
1099
1104
  .replace(/([A-Z])/g, '-$1')
@@ -1102,35 +1107,36 @@ export function generateCSSFromTokens(tokens) {
1102
1107
  .replace(/^-/, '');
1103
1108
  const className = `text-${kebabName}`;
1104
1109
 
1105
- lines.push(`.${className} {`);
1110
+ lines.push(` .${className} {`);
1106
1111
  if (style.fontFamily) {
1107
1112
  const fontVar = `--font-${style.fontFamily}`;
1108
- lines.push(` font-family: var(${fontVar});`);
1113
+ lines.push(` font-family: var(${fontVar});`);
1109
1114
  }
1110
1115
  if (style.fontWeight) {
1111
- lines.push(` font-weight: ${style.fontWeight};`);
1116
+ lines.push(` font-weight: ${style.fontWeight};`);
1112
1117
  }
1113
1118
  if (style.fontSize) {
1114
1119
  const fontSize = typeof style.fontSize === 'object' ? style.fontSize.base : style.fontSize;
1115
- lines.push(` font-size: ${fontSize};`);
1120
+ lines.push(` font-size: ${fontSize};`);
1116
1121
  }
1117
1122
  if (style.lineHeight) {
1118
1123
  const lineHeight = typeof style.lineHeight === 'object' ? style.lineHeight.base : style.lineHeight;
1119
- lines.push(` line-height: ${lineHeight};`);
1124
+ lines.push(` line-height: ${lineHeight};`);
1120
1125
  }
1121
1126
  if (style.letterSpacing) {
1122
1127
  const letterSpacing = typeof style.letterSpacing === 'object' ? style.letterSpacing.base : style.letterSpacing;
1123
- lines.push(` letter-spacing: ${letterSpacing};`);
1128
+ lines.push(` letter-spacing: ${letterSpacing};`);
1124
1129
  }
1125
1130
  if (style.textTransform) {
1126
- lines.push(` text-transform: ${style.textTransform};`);
1131
+ lines.push(` text-transform: ${style.textTransform};`);
1127
1132
  }
1128
1133
  if (style.textColor) {
1129
1134
  const colorVar = `--color-${style.textColor.replace(/([A-Z])/g, '-$1').toLowerCase()}`;
1130
- lines.push(` color: var(${colorVar});`);
1135
+ lines.push(` color: var(${colorVar});`);
1131
1136
  }
1132
- lines.push('}');
1137
+ lines.push(` }`);
1133
1138
  }
1139
+ lines.push('}'); // Close @layer components
1134
1140
 
1135
1141
  // Add responsive media queries for textStyles
1136
1142
  const breakpointKeys = ['md', 'lg', 'xl', '2xl'];
@@ -1158,24 +1164,26 @@ export function generateCSSFromTokens(tokens) {
1158
1164
  );
1159
1165
 
1160
1166
  if (hasResponsive) {
1161
- mediaQueryLines.push(` .${className} {`);
1167
+ mediaQueryLines.push(` .${className} {`);
1162
1168
  if (style.fontSize && typeof style.fontSize === 'object' && style.fontSize[bp]) {
1163
- mediaQueryLines.push(` font-size: ${style.fontSize[bp]};`);
1169
+ mediaQueryLines.push(` font-size: ${style.fontSize[bp]};`);
1164
1170
  }
1165
1171
  if (style.lineHeight && typeof style.lineHeight === 'object' && style.lineHeight[bp]) {
1166
- mediaQueryLines.push(` line-height: ${style.lineHeight[bp]};`);
1172
+ mediaQueryLines.push(` line-height: ${style.lineHeight[bp]};`);
1167
1173
  }
1168
1174
  if (style.letterSpacing && typeof style.letterSpacing === 'object' && style.letterSpacing[bp]) {
1169
- mediaQueryLines.push(` letter-spacing: ${style.letterSpacing[bp]};`);
1175
+ mediaQueryLines.push(` letter-spacing: ${style.letterSpacing[bp]};`);
1170
1176
  }
1171
- mediaQueryLines.push(` }`);
1177
+ mediaQueryLines.push(` }`);
1172
1178
  }
1173
1179
  }
1174
1180
 
1175
1181
  if (mediaQueryLines.length > 0) {
1176
1182
  lines.push('');
1177
- lines.push(`@media (min-width: ${breakpointValues[bp]}) {`);
1183
+ lines.push(`@layer components {`);
1184
+ lines.push(` @media (min-width: ${breakpointValues[bp]}) {`);
1178
1185
  lines.push(...mediaQueryLines);
1186
+ lines.push(` }`);
1179
1187
  lines.push('}');
1180
1188
  }
1181
1189
  }
package/src/markdown.js CHANGED
@@ -19,12 +19,44 @@ marked.setOptions({
19
19
  mangle: false, // Email-Adressen nicht verschleiern
20
20
  });
21
21
 
22
+ /**
23
+ * Shift heading levels in HTML
24
+ * @param {string} html - HTML string
25
+ * @param {number} startLevel - The level that h1 should become (e.g., 2 means h1 → h2)
26
+ * @returns {string} HTML with shifted heading levels
27
+ */
28
+ function shiftHeadingLevels(html, startLevel) {
29
+ if (!startLevel || startLevel === 1) return html;
30
+
31
+ const offset = startLevel - 1;
32
+
33
+ // Shift headings (h1 → h2, h2 → h3, etc.)
34
+ // We need to do this in reverse order to avoid double-shifting
35
+ for (let level = 6; level >= 1; level--) {
36
+ const newLevel = Math.min(level + offset, 6);
37
+ if (newLevel !== level) {
38
+ // Replace opening and closing tags
39
+ html = html.replace(
40
+ new RegExp(`<h${level}(\\s|>)`, 'gi'),
41
+ `<h${newLevel}$1`
42
+ );
43
+ html = html.replace(
44
+ new RegExp(`</h${level}>`, 'gi'),
45
+ `</h${newLevel}>`
46
+ );
47
+ }
48
+ }
49
+
50
+ return html;
51
+ }
52
+
22
53
  /**
23
54
  * Konvertiert Markdown zu sicherem HTML
24
55
  *
25
56
  * @param {string} markdown - Markdown-String
26
57
  * @param {object} options - Optionale Konfiguration
27
58
  * @param {boolean} options.sanitize - HTML sanitizen (default: true)
59
+ * @param {number} options.headingStartLevel - Start level for headings (1-6, default: 1)
28
60
  * @returns {string} - Sicherer HTML-String
29
61
  */
30
62
  export function markdownToHtml(markdown, options = {}) {
@@ -35,6 +67,11 @@ export function markdownToHtml(markdown, options = {}) {
35
67
  // Markdown zu HTML
36
68
  let html = marked.parse(markdown);
37
69
 
70
+ // Shift heading levels if requested
71
+ if (options.headingStartLevel) {
72
+ html = shiftHeadingLevels(html, options.headingStartLevel);
73
+ }
74
+
38
75
  // XSS-Schutz mit DOMPurify
39
76
  if (options.sanitize !== false) {
40
77
  const config = {
@@ -66,9 +103,10 @@ export function markdownToHtml(markdown, options = {}) {
66
103
  *
67
104
  * @param {Object} props - Component props
68
105
  * @param {Object} propSchema - Prop schema with format info (from component.json)
106
+ * @param {Object} componentMetadata - Component metadata (for headingStartLevel, etc.)
69
107
  * @returns {Object} Props with markdown converted to HTML
70
108
  */
71
- export function processMarkdownProps(props, propSchema = null) {
109
+ export function processMarkdownProps(props, propSchema = null, componentMetadata = null) {
72
110
  if (!props || typeof props !== 'object') return props;
73
111
 
74
112
  const processed = { ...props };
@@ -81,8 +119,15 @@ export function processMarkdownProps(props, propSchema = null) {
81
119
  const isMarkdown = propSchema?.[key]?.format === 'markdown';
82
120
 
83
121
  if (isMarkdown) {
122
+ const options = {};
123
+
124
+ // Apply headingStartLevel from component metadata
125
+ if (componentMetadata?.headingStartLevel) {
126
+ options.headingStartLevel = componentMetadata.headingStartLevel;
127
+ }
128
+
84
129
  // Convert markdown to HTML
85
- processed[key] = markdownToHtml(value);
130
+ processed[key] = markdownToHtml(value, options);
86
131
  }
87
132
  }
88
133