diff --git a/.cspell/code-terms.txt b/.cspell/code-terms.txt index 285b66365..e28e9d735 100644 --- a/.cspell/code-terms.txt +++ b/.cspell/code-terms.txt @@ -47,6 +47,7 @@ edgesep EMPTYSTR enddate ERDIAGRAM +eslint flatmap forwardable frontmatter diff --git a/.cspell/contributors.txt b/.cspell/contributors.txt index b7f52f8d0..80f4df22a 100644 --- a/.cspell/contributors.txt +++ b/.cspell/contributors.txt @@ -2,8 +2,10 @@ Ashish Jain cpettitt Dong Cai +knsv +Knut Sveidqvist Nikolay Rozhkov Peng Xiao Per Brolin +Sidharth Vinod subhash-halder -Vinod Sidharth diff --git a/.cspell/mermaid-terms.txt b/.cspell/mermaid-terms.txt index cb6db41de..b0cfa0a1d 100644 --- a/.cspell/mermaid-terms.txt +++ b/.cspell/mermaid-terms.txt @@ -13,11 +13,10 @@ gitgraph gzipped handDrawn kanban -knsv -Knut marginx marginy Markdownish +mermaidchart mermaidjs mindmap mindmaps @@ -35,7 +34,6 @@ sandboxed siebling statediagram substate -Sveidqvist unfixable Viewbox viewports diff --git a/.github/lychee.toml b/.github/lychee.toml index b4e8ba0fb..25beba157 100644 --- a/.github/lychee.toml +++ b/.github/lychee.toml @@ -52,7 +52,7 @@ exclude = [ # Timeout "https://huehive.co", "https://foswiki.org", -"https://www.gnu.org", +"https://www.gnu.org" ] # Exclude all private IPs from checking. diff --git a/docs/public/1-Callout-Easy.svg b/docs/public/1-Callout-Easy.svg new file mode 100644 index 000000000..a6e9251a0 --- /dev/null +++ b/docs/public/1-Callout-Easy.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/public/2-Callout-Integrations.svg b/docs/public/2-Callout-Integrations.svg new file mode 100644 index 000000000..b5ebdf055 --- /dev/null +++ b/docs/public/2-Callout-Integrations.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/public/3-Callout-Awards.svg b/docs/public/3-Callout-Awards.svg new file mode 100644 index 000000000..f10c0fc39 --- /dev/null +++ b/docs/public/3-Callout-Awards.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs/public/hero-chart-dark.svg b/docs/public/hero-chart-dark.svg new file mode 100644 index 000000000..2beb9bdab --- /dev/null +++ b/docs/public/hero-chart-dark.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/hero-chart.svg b/docs/public/hero-chart.svg new file mode 100644 index 000000000..fbc675f3a --- /dev/null +++ b/docs/public/hero-chart.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/icons/ai-diagram.svg b/docs/public/icons/ai-diagram.svg new file mode 100644 index 000000000..d3ff002f6 --- /dev/null +++ b/docs/public/icons/ai-diagram.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/icons/ai-repair.svg b/docs/public/icons/ai-repair.svg new file mode 100644 index 000000000..1e255ac81 --- /dev/null +++ b/docs/public/icons/ai-repair.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/icons/comment.svg b/docs/public/icons/comment.svg new file mode 100644 index 000000000..626bfd265 --- /dev/null +++ b/docs/public/icons/comment.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/icons/folder.svg b/docs/public/icons/folder.svg new file mode 100644 index 000000000..a443f1699 --- /dev/null +++ b/docs/public/icons/folder.svg @@ -0,0 +1,11 @@ + + + diff --git a/docs/public/icons/group.svg b/docs/public/icons/group.svg new file mode 100644 index 000000000..8a7443b6e --- /dev/null +++ b/docs/public/icons/group.svg @@ -0,0 +1,11 @@ + + + diff --git a/docs/public/icons/groups.svg b/docs/public/icons/groups.svg new file mode 100644 index 000000000..c827bebc6 --- /dev/null +++ b/docs/public/icons/groups.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/icons/open-source.svg b/docs/public/icons/open-source.svg new file mode 100644 index 000000000..d6c1f9843 --- /dev/null +++ b/docs/public/icons/open-source.svg @@ -0,0 +1,11 @@ + + + + diff --git a/docs/public/icons/plugins.svg b/docs/public/icons/plugins.svg new file mode 100644 index 000000000..fbf4a4800 --- /dev/null +++ b/docs/public/icons/plugins.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/icons/presentation.svg b/docs/public/icons/presentation.svg new file mode 100644 index 000000000..4c679a19e --- /dev/null +++ b/docs/public/icons/presentation.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/icons/public.svg b/docs/public/icons/public.svg new file mode 100644 index 000000000..3d563baa1 --- /dev/null +++ b/docs/public/icons/public.svg @@ -0,0 +1,11 @@ + + + diff --git a/docs/public/icons/terminal.svg b/docs/public/icons/terminal.svg new file mode 100644 index 000000000..5af2408d4 --- /dev/null +++ b/docs/public/icons/terminal.svg @@ -0,0 +1,11 @@ + + + diff --git a/docs/public/icons/version-history.svg b/docs/public/icons/version-history.svg new file mode 100644 index 000000000..bacb28629 --- /dev/null +++ b/docs/public/icons/version-history.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/icons/whiteboard.svg b/docs/public/icons/whiteboard.svg new file mode 100644 index 000000000..9ac13652a --- /dev/null +++ b/docs/public/icons/whiteboard.svg @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/docs/public/mermaid-logo-horizontal.svg b/docs/public/mermaid-logo-horizontal.svg new file mode 100644 index 000000000..34b3bb124 --- /dev/null +++ b/docs/public/mermaid-logo-horizontal.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/mermaid/src/docs/.vitepress/canonical-config.ts b/packages/mermaid/src/docs/.vitepress/canonical-config.ts new file mode 100644 index 000000000..7c2b5aaeb --- /dev/null +++ b/packages/mermaid/src/docs/.vitepress/canonical-config.ts @@ -0,0 +1,149 @@ +import type { CanonicalUrlConfig } from './canonical-urls.js'; + +/** + * Canonical URL configuration for Mermaid documentation + * + * This file contains the configuration for generating canonical URLs + * for the Mermaid documentation site. + */ +export const canonicalConfig: CanonicalUrlConfig = { + // Base URL for the Mermaid documentation site + baseUrl: 'https://docs.mermaidchart.com', + + // Disable automatic generation - only use specificCanonicalUrls + autoGenerate: false, + + // Patterns for pages to exclude from automatic canonical URL generation + excludePatterns: [ + // Exclude the main index page (handled by VitePress home layout) + 'index.md', + + // Exclude any draft or temporary files + 'draft-*.md', + '**/draft-*.md', + 'temp-*.md', + '**/temp-*.md', + '*.draft.md', + '**/*.draft.md', + + // Exclude any test files + '*.test.md', + '**/*.test.md', + '*.spec.md', + '**/*.spec.md', + + // You can add more patterns here as needed + // Examples: + // '**/internal/**', // Exclude internal documentation + // '**/archive/**', // Exclude archived content + ], + + // URL transformation rules + transformations: { + // Remove index.md from URLs (e.g., /intro/index.md -> /intro/) + //removeIndex: true, + + // Remove .md extension from URLs (e.g., /syntax/flowchart.md -> /syntax/flowchart) + removeMarkdownExtension: true, + + // Custom path transformations + customTransforms: [ + // Example: Redirect old paths to new paths + // { pattern: /^old-syntax\//, replacement: 'syntax/' }, + // Example: Handle special cases + // { pattern: /^config\/setup\/README$/, replacement: 'config/setup/' }, + // Add your custom transformations here + ], + }, +}; + +/** + * Pages that should have specific canonical URLs + * + * Since autoGenerate is set to false, ONLY pages listed here will get canonical URLs. + * + * Usage: Add entries to this object where the key is the relative path + * of the markdown file and the value is the desired canonical URL. + * + * Examples: + * - 'intro/index.md': 'https://docs.mermaidchart.com/intro/index.html' + * - 'syntax/flowchart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/flowchart.html' + * - 'config/configuration.md': 'https://docs.mermaidchart.com/mermaid-oss/config/configuration.html' + */ +export const specificCanonicalUrls: Record = { + // Add your specific canonical URLs here + // Example: + // 'syntax/flowchart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/flowchart.html', + + // Intro section + 'intro/index.md': 'https://docs.mermaidchart.com/intro/index.html', + 'intro/getting-started.md': + 'https://docs.mermaidchart.com/mermaid-oss/intro/getting-started.html', + 'intro/syntax-reference.md': + 'https://docs.mermaidchart.com/mermaid-oss/intro/syntax-reference.html', + + // Syntax section + 'syntax/flowchart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/flowchart.html', + 'syntax/sequenceDiagram.md': + 'https://docs.mermaidchart.com/mermaid-oss/syntax/sequenceDiagram.html', + 'syntax/classDiagram.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/classDiagram.html', + 'syntax/stateDiagram.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/stateDiagram.html', + 'syntax/entityRelationshipDiagram.md': + 'https://docs.mermaidchart.com/mermaid-oss/syntax/entityRelationshipDiagram.html', + 'syntax/userJourney.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/userJourney.html', + 'syntax/gantt.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/gantt.html', + 'syntax/pie.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/pie.html', + 'syntax/quadrantChart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/quadrantChart.html', + 'syntax/requirementDiagram.md': + 'https://docs.mermaidchart.com/mermaid-oss/syntax/requirementDiagram.html', + 'syntax/mindmap.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/mindmap.html', + 'syntax/timeline.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/timeline.html', + 'syntax/gitgraph.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/gitgraph.html', + 'syntax/c4.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/c4.html', + 'syntax/sankey.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/sankey.html', + 'syntax/xyChart.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/xyChart.html', + 'syntax/block.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/block.html', + 'syntax/packet.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/packet.html', + 'syntax/kanban.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/kanban.html', + 'syntax/architecture.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/architecture.html', + 'syntax/radar.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/radar.html', + 'syntax/examples.md': 'https://docs.mermaidchart.com/mermaid-oss/syntax/examples.html', + + // Config section + 'config/configuration.md': 'https://docs.mermaidchart.com/mermaid-oss/config/configuration.html', + 'config/usage.md': 'https://docs.mermaidchart.com/mermaid-oss/config/usage.html', + 'config/icons.md': 'https://docs.mermaidchart.com/mermaid-oss/config/icons.html', + 'config/directives.md': 'https://docs.mermaidchart.com/mermaid-oss/config/directives.html', + 'config/theming.md': 'https://docs.mermaidchart.com/mermaid-oss/config/theming.html', + 'config/math.md': 'https://docs.mermaidchart.com/mermaid-oss/config/math.html', + 'config/accessibility.md': 'https://docs.mermaidchart.com/mermaid-oss/config/accessibility.html', + 'config/mermaidCLI.md': 'https://docs.mermaidchart.com/mermaid-oss/config/mermaidCLI.html', + 'config/faq.md': 'https://docs.mermaidchart.com/mermaid-oss/config/faq.html', + + // Ecosystem section + 'ecosystem/mermaid-chart.md': + 'https://docs.mermaidchart.com/mermaid-oss/ecosystem/mermaid-chart.html', + 'ecosystem/tutorials.md': 'https://docs.mermaidchart.com/mermaid-oss/ecosystem/tutorials.html', + 'ecosystem/integrations-community.md': + 'https://docs.mermaidchart.com/mermaid-oss/ecosystem/integrations-community.html', + 'ecosystem/integrations-create.md': + 'https://docs.mermaidchart.com/mermaid-oss/ecosystem/integrations-create.html', + + // Community section + 'community/intro.md': 'https://docs.mermaidchart.com/mermaid-oss/community/intro.html', + 'community/contributing.md': + 'https://docs.mermaidchart.com/mermaid-oss/community/contributing.html', + 'community/new-diagram.md': + 'https://docs.mermaidchart.com/mermaid-oss/community/new-diagram.html', + 'community/questions-and-suggestions.md': + 'https://docs.mermaidchart.com/mermaid-oss/community/questions-and-suggestions.html', + 'community/security.md': 'https://docs.mermaidchart.com/mermaid-oss/community/security.html', +}; + +/** + * Helper function to get canonical URL for a specific page + * This can be used in frontmatter or for manual overrides + */ +export function getCanonicalUrl(relativePath: string): string | undefined { + return specificCanonicalUrls[relativePath]; +} diff --git a/packages/mermaid/src/docs/.vitepress/canonical-urls.ts b/packages/mermaid/src/docs/.vitepress/canonical-urls.ts new file mode 100644 index 000000000..6c04e1eb1 --- /dev/null +++ b/packages/mermaid/src/docs/.vitepress/canonical-urls.ts @@ -0,0 +1,198 @@ +import type { PageData } from 'vitepress'; +import { canonicalConfig, specificCanonicalUrls } from './canonical-config.js'; + +/** + * Configuration for canonical URL generation + */ +export interface CanonicalUrlConfig { + /** Base URL for the site (e.g., 'https://mermaid.js.org') */ + baseUrl: string; + /** Whether to automatically generate canonical URLs for pages without explicit ones */ + autoGenerate: boolean; + /** Pages to exclude from automatic canonical URL generation (glob patterns supported) */ + excludePatterns?: string[]; + /** Custom URL transformations */ + transformations?: { + /** Remove index.md from URLs */ + removeIndex?: boolean; + /** Remove .md extension from URLs */ + removeMarkdownExtension?: boolean; + /** Custom path transformations */ + customTransforms?: { + pattern: RegExp; + replacement: string; + }[]; + }; +} + +/** + * Default configuration for canonical URLs + */ +const defaultConfig: CanonicalUrlConfig = { + baseUrl: 'https://mermaid.js.org', + autoGenerate: true, + excludePatterns: [ + // Exclude the home page as it's handled separately + 'index.md', + // Exclude any temporary or draft files + '**/draft-*', + '**/temp-*', + ], + transformations: { + removeIndex: true, + removeMarkdownExtension: true, + customTransforms: [ + // Transform any special cases here + // Example: { pattern: /^old-path\//, replacement: 'new-path/' } + ], + }, +}; + +/** + * Check if a path matches any of the exclude patterns + */ +function shouldExcludePath(relativePath: string, excludePatterns: string[] = []): boolean { + return excludePatterns.some((pattern) => { + // Convert glob pattern to regex + const regexPattern = pattern + .replace(/\*\*/g, '.*') + .replace(/\*/g, '[^/]*') + .replace(/\?/g, '.') + .replace(/\./g, '\\.'); + const regex = new RegExp(`^${regexPattern}$`); + return regex.test(relativePath); + }); +} + +/** + * Transform a relative path to a canonical URL path + */ +function transformPath(relativePath: string, config: CanonicalUrlConfig): string { + let transformedPath = relativePath; + + // Apply built-in transformations + if (config.transformations?.removeMarkdownExtension) { + transformedPath = transformedPath.replace(/\.md$/, ''); + } + + if (config.transformations?.removeIndex) { + transformedPath = transformedPath.replace(/\/index$/, '/'); + if (transformedPath === 'index') { + transformedPath = ''; + } + } + + // Apply custom transformations + if (config.transformations?.customTransforms) { + for (const transform of config.transformations.customTransforms) { + transformedPath = transformedPath.replace(transform.pattern, transform.replacement); + } + } + + // Ensure path starts with / + if (transformedPath && !transformedPath.startsWith('/')) { + transformedPath = '/' + transformedPath; + } + + // Handle root path + if (!transformedPath) { + transformedPath = '/'; + } + + return transformedPath; +} + +/** + * Generate a canonical URL for a page + */ +function generateCanonicalUrl(relativePath: string, config: CanonicalUrlConfig): string { + const transformedPath = transformPath(relativePath, config); + return config.baseUrl + transformedPath; +} + +/** + * VitePress transformPageData hook to add canonical URLs + */ +export function addCanonicalUrls(pageData: PageData): void { + const config = canonicalConfig; + + // Check for specific canonical URLs first + const specificUrl = specificCanonicalUrls[pageData.relativePath]; + if (specificUrl) { + addCanonicalToHead(pageData, specificUrl); + return; + } + + // Skip if canonical URL is already explicitly set in frontmatter + if (pageData.frontmatter.canonical) { + // If it's already a full URL, use as-is + if (pageData.frontmatter.canonical.startsWith('http')) { + addCanonicalToHead(pageData, pageData.frontmatter.canonical); + return; + } + // If it's a relative path, convert to absolute URL + const canonicalUrl = config.baseUrl + pageData.frontmatter.canonical; + addCanonicalToHead(pageData, canonicalUrl); + return; + } + + // Skip if canonicalPath is set in frontmatter + if (pageData.frontmatter.canonicalPath) { + const canonicalUrl = config.baseUrl + pageData.frontmatter.canonicalPath; + addCanonicalToHead(pageData, canonicalUrl); + return; + } + + // Skip if auto-generation is disabled + if (!config.autoGenerate) { + return; + } + + // Skip if path should be excluded + if (shouldExcludePath(pageData.relativePath, config.excludePatterns)) { + return; + } + + // Generate canonical URL + const canonicalUrl = generateCanonicalUrl(pageData.relativePath, config); + addCanonicalToHead(pageData, canonicalUrl); +} + +/** + * Add canonical URL to page head + */ +function addCanonicalToHead(pageData: PageData, canonicalUrl: string): void { + // Initialize head array if it doesn't exist + pageData.frontmatter.head = pageData.frontmatter.head || []; + + // Check if canonical link already exists + const hasCanonical = pageData.frontmatter.head.some( + (item: any) => Array.isArray(item) && item[0] === 'link' && item[1]?.rel === 'canonical' + ); + + // Add canonical link if it doesn't exist + if (!hasCanonical) { + pageData.frontmatter.head.push(['link', { rel: 'canonical', href: canonicalUrl }]); + } +} + +/** + * Utility function to create a custom configuration + * This can be used to override the default configuration + */ +export function createCanonicalUrlConfig( + customConfig: Partial +): CanonicalUrlConfig { + return { + ...defaultConfig, + ...customConfig, + transformations: { + ...defaultConfig.transformations, + ...customConfig.transformations, + customTransforms: [ + ...(defaultConfig.transformations?.customTransforms || []), + ...(customConfig.transformations?.customTransforms || []), + ], + }, + }; +} diff --git a/packages/mermaid/src/docs/.vitepress/components/EditorSelectionModal.vue b/packages/mermaid/src/docs/.vitepress/components/EditorSelectionModal.vue new file mode 100644 index 000000000..e41a7096d --- /dev/null +++ b/packages/mermaid/src/docs/.vitepress/components/EditorSelectionModal.vue @@ -0,0 +1,138 @@ + + + diff --git a/packages/mermaid/src/docs/.vitepress/components/TopBar.vue b/packages/mermaid/src/docs/.vitepress/components/TopBar.vue index 0914d808e..c5b464bb6 100644 --- a/packages/mermaid/src/docs/.vitepress/components/TopBar.vue +++ b/packages/mermaid/src/docs/.vitepress/components/TopBar.vue @@ -1,134 +1,65 @@