Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,64 @@ To add other remote content (non-component):
**For local website content:**
- Follow the standard pull request process below

### Creating Tabs in Remote Content

When writing documentation in source repositories (like llm-d/llm-d) that will be synced to this Docusaurus site, you can create tabbed content using HTML comment markers. These are invisible in GitHub but will be transformed into Docusaurus tabs during the build.

**In your GitHub README:**
```markdown
### Deploy Model Servers

<!-- TABS:START -->
<!-- TAB:GKE (H200):default -->
kubectl apply -k ./manifests/modelserver/gke -n ${NAMESPACE}

<!-- TAB:GKE (B200) -->
kubectl apply -k ./manifests/modelserver/gke-a4 -n ${NAMESPACE}

<!-- TAB:CoreWeave -->
kubectl apply -k ./manifests/modelserver/coreweave -n ${NAMESPACE}

<!-- TABS:END -->
```

**Key points:**
- Use `<!-- TABS:START -->` and `<!-- TABS:END -->` to wrap the entire tabbed section
- Use `<!-- TAB:Label -->` before each tab's content
- Add `:default` after the label to make it the default selected tab (e.g., `<!-- TAB:GKE:default -->`)
- **No imports needed** - the transformation automatically adds them
- On GitHub, the HTML comments are invisible, showing clean markdown
- On Docusaurus, these are transformed into proper `<Tabs>` components

**Result on Docusaurus:**
The content will automatically be transformed with the proper Tabs imports and components, creating an interactive tabbed interface.

### GitHub Callouts Support

The transformation system also automatically converts GitHub-style callouts to Docusaurus admonitions:

```markdown
> [!NOTE]
> This is a note

> [!TIP]
> This is a tip

> [!IMPORTANT]
> This is important

> [!WARNING]
> This is a warning

> [!CAUTION]
> This is dangerous

> [!REQUIREMENTS]
> These are requirements
```

These will be automatically converted to the appropriate Docusaurus `:::note`, `:::tip`, `:::info`, `:::warning`, and `:::danger` admonitions during the build.

### Troubleshooting

| Problem | Solution |
Expand All @@ -170,6 +228,7 @@ To add other remote content (non-component):
| Links broken | Make sure you're using `createStandardTransform()` |
| Component not showing | Check `components-data.yaml` and repository accessibility |
| Wrong sidebar order | Adjust `sidebarPosition` numbers in configuration |
| Tabs not rendering | Check that you have both `TABS:START` and `TABS:END` markers |

## BEFORE DOING A PULL REQUEST

Expand Down
66 changes: 63 additions & 3 deletions remote-content/remote-sources/repo-transforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,81 @@ function getInternalGuidePath(githubUrl) {
return null;
}

/**
* Convert GitHub-friendly tab markers to Docusaurus Tabs components
*/
function convertTabsToDocusaurus(content) {
// Check if there are any tab blocks
const hasTabBlocks = /<!-- TABS:START -->/.test(content);
if (!hasTabBlocks) return content;

// Pattern to match tab blocks
const tabBlockRegex = /<!-- TABS:START -->\n([\s\S]*?)<!-- TABS:END -->/g;

const transformedContent = content.replace(tabBlockRegex, (match, tabsContent) => {
// Split content by TAB markers to extract individual tabs
const tabSections = tabsContent.split(/<!-- TAB:/);
const tabs = [];

// Skip first element (empty or content before first tab)
for (let i = 1; i < tabSections.length; i++) {
const section = tabSections[i];

// Extract label and check for :default marker
const labelMatch = section.match(/^([^:]+?)(?::default)?\s*-->\n([\s\S]*?)$/);
if (labelMatch) {
const label = labelMatch[1].trim();
const content = labelMatch[2].trim();
const isDefault = section.includes(':default -->');
tabs.push({ label, content, isDefault });
}
}

if (tabs.length === 0) return match;

// Generate Docusaurus Tabs component (without imports here)
let result = `<Tabs>\n`;

tabs.forEach(tab => {
const defaultAttr = tab.isDefault ? ' default' : '';
result += ` <TabItem value="${tab.label.toLowerCase().replace(/[^a-z0-9]/g, '-')}" label="${tab.label}"${defaultAttr}>\n\n`;
result += `${tab.content}\n\n`;
result += ` </TabItem>\n`;
});

result += `</Tabs>`;

return result;
});

// Add imports at the top of the file if tabs were found
if (transformedContent !== content) {
return `import Tabs from '@theme/Tabs';\nimport TabItem from '@theme/TabItem';\n\n${transformedContent}`;
}

return transformedContent;
}

/**
* Apply essential MDX compatibility fixes and content transformations
* Combines all content-only transformations that don't require repository information
*/
function applyBasicMdxFixes(content) {
return content
// First convert tabs to Docusaurus format
let transformed = convertTabsToDocusaurus(content);

// Then apply other MDX fixes
return transformed
// Convert GitHub-style callouts to Docusaurus admonitions
.replace(/^> \[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*\n((?:> .*\n?)*)/gm, (match, type, content) => {
.replace(/^> \[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|REQUIREMENTS)\]\s*\n((?:> .*\n?)*)/gm, (match, type, content) => {
// Map GitHub callout types to Docusaurus admonition types
const typeMap = {
'NOTE': 'note',
'TIP': 'tip',
'IMPORTANT': 'info',
'WARNING': 'warning',
'CAUTION': 'danger'
'CAUTION': 'danger',
'REQUIREMENTS': 'info' // Map to info admonition
};

const docusaurusType = typeMap[type] || type.toLowerCase();
Expand Down