Skip to content

Commit bc19ffe

Browse files
Code snippets (#84)
* refactor: unify portal code-block component across skills/terraform/mcp Skills, Terraform, and MCP install snippets now share a single code-block component with consistent wrapper (gray header + copy button), tokens palette (Mulesoft-style for keywords/tags/strings, Postman-style for JSON keys/values), and Prism inclusion. - Extract Prism includes into partials/prism_head.html (4 dupes -> 1). - Rename terraform-code-wrapper/header/btn-copy to neutral code-block-wrapper/header/copy-btn. - Generalize wrapper JS via wrapCodeBlocksWithCopyHeader(selector); add wrapMcpCodeBlocks for MCP install snippets. - Replace MCP legacy mcp-install-block + btn-copy-install with the unified wrapper to remove duplicated copy buttons. - Consolidate token palette under .code-block-wrapper; JSON keeps Postman red/blue via :not(.language-json) negation. - Drop duplicated pre/code rules in step-prose, terraform-view-markdown, and mcp-install-block (~80 lines removed). APIs (Ace editors) intentionally out of scope; MCP config blocks keep their existing in-line copy button to avoid changing modal UX. * fix: resolve copyMcpCurlCommand collision and unify Ace editor palette - Rename duplicate copyMcpCurlCommand (x-origin modal) to copyXOriginMcpCurl, fixing silent failure on MCP try-it Copy cURL. - Apply Postman JSON palette to Ace editors: red keys, blue string values, dark blue for null/true/false. - Remove active-line and gutter gray backgrounds from Ace editors. - Unify Prism JSON boolean/keyword to same dark blue (#014486). - Wrap .skill-view-markdown pre blocks (3 previously missed). * chore: trigger CI rebuild * fix: add skill-view-markdown class to step prose and remove max-width - Add skill-view-markdown class to step_unified.html prose_before and prose_after divs so code blocks in steps get wrapped by wrapSkillCodeBlocks. - Remove max-width from .skill-view-markdown to allow full-width content. * fix: consistent white bg and borders for all code blocks and Ace editors - Ace editors: force white bg on gutter/scroller/editor, add consistent border and radius, remove active-line gray highlight. - Response body/headers: white bg instead of gray-50, matching border and radius with request editors. - Code block snippets: switch to pre-wrap to eliminate horizontal scroll on long lines (XML options, CLI commands). - Add skill-view-markdown class to step_unified prose divs for wrapper coverage. - Remove max-width from .skill-view-markdown. * feat: add skill.yaml metadata for prose skill type with full content rendering - Create skills/mule-development/skill.yaml with type:prose — covers all child skills via hierarchical resolution. - Add _resolve_skill_type() to discovery.py: reads skill.yaml from skill dir then parent dir, falls back to heuristic when absent. - Add prose heading extraction and anchor injection to skill_parser.py: _extract_prose_headings(), _inject_heading_ids(), shared _slugify(). - Generator uses explicit skill_type when present, heuristic fallback otherwise — zero change for skills without metadata. - skill_detail.html: prose branch renders full_content_html; JTBD branch unchanged. - skill_sidebar.html: prose skills show ## headings as nav links with matching anchor hrefs. * feat: on-demand skill ZIP download via JSZip and manifest - Replace "Open Skill Markdown" with "Download Skill" button that generates a ZIP on-demand in the browser using JSZip. - Generator creates manifest.json per skill listing all files, and copies full skill directory contents to portal output. - Excludes node_modules, __pycache__, .git, .DS_Store from manifests. - No pre-generated ZIP files — scales better as skill count grows. * fix: exclude overview from prose sidebar, hide step badge, linkify related jobs - skill_sidebar.html: exclude heading with id 'overview' from prose nav (already shown as fixed link above). - skill_card.html: hide step count badge for skills with type 'prose'. - skill_parser.py: add _linkify_related_jobs() to convert **slug**: patterns into clickable links in full_content_html. * fix: skip code-block wrapper for mermaid diagrams Exclude <pre> containing <code class="language-mermaid"> from wrapCodeBlocksWithCopyHeader so Mermaid can replace them with SVG without being trapped inside the code-block wrapper. * feat: add skill-prose-content class and fix prose rendering - Add .skill-prose-content CSS class with styles matching skill-subsection (h2 = 24px bold, proper spacing, scroll-margin). Isolated from .skill-view-markdown to avoid side-effects on JTBD skills. - Use skill.skill_type == 'prose' (explicit) instead of prose_only (heuristic) for full_content_html rendering — prose-by-fallback skills keep section-based render. - Hide h1 inside prose content (already shown in page header). - Hide overview/prerequisites sections for explicit prose (already in full_content_html). - Add skill.yaml to anypoint-platform-operations with type: prose. - Add .skill-prose-content pre to wrapSkillCodeBlocks selector. * fix: remove skill-view-markdown class from step-prose in step_unified Reverts the addition of skill-view-markdown to step-prose divs — it was causing unintended margin-bottom on li elements in JTBD steps. The code-block wrapping already works via .step-prose pre selector. * fix: PR feedback - rename metadata, restore view markdown, fix copy, show H1 - Rename skill.yaml to skills-metadata.yaml (less confusing, supports hierarchical override at any level). - Restore "View Markdown" link alongside "Download Skill" in dropdown. - Fix "Copy Skill" by passing skill_rel_path instead of slug (fixes nested skills like mule-development/*). - Show H1 title in prose skills (was hidden with display:none).
1 parent 6f6b9fb commit bc19ffe

19 files changed

Lines changed: 536 additions & 195 deletions

File tree

scripts/portal_generator/assets/portal.js

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -918,7 +918,7 @@ function _buildMcpSourcePanel(idx, mcpSlug, toolName, origin, mcpLookup, envVars
918918
html += '<span class="try-spinner" id="spinner-xorigin-' + idx + '" style="display:none">Sending...</span>';
919919
html += '<button class="btn-send" onclick="executeMcpXOriginSource(' + idx + ', this)">';
920920
html += '<img src="../assets/icons/send-icon.svg" alt="" width="13" height="11"><span>Send</span></button>';
921-
html += '<button class="btn-copy-curl" onclick="copyMcpCurlCommand(\'' + xoriginOpId + '\', ' + idx + ', this)">';
921+
html += '<button class="btn-copy-curl" onclick="copyXOriginMcpCurl(\'' + xoriginOpId + '\', ' + idx + ', this)">';
922922
html += '<img src="../assets/icons/copy-curl-icon.svg" alt="" width="13" height="13"><span>Copy cURL</span></button>';
923923
html += '</div></div>';
924924

@@ -4107,7 +4107,7 @@ function copyCurlCommand(opId, buttonEl) {
41074107
});
41084108
}
41094109

4110-
function copyMcpCurlCommand(xoriginOpId, sourceIdx, buttonEl) {
4110+
function copyXOriginMcpCurl(xoriginOpId, sourceIdx, buttonEl) {
41114111
var currentModal = xOriginModalStack[xOriginModalStack.length - 1];
41124112
if (!currentModal) return;
41134113
var origin = currentModal.origins[sourceIdx];
@@ -8450,14 +8450,31 @@ function clearTerraformSidebarSearch() {
84508450
}
84518451

84528452
function wrapTerraformCodeBlocks() {
8453-
document.querySelectorAll('.terraform-view-markdown pre').forEach(function(pre) {
8454-
if (pre.parentElement.classList.contains('terraform-code-wrapper')) return;
8453+
wrapCodeBlocksWithCopyHeader('.terraform-view-markdown pre');
8454+
}
8455+
8456+
function wrapSkillCodeBlocks() {
8457+
wrapCodeBlocksWithCopyHeader('.step-prose pre');
8458+
wrapCodeBlocksWithCopyHeader('.skill-view-markdown pre');
8459+
wrapCodeBlocksWithCopyHeader('.skill-prose-content pre');
8460+
}
8461+
8462+
function wrapMcpCodeBlocks() {
8463+
// MCP config blocks already have their own copy button inside `.mcp-config-block`,
8464+
// so only the install pre gets the unified wrapper.
8465+
wrapCodeBlocksWithCopyHeader('pre.mcp-install-command');
8466+
}
8467+
8468+
function wrapCodeBlocksWithCopyHeader(selector) {
8469+
document.querySelectorAll(selector).forEach(function(pre) {
8470+
if (pre.parentElement.classList.contains('code-block-wrapper')) return;
8471+
if (pre.querySelector('code.language-mermaid')) return;
84558472
var wrapper = document.createElement('div');
8456-
wrapper.className = 'terraform-code-wrapper';
8473+
wrapper.className = 'code-block-wrapper';
84578474
var header = document.createElement('div');
8458-
header.className = 'terraform-code-header';
8475+
header.className = 'code-block-header';
84598476
var btn = document.createElement('button');
8460-
btn.className = 'terraform-btn-copy';
8477+
btn.className = 'code-block-copy-btn';
84618478
btn.onclick = function() { copyTerraformCode(btn); };
84628479
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>';
84638480
header.appendChild(btn);
@@ -8468,7 +8485,7 @@ function wrapTerraformCodeBlocks() {
84688485
}
84698486

84708487
function copyTerraformCode(button) {
8471-
var wrapper = button.closest('.terraform-code-wrapper');
8488+
var wrapper = button.closest('.code-block-wrapper');
84728489
if (!wrapper) return;
84738490
var code = wrapper.querySelector('pre code') || wrapper.querySelector('pre');
84748491
var text = code.textContent || code.innerText;
@@ -8604,3 +8621,34 @@ function copyToClipboard(text, buttonEl) {
86048621
});
86058622
}
86068623

8624+
8625+
function downloadSkillZip(skillRelPath, slug) {
8626+
var basePath = '../skills/' + skillRelPath + '/';
8627+
fetch(basePath + 'manifest.json')
8628+
.then(function(r) { return r.json(); })
8629+
.then(function(manifest) {
8630+
var zip = new JSZip();
8631+
var fetches = manifest.files.map(function(file) {
8632+
return fetch(basePath + file)
8633+
.then(function(r) { return r.blob(); })
8634+
.then(function(blob) { zip.file(file, blob); });
8635+
});
8636+
return Promise.all(fetches).then(function() { return zip; });
8637+
})
8638+
.then(function(zip) {
8639+
return zip.generateAsync({ type: 'blob' });
8640+
})
8641+
.then(function(blob) {
8642+
var a = document.createElement('a');
8643+
a.href = URL.createObjectURL(blob);
8644+
a.download = slug + '.zip';
8645+
document.body.appendChild(a);
8646+
a.click();
8647+
document.body.removeChild(a);
8648+
URL.revokeObjectURL(a.href);
8649+
})
8650+
.catch(function(err) {
8651+
console.error('Failed to download skill:', err);
8652+
alert('Download failed. Please try again.');
8653+
});
8654+
}

0 commit comments

Comments
 (0)