Skip to content
Open
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
7 changes: 7 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
nodeLinker: node-modules
packageExtensions:
"@patternfly/patternfly-doc-core@*":
peerDependenciesMeta:
astro:
optional: true
"@patternfly/react-icons":
optional: true
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"@patternfly/react-code-editor": "^6.5.1",
"@patternfly/react-core": "^6.5.1",
"@patternfly/react-table": "^6.5.1",
"@project-felt/ai-guidelines": "github:project-felt/ai-guidelines",
"monaco-editor": "0.54.0"
}
}
2 changes: 1 addition & 1 deletion packages/documentation-framework/scripts/cli/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function getSource(options) {
async function generate(options) {
const start = new Date();
console.log('write source files to patternfly-docs/generated');
const sourceMDWithOptions = (glob, source, ignore) => sourceMD(glob, source, ignore, options._name);
const sourceMDWithOptions = (glob, source, ignore, mdOptions) => sourceMD(glob, source, ignore, options._name, mdOptions);
getSource(options)(sourceMDWithOptions, sourceProps, sourceFunctionDocs);
await waitForProps();
processMD();
Expand Down
40 changes: 31 additions & 9 deletions packages/documentation-framework/scripts/md/parseMD.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ const globs = {
md: [],
};

function toReactComponent(mdFilePath, source, buildMode) {
function toReactComponent(mdFilePath, source, buildMode, { frontmatterDefaults, frontmatterMapping } = {}) {
// vfiles allow for nicer error messages and have native `unified` support
const vfile = toVfile.readSync(mdFilePath);

// Normalize void HTML elements to self-closing for MDX compatibility
const voidElements = 'area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr';
const voidTagRegex = new RegExp(`<(${voidElements})(\\s[^>]*?)\\s*(?<!/)>`, 'g');
vfile.contents = String(vfile.contents).replace(voidTagRegex, '<$1$2 />');

const relPath = path.relative(path.join(process.cwd(), '../..'), vfile.path).split(path.sep).join(path.posix.sep);

let jsx;
Expand All @@ -47,6 +52,23 @@ function toReactComponent(mdFilePath, source, buildMode) {
}
frontmatter = yaml.load(yamlNode.value);

// Apply frontmatter mapping (e.g., { title: "id" } maps title -> id)
if (frontmatterMapping) {
Object.entries(frontmatterMapping).forEach(([fromKey, toKey]) => {
if (frontmatter[fromKey] !== undefined && frontmatter[toKey] === undefined) {
frontmatter[toKey] = frontmatter[fromKey];
}
});
}
// Apply frontmatter defaults (e.g., { section: "AI" })
if (frontmatterDefaults) {
Object.entries(frontmatterDefaults).forEach(([key, value]) => {
if (frontmatter[key] === undefined) {
frontmatter[key] = value;
}
});
}

// Fail early
if (!frontmatter.id) {
file.fail('id attribute is required in frontmatter for PatternFly docs');
Expand Down Expand Up @@ -276,7 +298,7 @@ async function sourcePropsFile(file) {
});
}

function sourceMDFile(file, source, buildMode) {
function sourceMDFile(file, source, buildMode, options) {
if (path.basename(file).startsWith('_')) {
return;
}
Expand All @@ -285,7 +307,7 @@ function sourceMDFile(file, source, buildMode) {
if (source === 'design-guidelines' && file.includes('/accessibility/')) {
return;
}
const { jsx, pageData, outPath } = toReactComponent(file, source, buildMode);
const { jsx, pageData, outPath } = toReactComponent(file, source, buildMode, options);

if (jsx) {
fs.outputFileSync(outPath, jsx);
Expand Down Expand Up @@ -352,12 +374,12 @@ module.exports = {
async waitForProps() {
await Promise.all(pendingProps);
},
sourceMD(glob, source, ignore, buildMode) {
globs.md.push({ glob, source, ignore, buildMode });
sourceMD(glob, source, ignore, buildMode, options) {
globs.md.push({ glob, source, ignore, buildMode, options });
},
processMD() {
globs.md.forEach(({ glob, source, ignore, buildMode }) => {
globSync(glob, { ignore }).forEach(file => sourceMDFile(file, source, buildMode));
globs.md.forEach(({ glob, source, ignore, buildMode, options }) => {
globSync(glob, { ignore }).forEach(file => sourceMDFile(file, source, buildMode, options));
});
},
sourceFunctionDocs,
Expand All @@ -373,10 +395,10 @@ module.exports = {
propWatcher.on('add', onPropFile);
propWatcher.on('change', onPropFile);
});
globs.md.forEach(({ glob, source, ignore }) => {
globs.md.forEach(({ glob, source, ignore, options }) => {
const mdWatcher = chokidar.watch(globSync(glob, { ignored: ignore, ignoreInitial: true }));
function onMDFileChange(file) {
sourceMDFile(file, source, 'start');
sourceMDFile(file, source, 'start', options);
writeIndex();
}
mdWatcher.on('add', onMDFileChange);
Expand Down
28 changes: 25 additions & 3 deletions packages/documentation-framework/scripts/md/styled-tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,35 @@ const styledMdTags = [

function styledTags() {
return tree => {
visit(tree, 'element', node => {
visit(tree, 'element', (node, index, parent) => {
node.properties.className = node.properties.className || '';

if (contentStyledMdTags.includes(node.tagName)) {

const hasDataType = node.properties && (node.properties.dataType || node.properties['data-type']);
const parentHasDataType = parent && parent.properties && (parent.properties.dataType || parent.properties['data-type']);
if (contentStyledMdTags.includes(node.tagName) && !hasDataType && !parentHasDataType) {
node.properties.className += `pf-v6-c-content--${node.tagName} pf-m-editorial`;
}

// GitHub-style admonitions: > [!NOTE], > [!TIP], > [!WARNING], etc.
if (node.tagName === 'blockquote') {
const firstP = node.children && node.children.find(c => c.tagName === 'p');
if (firstP && firstP.children) {
const firstText = firstP.children.find(c => c.type === 'text');
if (firstText) {
const match = firstText.value.match(/^\[!(NOTE|TIP|WARNING|IMPORTANT|CAUTION)\]\s*/i);
if (match) {
const type = match[1].toLowerCase();
node.properties['data-admonition'] = type;
node.properties.className += ` ws-admonition ws-admonition-${type}`;
firstText.value = firstText.value.slice(match[0].length);
if (!firstText.value.trim() && firstP.children.length === 1) {
node.children = node.children.filter(c => c !== firstP);
}
}
}
}
}

if (styledMdTags.includes(node.tagName)) {
node.properties.className += node.properties.className ? ' ' : '';
node.properties.className += `ws-${node.tagName} `;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/* Styles for @project-felt/ai-guidelines content */

/* Figure base styles */
[data-content-source="ai-guidelines"] figure[data-type] {
margin: var(--pf-t--global--spacer--lg) 0;
padding: var(--pf-t--global--spacer--md);
border: 1px solid var(--pf-t--global--border--color--default);
border-radius: var(--pf-t--global--border--radius--small);
text-align: center;
max-width: 700px;
margin-inline: auto;
width: 100%;
}

[data-content-source="ai-guidelines"] figure[data-type="example landscape"] {
max-width: 100%;
}

[data-content-source="ai-guidelines"] figure[data-type] img {
max-width: 100%;
height: auto;
}

[data-content-source="ai-guidelines"] figure[data-type] figcaption {
margin-top: var(--pf-t--global--spacer--sm);
font-size: var(--pf-t--global--font--size--sm);
color: var(--pf-t--global--text--color--subtle);
text-align: left;
}

/* Inline icons in lists and paragraphs */
[data-content-source="ai-guidelines"] li img,
[data-content-source="ai-guidelines"] p img {
height: 1em;
width: auto;
max-width: none;
vertical-align: middle;
}

/* Do examples — green accent */
[data-content-source="ai-guidelines"] figure[data-type="do"] {
border-color: var(--pf-t--global--color--status--success--default);
border-left-width: 4px;
}

[data-content-source="ai-guidelines"] figure[data-type="do"]::before {
content: "Do";
display: block;
text-align: left;
font-weight: var(--pf-t--global--font--weight--body--bold);
color: var(--pf-t--global--color--status--success--default);
margin-bottom: var(--pf-t--global--spacer--sm);
}

/* Don't examples — red accent */
[data-content-source="ai-guidelines"] figure[data-type="dont"] {
border-color: var(--pf-t--global--color--status--danger--default);
border-left-width: 4px;
}

[data-content-source="ai-guidelines"] figure[data-type="dont"]::before {
content: "Don't";
display: block;
text-align: left;
font-weight: var(--pf-t--global--font--weight--body--bold);
color: var(--pf-t--global--color--status--danger--default);
margin-bottom: var(--pf-t--global--spacer--sm);
}

/* Figure groups */
[data-content-source="ai-guidelines"] ul[data-type] {
list-style: none;
padding: 0;
margin: var(--pf-t--global--spacer--lg) 0;
display: grid;
gap: var(--pf-t--global--spacer--md);
}

[data-content-source="ai-guidelines"] ul[data-type="dos-donts"] {
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

[data-content-source="ai-guidelines"] ul[data-type="examples"] {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

[data-content-source="ai-guidelines"] ul[data-type] > li {
list-style: none;
padding: 0;
margin: 0;
}

[data-content-source="ai-guidelines"] ul[data-type] > li > figure[data-type] {
margin: 0;
height: 100%;
}

/* GitHub-style admonitions */
[data-content-source="ai-guidelines"] .ws-admonition {
border-left-width: 4px;
padding: var(--pf-t--global--spacer--md);
margin: var(--pf-t--global--spacer--md) 0;
}

[data-content-source="ai-guidelines"] .ws-admonition::before {
display: block;
font-weight: var(--pf-t--global--font--weight--body--bold);
margin-bottom: var(--pf-t--global--spacer--xs);
}

[data-content-source="ai-guidelines"] .ws-admonition-note {
border-left-color: var(--pf-t--global--color--status--info--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-note::before {
content: "Note";
color: var(--pf-t--global--color--status--info--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-tip {
border-left-color: var(--pf-t--global--color--status--success--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-tip::before {
content: "Tip";
color: var(--pf-t--global--color--status--success--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-warning {
border-left-color: var(--pf-t--global--color--status--warning--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-warning::before {
content: "Warning";
color: var(--pf-t--global--color--status--warning--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-important {
border-left-color: var(--pf-t--global--color--status--danger--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-important::before {
content: "Important";
color: var(--pf-t--global--color--status--danger--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-caution {
border-left-color: var(--pf-t--global--color--status--warning--default);
}

[data-content-source="ai-guidelines"] .ws-admonition-caution::before {
content: "Caution";
color: var(--pf-t--global--color--status--warning--default);
}
2 changes: 2 additions & 0 deletions packages/documentation-framework/templates/mdx.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import './content-sources/ai-guidelines.css';

p.pf-v6-c-content--p.ws-p {
margin: 0;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/documentation-framework/templates/mdx.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const MDXChildTemplate = ({ Component, source, toc = [], index = 0, id }) => {
);
// Create dynamic component for @reach/router
const ChildComponent = () => (
<div className={source !== 'landing-pages' ? 'pf-v6-l-flex pf-v6-m-column pf-m-nowrap-on-2xl' : ''}>
<div className={source !== 'landing-pages' ? 'pf-v6-l-flex pf-v6-m-column pf-m-nowrap-on-2xl' : ''} data-content-source={source}>
{toc.length > 1 && <TableOfContents items={toc} />}
<Stack hasGutter className={(source !== 'landing-pages' && 'ws-example-page-wrapper')}>
{InlineAlerts}
Expand Down
57 changes: 51 additions & 6 deletions packages/documentation-site/patternfly-docs/content/AI/ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,67 @@ sortValue: 1
import { Alert, AlertActionLink, Accordion, AccordionItem, AccordionContent, AccordionToggle, Button, Card, CardHeader, CardTitle, CardBody, CardFooter, Checkbox, Divider, DescriptionList, DescriptionListTerm, DescriptionListGroup, DescriptionListDescription, Grid, GridItem} from '@patternfly/react-core';
import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon';
import DownloadIcon from '@patternfly/react-icons/dist/esm/icons/download-icon';
import { Icon } from '@patternfly/react-core';
import RhUiAiCreateIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-create-icon';
import RhUiAiEditIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-edit-icon';
import RhUiAiEnhanceIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-enhance-icon';
import RhUiAiErrorIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-error-icon';
import RhUiAiExperienceFillIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-experience-fill-icon';
import RhUiAiExperienceIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-experience-icon';
import RhUiAiFilterIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-filter-icon';
import RhUiAiInfoIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-info-icon';
import RhUiAiSearchIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-search-icon';
import RhUiAiTroubleshootIcon from '@patternfly/react-icons/dist/esm/icons/rh-ui-ai-troubleshoot-icon';

When used thoughtfully, **AI** can enhance user experiences through personalized interactions, increased efficiency, and innovative designs. Regardless of the AI resources or workflows you use, it's important to ensure that you're aligned with the compliance rules, ethical considerations, and best practices on this page.

## PatternFly AI resources

The following guides are intended to help you integrate AI into your workflows as you design and develop products with:
### Guidelines

- **[Design language](/ai/design-language):** The foundational design decisions that guide the use of AI features in products.
- **[Rapid prototyping](/ai/rapid-prototyping):** Guidance for generating and iterating AI features during early stages of design.
- **[Marketplace](/ai/marketplace):** Plugins that give AI coding assistants knowledge and skills to generate more accurate, PatternFly-compliant code.
- **[AI-assisted code migration](/ai/ai-assisted-code-migration):** Guidance for using AI to speed up and simplify codebase migrations.
- **[Conversational design principles](/ai/conversation-design):** Guidance for designing effective and human-centered AI conversations.
- **[Transparency notices](/ai/guidelines/transparency-notices):** Guidelines for communicating AI usage to users through visual and verbal indicators.
- **[Iconography](/ai/guidelines/iconography):** Guidelines for using AI-related icons, sparkles, and visual representations.
- **[Color](/ai/guidelines/color):** Color usage guidelines for AI-enabled features.
- **[Chatbot avatars](/ai/guidelines/chatbot-avatars):** Guidelines for chatbot avatar design, robot icons, and launch buttons.
- **[Animation](/ai/guidelines/animation):** Guidelines for AI-related animations and sparkle effects.
- **[Conversation design](/ai/guidelines/conversation-design):** Guidance for designing effective and human-centered AI conversations.

### AI-assisted development

- **[Marketplace](/ai/ai-assisted-development/marketplace):** Plugins that give AI coding assistants knowledge and skills to generate more accurate, PatternFly-compliant code.
- **[PatternFly CLI](/ai/ai-assisted-development/patternfly-cli):** A command-line tool for scaffolding projects, performing code modifications, and running project-related tasks.
- **[PatternFly MCP](/ai/ai-assisted-development/patternfly-mcp):** An MCP server that gives AI coding tools PatternFly knowledge and capabilities.
- **[Rapid prototyping](/ai/ai-assisted-development/rapid-prototyping):** Guidance for generating and iterating AI features during early stages of design.
- **[AI-assisted code migration](/ai/ai-assisted-development/ai-assisted-code-migration):** Guidance for using AI to speed up and simplify codebase migrations.
- **[Compass layout (org demos)](/components/compass/org-demos):** Full-page Compass layout examples for generative UI patterns. For React Flow integration, see the [React Flow guide](/developer-guides/react-flow).

---

## Using AI icons in React

The following AI icons are available in the [@patternfly/react-icons](https://www.npmjs.com/package/@patternfly/react-icons) package. For detailed usage guidelines, see [Iconography](/ai/guidelines/iconography).

| **Icon** | **React** | **Text label** | **Usage** |
| :---: | --- | --- | --- |
| <Icon size="lg"><RhUiAiExperienceIcon /></Icon> | RhUiAiExperienceIcon | | General AI identification, or when no other AI icon is appropriate. |
| <Icon size="lg"><RhUiAiExperienceFillIcon /></Icon> | RhUiAiExperienceFillIcon | | General AI identification, or when no other AI icon is appropriate. |
| <Icon size="lg"><RhUiAiCreateIcon /></Icon> | RhUiAiCreateIcon | "Create with AI" | Create something new with the help of AI. |
| <Icon size="lg"><RhUiAiEditIcon /></Icon> | RhUiAiEditIcon | "Edit with AI" | Edit something with the help of AI. Typically used for editing text. |
| <Icon size="lg"><RhUiAiEnhanceIcon /></Icon> | RhUiAiEnhanceIcon | "Enhance with AI" | Enhance something with AI. |
| <Icon size="lg"><RhUiAiErrorIcon /></Icon> | RhUiAiErrorIcon | "Error found by AI" | A problem has been identified by AI. |
| <Icon size="lg"><RhUiAiFilterIcon /></Icon> | RhUiAiFilterIcon | "Filter with AI" | Filter data with the help of AI. |
| <Icon size="lg"><RhUiAiInfoIcon /></Icon> | RhUiAiInfoIcon | "Information by AI" | Information partially or completely generated by AI. |
| <Icon size="lg"><RhUiAiSearchIcon /></Icon> | RhUiAiSearchIcon | "Search with AI" | Search with the help of AI. |
| <Icon size="lg"><RhUiAiTroubleshootIcon /></Icon> | RhUiAiTroubleshootIcon | "Troubleshoot with AI" | Receive help from AI when troubleshooting issues. |

In Figma, these icons are available in the PatternFly components library via the Red Hat brand library. Using the icon wrapper component, you can swap the icons in the instance menu:

<div class="ws-docs-content-img">
![Menu in Figma showing how to switch between PatternFly and Red Hat icon libraries](./img/rh-icons-figma.png)
</div>

---

## What rules and best practices do I need to follow?

All AI systems built with PatternFly must adhere to Red Hat's legal and ethical framework.
Expand Down
Loading