mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
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:
+130
-56
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user