improvement(docs): updated with new ss, docs script updated to copy items from main app into docs for tools (#1918)

* improvement(docs): updated script to copy over icons, cleanup unnecessary pages

* updated script with auto-icon generation

* ignore translations, only icons changed

* updated images

* updated i18n.lock

* updated images
This commit is contained in:
Waleed
2025-11-12 01:15:23 -08:00
committed by GitHub
parent 41066b84d2
commit 9a6a6fdacb
514 changed files with 43270 additions and 15914 deletions
+130 -56
View File
@@ -13,11 +13,18 @@ const rootDir = path.resolve(__dirname, '..')
const BLOCKS_PATH = path.join(rootDir, 'apps/sim/blocks/blocks')
const DOCS_OUTPUT_PATH = path.join(rootDir, 'apps/docs/content/docs/en/tools')
const ICONS_PATH = path.join(rootDir, 'apps/sim/components/icons.tsx')
const DOCS_ICONS_PATH = path.join(rootDir, 'apps/docs/components/icons.tsx')
if (!fs.existsSync(DOCS_OUTPUT_PATH)) {
fs.mkdirSync(DOCS_OUTPUT_PATH, { recursive: true })
}
// Ensure docs components directory exists
const docsComponentsDir = path.dirname(DOCS_ICONS_PATH)
if (!fs.existsSync(docsComponentsDir)) {
fs.mkdirSync(docsComponentsDir, { recursive: true })
}
interface BlockConfig {
type: string
name: string
@@ -32,55 +39,120 @@ interface BlockConfig {
[key: string]: any
}
function extractIcons(): Record<string, string> {
/**
* Copy the icons.tsx file from the main sim app to the docs app
* This ensures icons are rendered consistently across both apps
*/
function copyIconsFile(): void {
try {
console.log('Copying icons from sim app to docs app...')
if (!fs.existsSync(ICONS_PATH)) {
console.error(`Source icons file not found: ${ICONS_PATH}`)
return
}
const iconsContent = fs.readFileSync(ICONS_PATH, 'utf-8')
const icons: Record<string, string> = {}
fs.writeFileSync(DOCS_ICONS_PATH, iconsContent)
const functionDeclarationRegex =
/export\s+function\s+(\w+Icon)\s*\([^)]*\)\s*{[\s\S]*?return\s*\(\s*<svg[\s\S]*?<\/svg>\s*\)/g
const arrowFunctionRegex =
/export\s+const\s+(\w+Icon)\s*=\s*\([^)]*\)\s*=>\s*(\(?\s*<svg[\s\S]*?<\/svg>\s*\)?)/g
const functionMatches = Array.from(iconsContent.matchAll(functionDeclarationRegex))
for (const match of functionMatches) {
const iconName = match[1]
const svgMatch = match[0].match(/<svg[\s\S]*?<\/svg>/)
if (iconName && svgMatch) {
let svgContent = svgMatch[0]
svgContent = svgContent.replace(/{\.\.\.props}/g, '')
svgContent = svgContent.replace(/{\.\.\.(props|rest)}/g, '')
svgContent = svgContent.replace(/width=["'][^"']*["']/g, '')
svgContent = svgContent.replace(/height=["'][^"']*["']/g, '')
svgContent = svgContent.replace(/<svg/, '<svg className="block-icon"')
icons[iconName] = svgContent
}
}
const arrowMatches = Array.from(iconsContent.matchAll(arrowFunctionRegex))
for (const match of arrowMatches) {
const iconName = match[1]
const svgContent = match[2]
const svgMatch = svgContent.match(/<svg[\s\S]*?<\/svg>/)
if (iconName && svgMatch) {
let cleanedSvg = svgMatch[0]
cleanedSvg = cleanedSvg.replace(/{\.\.\.props}/g, '')
cleanedSvg = cleanedSvg.replace(/{\.\.\.(props|rest)}/g, '')
cleanedSvg = cleanedSvg.replace(/width=["'][^"']*["']/g, '')
cleanedSvg = cleanedSvg.replace(/height=["'][^"']*["']/g, '')
cleanedSvg = cleanedSvg.replace(/<svg/, '<svg className="block-icon"')
icons[iconName] = cleanedSvg
}
}
return icons
console.log('✓ Icons successfully copied to docs app')
} catch (error) {
console.error('Error extracting icons:', error)
console.error('Error copying icons file:', error)
}
}
/**
* Generate icon mapping from all block definitions
* Maps block types to their icon component names
* Skips blocks that don't have documentation generated (same logic as generateBlockDoc)
*/
async function generateIconMapping(): Promise<Record<string, string>> {
try {
console.log('Generating icon mapping from block definitions...')
const iconMapping: Record<string, string> = {}
const blockFiles = await glob(`${BLOCKS_PATH}/*.ts`)
for (const blockFile of blockFiles) {
const fileContent = fs.readFileSync(blockFile, 'utf-8')
const blockConfig = extractBlockConfig(fileContent)
if (!blockConfig?.type || !blockConfig.iconName) {
continue
}
// Skip blocks that don't have documentation (same logic as generateBlockDoc)
if (blockConfig.type.includes('_trigger') || blockConfig.type.includes('_webhook')) {
continue
}
if (
(blockConfig.category === 'blocks' &&
blockConfig.type !== 'memory' &&
blockConfig.type !== 'knowledge') ||
blockConfig.type === 'evaluator' ||
blockConfig.type === 'number' ||
blockConfig.type === 'webhook' ||
blockConfig.type === 'schedule' ||
blockConfig.type === 'mcp' ||
blockConfig.type === 'generic_webhook'
) {
continue
}
iconMapping[blockConfig.type] = blockConfig.iconName
}
console.log(`✓ Generated icon mapping for ${Object.keys(iconMapping).length} blocks`)
return iconMapping
} catch (error) {
console.error('Error generating icon mapping:', error)
return {}
}
}
/**
* Write the icon mapping to the docs app
* This file is imported by BlockInfoCard to resolve icons automatically
*/
function writeIconMapping(iconMapping: Record<string, string>): void {
try {
const iconMappingPath = path.join(rootDir, 'apps/docs/components/ui/icon-mapping.ts')
// Get unique icon names
const iconNames = [...new Set(Object.values(iconMapping))].sort()
// Generate imports
const imports = iconNames.map((icon) => ` ${icon},`).join('\n')
// Generate mapping with direct references (no dynamic access for tree shaking)
const mappingEntries = Object.entries(iconMapping)
.map(([blockType, iconName]) => ` ${blockType}: ${iconName},`)
.join('\n')
const content = `// Auto-generated file - do not edit manually
// Generated by scripts/generate-docs.ts
// Maps block types to their icon component references
import type { ComponentType, SVGProps } from 'react'
import {
${imports}
} from '@/components/icons'
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
export const blockTypeToIconMap: Record<string, IconComponent> = {
${mappingEntries}
}
`
fs.writeFileSync(iconMappingPath, content)
console.log('✓ Icon mapping file written to docs app')
} catch (error) {
console.error('Error writing icon mapping:', error)
}
}
function extractBlockConfig(fileContent: string): BlockConfig | null {
try {
const exportMatch = fileContent.match(/export\s+const\s+(\w+)Block\s*:/)
@@ -787,7 +859,7 @@ function mergeWithManualContent(
Object.entries(manualSections).forEach(([sectionName, content]) => {
const insertionPoints: Record<string, { regex: RegExp }> = {
intro: {
regex: /<BlockInfoCard[\s\S]*?<\/svg>`}\s*\/>/,
regex: /<BlockInfoCard[\s\S]*?(\/>|<\/svg>`}\s*\/>)/,
},
usage: {
regex: /## Usage Instructions/,
@@ -822,7 +894,7 @@ function mergeWithManualContent(
return mergedContent
}
async function generateBlockDoc(blockPath: string, icons: Record<string, string>) {
async function generateBlockDoc(blockPath: string) {
try {
const blockFileName = path.basename(blockPath, '.ts')
if (blockFileName.endsWith('.test')) {
@@ -848,7 +920,11 @@ async function generateBlockDoc(blockPath: string, icons: Record<string, string>
blockConfig.type !== 'memory' &&
blockConfig.type !== 'knowledge') ||
blockConfig.type === 'evaluator' ||
blockConfig.type === 'number'
blockConfig.type === 'number' ||
blockConfig.type === 'webhook' ||
blockConfig.type === 'schedule' ||
blockConfig.type === 'mcp' ||
blockConfig.type === 'generic_webhook'
) {
return
}
@@ -863,7 +939,7 @@ async function generateBlockDoc(blockPath: string, icons: Record<string, string>
const manualSections = existingContent ? extractManualContent(existingContent) : {}
const markdown = await generateMarkdownForBlock(blockConfig, icons)
const markdown = await generateMarkdownForBlock(blockConfig)
let finalContent = markdown
if (Object.keys(manualSections).length > 0) {
@@ -880,10 +956,7 @@ async function generateBlockDoc(blockPath: string, icons: Record<string, string>
}
}
async function generateMarkdownForBlock(
blockConfig: BlockConfig,
icons: Record<string, string>
): Promise<string> {
async function generateMarkdownForBlock(blockConfig: BlockConfig): Promise<string> {
const {
type,
name,
@@ -891,13 +964,10 @@ async function generateMarkdownForBlock(
longDescription,
category,
bgColor,
iconName,
outputs = {},
tools = { access: [] },
} = blockConfig
const iconSvg = iconName && icons[iconName] ? icons[iconName] : null
let outputsSection = ''
if (outputs && Object.keys(outputs).length > 0) {
@@ -1063,8 +1133,6 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
<BlockInfoCard
type="${type}"
color="${bgColor || '#F5F5F5'}"
icon={${iconSvg ? 'true' : 'false'}}
iconSvg={\`${iconSvg || ''}\`}
/>
${usageInstructions}
@@ -1080,12 +1148,17 @@ ${toolsSection}
async function generateAllBlockDocs() {
try {
const icons = extractIcons()
// Copy icons from sim app to docs app
copyIconsFile()
// Generate icon mapping from block definitions
const iconMapping = await generateIconMapping()
writeIconMapping(iconMapping)
const blockFiles = await glob(`${BLOCKS_PATH}/*.ts`)
for (const blockFile of blockFiles) {
await generateBlockDoc(blockFile, icons)
await generateBlockDoc(blockFile)
}
updateMetaJson()
@@ -1115,6 +1188,7 @@ function updateMetaJson() {
}
fs.writeFileSync(metaJsonPath, JSON.stringify(metaJson, null, 2))
console.log(`Updated meta.json with ${items.length} entries`)
}
generateAllBlockDocs()