@protomarkdown/parser 1.0.1 → 1.0.2
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 +9 -0
- package/dist/HtmlGenerator.d.ts +35 -0
- package/dist/HtmlGenerator.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +266 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +266 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1005,6 +1005,272 @@ ${this.indent()}</div>`;
|
|
|
1005
1005
|
}
|
|
1006
1006
|
}
|
|
1007
1007
|
|
|
1008
|
+
/**
|
|
1009
|
+
* Generates HTML from a Proto Markdown AST
|
|
1010
|
+
* Used for VS Code extension preview rendering
|
|
1011
|
+
*/
|
|
1012
|
+
class HtmlGenerator {
|
|
1013
|
+
/**
|
|
1014
|
+
* Generate HTML from markdown AST
|
|
1015
|
+
*/
|
|
1016
|
+
generate(nodes) {
|
|
1017
|
+
return nodes.map((node) => this.renderNode(node)).join("\n");
|
|
1018
|
+
}
|
|
1019
|
+
renderNode(node) {
|
|
1020
|
+
switch (node.type) {
|
|
1021
|
+
case "header":
|
|
1022
|
+
return this.renderHeader(node);
|
|
1023
|
+
case "text":
|
|
1024
|
+
return this.renderText(node);
|
|
1025
|
+
case "bold":
|
|
1026
|
+
return this.renderBold(node);
|
|
1027
|
+
case "italic":
|
|
1028
|
+
return this.renderItalic(node);
|
|
1029
|
+
case "input":
|
|
1030
|
+
return this.renderInput(node);
|
|
1031
|
+
case "textarea":
|
|
1032
|
+
return this.renderTextarea(node);
|
|
1033
|
+
case "checkbox":
|
|
1034
|
+
return this.renderCheckbox(node);
|
|
1035
|
+
case "radiogroup":
|
|
1036
|
+
return this.renderRadioGroup(node);
|
|
1037
|
+
case "dropdown":
|
|
1038
|
+
return this.renderDropdown(node);
|
|
1039
|
+
case "button":
|
|
1040
|
+
return this.renderButton(node);
|
|
1041
|
+
case "card":
|
|
1042
|
+
return this.renderCard(node);
|
|
1043
|
+
case "container":
|
|
1044
|
+
return this.renderContainer(node);
|
|
1045
|
+
case "grid":
|
|
1046
|
+
return this.renderGrid(node);
|
|
1047
|
+
case "div":
|
|
1048
|
+
return this.renderDiv(node);
|
|
1049
|
+
case "table":
|
|
1050
|
+
return this.renderTable(node);
|
|
1051
|
+
case "image":
|
|
1052
|
+
return this.renderImage(node);
|
|
1053
|
+
case "workflow":
|
|
1054
|
+
return this.renderWorkflow(node);
|
|
1055
|
+
case "screen":
|
|
1056
|
+
return this.renderScreen(node);
|
|
1057
|
+
default:
|
|
1058
|
+
return `<div class="proto-unknown">${JSON.stringify(node)}</div>`;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
renderHeader(node) {
|
|
1062
|
+
const level = node.level || 1;
|
|
1063
|
+
let content;
|
|
1064
|
+
if (node.children && node.children.length > 0) {
|
|
1065
|
+
content = this.renderInlineNodes(node.children);
|
|
1066
|
+
}
|
|
1067
|
+
else {
|
|
1068
|
+
content = this.escapeHtml(node.content || "");
|
|
1069
|
+
}
|
|
1070
|
+
return `<h${level} class="proto-header">${content}</h${level}>`;
|
|
1071
|
+
}
|
|
1072
|
+
renderText(node) {
|
|
1073
|
+
if (node.children && node.children.length > 0) {
|
|
1074
|
+
const content = this.renderInlineNodes(node.children);
|
|
1075
|
+
return `<p class="proto-text">${content}</p>`;
|
|
1076
|
+
}
|
|
1077
|
+
return `<p class="proto-text">${this.escapeHtml(node.content || "")}</p>`;
|
|
1078
|
+
}
|
|
1079
|
+
renderBold(node) {
|
|
1080
|
+
if (node.children && node.children.length > 0) {
|
|
1081
|
+
return `<strong>${this.renderInlineNodes(node.children)}</strong>`;
|
|
1082
|
+
}
|
|
1083
|
+
return `<strong>${this.escapeHtml(node.content || "")}</strong>`;
|
|
1084
|
+
}
|
|
1085
|
+
renderItalic(node) {
|
|
1086
|
+
if (node.children && node.children.length > 0) {
|
|
1087
|
+
return `<em>${this.renderInlineNodes(node.children)}</em>`;
|
|
1088
|
+
}
|
|
1089
|
+
return `<em>${this.escapeHtml(node.content || "")}</em>`;
|
|
1090
|
+
}
|
|
1091
|
+
renderInlineNodes(nodes) {
|
|
1092
|
+
return nodes.map((node) => this.renderInlineNode(node)).join("");
|
|
1093
|
+
}
|
|
1094
|
+
renderInlineNode(node) {
|
|
1095
|
+
switch (node.type) {
|
|
1096
|
+
case "bold":
|
|
1097
|
+
if (node.children && node.children.length > 0) {
|
|
1098
|
+
return `<strong>${this.renderInlineNodes(node.children)}</strong>`;
|
|
1099
|
+
}
|
|
1100
|
+
return `<strong>${this.escapeHtml(node.content || "")}</strong>`;
|
|
1101
|
+
case "italic":
|
|
1102
|
+
if (node.children && node.children.length > 0) {
|
|
1103
|
+
return `<em>${this.renderInlineNodes(node.children)}</em>`;
|
|
1104
|
+
}
|
|
1105
|
+
return `<em>${this.escapeHtml(node.content || "")}</em>`;
|
|
1106
|
+
case "text":
|
|
1107
|
+
if (node.children && node.children.length > 0) {
|
|
1108
|
+
return this.renderInlineNodes(node.children);
|
|
1109
|
+
}
|
|
1110
|
+
return this.escapeHtml(node.content || "");
|
|
1111
|
+
default:
|
|
1112
|
+
return this.escapeHtml(node.content || "");
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
renderInput(node) {
|
|
1116
|
+
const placeholder = node.inputType === "password" ? "••••••••" : "";
|
|
1117
|
+
return `
|
|
1118
|
+
<div class="proto-field">
|
|
1119
|
+
<label class="proto-label">${this.escapeHtml(node.label || "")}</label>
|
|
1120
|
+
<input type="${node.inputType || "text"}" class="proto-input" placeholder="${placeholder}" disabled />
|
|
1121
|
+
</div>`;
|
|
1122
|
+
}
|
|
1123
|
+
renderTextarea(node) {
|
|
1124
|
+
return `
|
|
1125
|
+
<div class="proto-field">
|
|
1126
|
+
<label class="proto-label">${this.escapeHtml(node.label || "")}</label>
|
|
1127
|
+
<textarea class="proto-textarea" disabled></textarea>
|
|
1128
|
+
</div>`;
|
|
1129
|
+
}
|
|
1130
|
+
renderCheckbox(node) {
|
|
1131
|
+
return `
|
|
1132
|
+
<div class="proto-checkbox">
|
|
1133
|
+
<input type="checkbox" class="proto-checkbox-input" disabled />
|
|
1134
|
+
<label class="proto-checkbox-label">${this.escapeHtml(node.label || "")}</label>
|
|
1135
|
+
</div>`;
|
|
1136
|
+
}
|
|
1137
|
+
renderRadioGroup(node) {
|
|
1138
|
+
const options = (node.options || [])
|
|
1139
|
+
.map((opt) => `
|
|
1140
|
+
<div class="proto-radio-option">
|
|
1141
|
+
<input type="radio" class="proto-radio-input" name="${this.escapeHtml(node.label || "")}" disabled />
|
|
1142
|
+
<label class="proto-radio-label">${this.escapeHtml(opt)}</label>
|
|
1143
|
+
</div>`)
|
|
1144
|
+
.join("");
|
|
1145
|
+
return `
|
|
1146
|
+
<div class="proto-radiogroup">
|
|
1147
|
+
<label class="proto-label">${this.escapeHtml(node.label || "")}</label>
|
|
1148
|
+
<div class="proto-radio-options">${options}</div>
|
|
1149
|
+
</div>`;
|
|
1150
|
+
}
|
|
1151
|
+
renderDropdown(node) {
|
|
1152
|
+
const options = (node.options || ["Select an option"])
|
|
1153
|
+
.map((opt) => `<option>${this.escapeHtml(opt)}</option>`)
|
|
1154
|
+
.join("");
|
|
1155
|
+
return `
|
|
1156
|
+
<div class="proto-field">
|
|
1157
|
+
<label class="proto-label">${this.escapeHtml(node.label || "")}</label>
|
|
1158
|
+
<select class="proto-select" disabled>${options}</select>
|
|
1159
|
+
</div>`;
|
|
1160
|
+
}
|
|
1161
|
+
renderButton(node) {
|
|
1162
|
+
const btnClass = node.variant === "default"
|
|
1163
|
+
? "proto-button-default"
|
|
1164
|
+
: "proto-button-outline";
|
|
1165
|
+
const navIndicator = node.navigateTo
|
|
1166
|
+
? ` <span class="proto-nav-indicator">→ ${this.escapeHtml(node.navigateTo)}</span>`
|
|
1167
|
+
: "";
|
|
1168
|
+
return `<button class="proto-button ${btnClass}" disabled>${this.escapeHtml(node.content || "")}${navIndicator}</button>`;
|
|
1169
|
+
}
|
|
1170
|
+
renderCard(node) {
|
|
1171
|
+
let cardTitle = "";
|
|
1172
|
+
if (node.titleChildren && node.titleChildren.length > 0) {
|
|
1173
|
+
cardTitle = `<div class="proto-card-header">${this.renderInlineNodes(node.titleChildren)}</div>`;
|
|
1174
|
+
}
|
|
1175
|
+
else if (node.title) {
|
|
1176
|
+
cardTitle = `<div class="proto-card-header">${this.escapeHtml(node.title)}</div>`;
|
|
1177
|
+
}
|
|
1178
|
+
const cardChildren = node.children ? this.generate(node.children) : "";
|
|
1179
|
+
return `
|
|
1180
|
+
<div class="proto-card">
|
|
1181
|
+
${cardTitle}
|
|
1182
|
+
<div class="proto-card-content">${cardChildren}</div>
|
|
1183
|
+
</div>`;
|
|
1184
|
+
}
|
|
1185
|
+
renderContainer(node) {
|
|
1186
|
+
const children = node.children ? this.generate(node.children) : "";
|
|
1187
|
+
return `<div class="proto-container">${children}</div>`;
|
|
1188
|
+
}
|
|
1189
|
+
renderGrid(node) {
|
|
1190
|
+
const children = node.children ? this.generate(node.children) : "";
|
|
1191
|
+
const gridConfig = this.parseGridConfig(node.gridConfig || "");
|
|
1192
|
+
return `<div class="proto-grid" style="${gridConfig}">${children}</div>`;
|
|
1193
|
+
}
|
|
1194
|
+
renderDiv(node) {
|
|
1195
|
+
const children = node.children ? this.generate(node.children) : "";
|
|
1196
|
+
return `<div class="proto-div ${this.escapeHtml(node.className || "")}">${children}</div>`;
|
|
1197
|
+
}
|
|
1198
|
+
renderTable(node) {
|
|
1199
|
+
const headerCells = (node.headers || [])
|
|
1200
|
+
.map((h) => `<th class="proto-table-th">${this.escapeHtml(h)}</th>`)
|
|
1201
|
+
.join("");
|
|
1202
|
+
const bodyRows = (node.rows || [])
|
|
1203
|
+
.map((row) => `<tr>${row
|
|
1204
|
+
.map((cell) => `<td class="proto-table-td">${this.escapeHtml(cell)}</td>`)
|
|
1205
|
+
.join("")}</tr>`)
|
|
1206
|
+
.join("");
|
|
1207
|
+
return `
|
|
1208
|
+
<table class="proto-table">
|
|
1209
|
+
<thead><tr>${headerCells}</tr></thead>
|
|
1210
|
+
<tbody>${bodyRows}</tbody>
|
|
1211
|
+
</table>`;
|
|
1212
|
+
}
|
|
1213
|
+
renderImage(node) {
|
|
1214
|
+
return `<img class="proto-image" src="${this.escapeHtml(node.src || "")}" alt="${this.escapeHtml(node.alt || "")}" />`;
|
|
1215
|
+
}
|
|
1216
|
+
renderWorkflow(node) {
|
|
1217
|
+
const screens = (node.children || [])
|
|
1218
|
+
.map((screen, idx) => {
|
|
1219
|
+
const isInitial = screen.id === node.initialScreen || idx === 0;
|
|
1220
|
+
const screenContent = screen.children
|
|
1221
|
+
? this.generate(screen.children)
|
|
1222
|
+
: "";
|
|
1223
|
+
const screenId = screen.id || "";
|
|
1224
|
+
return `
|
|
1225
|
+
<div class="proto-screen${isInitial ? " proto-screen-active" : ""}" data-screen-id="${this.escapeHtml(screenId)}">
|
|
1226
|
+
<div class="proto-screen-header">
|
|
1227
|
+
<span class="proto-screen-badge">${this.escapeHtml(screenId)}</span>
|
|
1228
|
+
${isInitial
|
|
1229
|
+
? '<span class="proto-screen-initial">Initial</span>'
|
|
1230
|
+
: ""}
|
|
1231
|
+
</div>
|
|
1232
|
+
<div class="proto-screen-content">${screenContent}</div>
|
|
1233
|
+
</div>`;
|
|
1234
|
+
})
|
|
1235
|
+
.join("");
|
|
1236
|
+
return `<div class="proto-workflow">${screens}</div>`;
|
|
1237
|
+
}
|
|
1238
|
+
renderScreen(node) {
|
|
1239
|
+
const screenChildren = node.children ? this.generate(node.children) : "";
|
|
1240
|
+
const screenId = node.id || "";
|
|
1241
|
+
return `
|
|
1242
|
+
<div class="proto-screen" data-screen-id="${this.escapeHtml(screenId)}">
|
|
1243
|
+
<div class="proto-screen-header">
|
|
1244
|
+
<span class="proto-screen-badge">${this.escapeHtml(screenId)}</span>
|
|
1245
|
+
</div>
|
|
1246
|
+
<div class="proto-screen-content">${screenChildren}</div>
|
|
1247
|
+
</div>`;
|
|
1248
|
+
}
|
|
1249
|
+
parseGridConfig(config) {
|
|
1250
|
+
const styles = [];
|
|
1251
|
+
// Parse cols-N
|
|
1252
|
+
const colsMatch = config.match(/cols-(\d+)/);
|
|
1253
|
+
if (colsMatch) {
|
|
1254
|
+
styles.push(`grid-template-columns: repeat(${colsMatch[1]}, 1fr)`);
|
|
1255
|
+
}
|
|
1256
|
+
// Parse gap-N
|
|
1257
|
+
const gapMatch = config.match(/gap-(\d+)/);
|
|
1258
|
+
if (gapMatch) {
|
|
1259
|
+
styles.push(`gap: ${parseInt(gapMatch[1]) * 4}px`);
|
|
1260
|
+
}
|
|
1261
|
+
return styles.join("; ");
|
|
1262
|
+
}
|
|
1263
|
+
escapeHtml(text) {
|
|
1264
|
+
return text
|
|
1265
|
+
.replace(/&/g, "&")
|
|
1266
|
+
.replace(/</g, "<")
|
|
1267
|
+
.replace(/>/g, ">")
|
|
1268
|
+
.replace(/"/g, """)
|
|
1269
|
+
.replace(/'/g, "'");
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
exports.HtmlGenerator = HtmlGenerator;
|
|
1008
1274
|
exports.MarkdownParser = MarkdownParser;
|
|
1009
1275
|
exports.ShadcnCodeGenerator = ShadcnCodeGenerator;
|
|
1010
1276
|
//# sourceMappingURL=index.js.map
|