From 4280c3e4cb74d3325ae94e6abf2666ed82b21b0c Mon Sep 17 00:00:00 2001 From: Guilherme Segantini <25270914+guilherme-segantini@users.noreply.github.com> Date: Tue, 28 Apr 2026 01:16:23 -0500 Subject: [PATCH 01/14] blog: improve readability of agentic engineering post (#983) --- news/2026-04-27-agentic-engineering.md | 118 ++++++++++++------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/news/2026-04-27-agentic-engineering.md b/news/2026-04-27-agentic-engineering.md index 80cf3fb90c..0c132ce3a5 100644 --- a/news/2026-04-27-agentic-engineering.md +++ b/news/2026-04-27-agentic-engineering.md @@ -18,19 +18,19 @@ Let's use Claude Code to build an SAP Extensions app. With a grounded spec, it b Excitement is not just getting code done fast but code that's reliable and truly works for the enterprise. Claude fully tested the app. Then I opened it and got a blank page. Several debugging rounds later the page showed up but columns came up empty, buttons did nothing, and the risk data never reached the frontend. It was not one bug. Several issues including deprecated annotations the runtime silently ignored, naming mismatches between the controller and what Fiori Elements actually looks for, and OData wiring that looked correct but had no execution path in V4. -![Financial Risk Analyzer — Fiori Elements List Report showing GL transactions with risk classifications, criticality indicators, and anomaly scores](img/2026-04-27/sample-cap-app-screenshot.png) +![Financial Risk Analyzer, Fiori Elements List Report showing GL transactions with risk classifications, criticality indicators, and anomaly scores](img/2026-04-27/sample-cap-app-screenshot.png) ## The SAP MCP Servers Advantage -Frontier models aren't lacking intelligence. They're really good. But SDKs, APIs, tools, and frameworks evolve rapidly. New library versions ship constantly, yet these models are trained on specifications that become obsolete by the time they ship. They can't be trained at the speed that tools and frameworks evolve. The challenge becomes feeding the model with the right context — grounded in best practices and sources of truth that are up to date. +Frontier models aren't lacking intelligence. They're really good. But SDKs, APIs, tools, and frameworks evolve rapidly. New library versions ship constantly, yet these models are trained on specifications that become obsolete by the time they ship. They can't be trained at the speed that tools and frameworks evolve. The challenge becomes feeding the model with the right context, grounded in best practices and sources of truth that are up to date. -SAP MCP servers give the coding agent real-time access to domain expertise — not static documentation, but callable tools it consults while it works. Skills complement them with procedural knowledge: your team's deploy process, review checklist, CDS modeling conventions. +SAP MCP servers give the coding agent real-time access to domain expertise. Not static documentation, but callable tools it consults while it works. Skills complement them with procedural knowledge: your team's deploy process, review checklist, CDS modeling conventions. For my SAP projects, three MCP servers made the biggest difference: -- **CAP MCP Server** — guides CDS entity modeling, service definitions, and backend patterns according to current CAP conventions -- **Fiori MCP Server** — ensures Fiori Elements applications follow SAP UX guidelines, annotation patterns, and page configurations -- **UI5 MCP Server** — provides UI5 Web Components guidance, control usage, and binding patterns +- **CAP MCP Server:** guides CDS entity modeling, service definitions, and backend patterns according to current CAP conventions +- **Fiori MCP Server:** ensures Fiori Elements applications follow SAP UX guidelines, annotation patterns, and page configurations +- **UI5 MCP Server:** provides UI5 Web Components guidance, control usage, and binding patterns Once I equipped Claude Code with these servers, it stopped guessing at SAP conventions. It checked. The agent queried the CAP MCP server before defining an entity, consulted the Fiori MCP server before configuring a list report page, and validated control usage against the UI5 MCP server before writing a view. @@ -52,14 +52,14 @@ Without the SAP MCP servers, the coding agent general knowledge produced 15 reco | Use `cuid` aspect for auto-generated keys | CAP MCP: composite keys are valid for ERP-sourced data | **Wrong.** Would break the data pipeline from source systems. | | Refactor to Composition pattern | CAP MCP: flat entities are fine for read-heavy ML result tables | **Wrong.** Unnecessary complexity. | -A 27% error rate on architectural decisions from a frontier model. These aren't cosmetic — renaming an entity breaks every OData URL, KPI facets on the wrong floorplan crash at runtime, and `cuid` would force a restructure of the entire data ingestion from SAP. +A 27% error rate on architectural decisions from a frontier model. These aren't cosmetic. Renaming an entity breaks every OData URL, KPI facets on the wrong floorplan crash at runtime, and `cuid` would force a restructure of the entire data ingestion from SAP. ### The Button That Did Nothing: Wiring Unbound Actions The Analyze Risks button was the worst offender. The agent had wired it via a `UI.DataFieldForAction` CDS annotation pointing to an unbound action: ```cds -// ❌ Without MCP — DataFieldForAction for unbound action +// ❌ Without MCP: DataFieldForAction for unbound action annotate RiskService.GLTransactions with @( UI.LineItem: [ // ... data field columns ... @@ -68,10 +68,10 @@ annotate RiskService.GLTransactions with @( ); ``` -The button rendered in the toolbar. Looked correct. But clicking it — nothing. No network request, no error, completely silent (a classic 'needle in a haystack' problem, as my colleague recently [wrote about](/news/2026-04-23-finding-the-needle-ai-assisted-debugging)). The Fiori MCP server flags this pattern: `UI.DataFieldForAction` with an unbound action has no execution path in OData V4. The runtime renders the button but never wires it. With MCP, the agent was guided to a manifest custom action instead: +The button rendered in the toolbar. Looked correct. But clicking it? Nothing. No network request, no error, completely silent (a classic 'needle in a haystack' problem, as my colleague recently [wrote about](/news/2026-04-23-finding-the-needle-ai-assisted-debugging)). The Fiori MCP server flags this pattern: `UI.DataFieldForAction` with an unbound action has no execution path in OData V4. The runtime renders the button but never wires it. With MCP, the agent was guided to a manifest custom action instead: ```json -// ✅ With Fiori MCP — manifest custom action for unbound actions +// ✅ With Fiori MCP: manifest custom action for unbound actions "controlConfiguration": { "@com.sap.vocabularies.UI.v1.LineItem": { "actions": { @@ -92,10 +92,10 @@ This one cost us real debugging time. The Fiori Elements list report loaded slow ``` Failed to drill-down into (...)/anomalyScoreResult, invalid segment: anomalyScoreResult ``` -Hundreds of these, one per row per render cycle. The root cause: the `after('READ')` handler only set virtual fields when a cached prediction existed. Before the user clicks "Analyze," the cache is empty — so the handler left the fields unset: +Hundreds of these, one per row per render cycle. The root cause: the `after('READ')` handler only set virtual fields when a cached prediction existed. Before the user clicks "Analyze," the cache is empty, so the handler left the fields unset: ```javascript -// ❌ Before — virtual fields missing from response when uncached +// ❌ Before: virtual fields missing from response when uncached this.after('READ', 'GLTransactions', (results) => { for (const row of results) { const cached = riskCache.get(`${row.DocumentNumber}_${row.LineItem}`); @@ -104,17 +104,17 @@ this.after('READ', 'GLTransactions', (results) => { row.criticality = cached.criticality; // ... } - // No else — fields left undefined + // No else: fields left undefined } }); ``` -OData V4's `$select` included these fields because the annotations reference them. But the server omitted them from the response payload because they were `undefined`. The client then failed the property drill-down on every row, every read — producing both the console noise and measurable performance degradation. +OData V4's `$select` included these fields because the annotations reference them. But the server omitted them from the response payload because they were `undefined`. The client then failed the property drill-down on every row, every read, producing both the console noise and measurable performance degradation. The fix is one `else` branch: ```javascript -// ✅ With Fiori MCP — virtual fields always present in OData response +// ✅ With Fiori MCP: virtual fields always present in OData response this.after('READ', 'GLTransactions', (results) => { for (const row of results) { const cached = riskCache.get(`${row.DocumentNumber}_${row.LineItem}`); @@ -133,7 +133,7 @@ This is exactly the kind of bug that sits at the intersection of CAP virtual fie Without Fiori MCP, the agent got the criticality mapping wrong: ```javascript -// ❌ Without MCP — agent guessed criticality values +// ❌ Without MCP: agent guessed criticality values const RISK_LABELS = { "Normal": { criticality: 0 // Agent assumed 0 = positive/green @@ -143,10 +143,10 @@ const RISK_LABELS = { }; ``` -The Fiori MCP server returned the actual OData V4 vocabulary values — `0` means Neutral (grey), `3` means Positive (green). One number, but it determines whether your risk dashboard communicates anything at all: +The Fiori MCP server returned the actual OData V4 vocabulary values. `0` means Neutral (grey), `3` means Positive (green). One number, but it determines whether your risk dashboard communicates anything at all: ```javascript -// ✅ With Fiori MCP — grounded in OData V4 vocabulary +// ✅ With Fiori MCP: grounded in OData V4 vocabulary const RISK_LABELS = { "Normal": { criticality: 3 // 3 = Positive/green per OData spec @@ -158,14 +158,14 @@ const RISK_LABELS = { ### CDS Enum Types and Naming Conventions -But the CAP MCP server went further than just correcting the value. It confirmed that CDS enum types are the right pattern for fields with a fixed set of valid states — and flagged a detail you won't find in most training data: **Integer enums require explicit values.** Omit them and the CDS compiler errors out: +But the CAP MCP server went further than just correcting the value. It confirmed that CDS enum types are the right pattern for fields with a fixed set of valid states, and flagged a detail you won't find in most training data: **Integer enums require explicit values.** Omit them and the CDS compiler errors out: ```cds -// ❌ Without MCP — raw types, no documentation, no compiler safety +// ❌ Without MCP: raw types, no documentation, no compiler safety virtual riskClassification : String; virtual criticality : Integer; -// ✅ With CAP MCP — enum types with explicit values +// ✅ With CAP MCP: enum types with explicit values type Criticality : Integer enum { Neutral = 0; Negative = 1; // red @@ -184,25 +184,25 @@ virtual riskClassification : RiskClassification; virtual criticality : Criticality; ``` -The enum is the source of truth — not a comment, not a wiki page, not tribal knowledge. +The enum is the source of truth, not a comment, not a wiki page, not tribal knowledge. -Naming conventions were another quiet catch. The 24 ML feature columns came from the Python model using `snake_case` — `anomaly_score`, `peer_amount_stddev`, `posting_delay_days`. But every CDS example the CAP MCP server returned used `camelCase`. That's not a style preference — it's how Fiori Elements generates labels. Rename `anomaly_score` to `anomalyScore` and the table header reads "Anomaly Score" automatically. Keep `snake_case` and you ship a professional risk dashboard with column headers like `anomaly_score`. +Naming conventions were another quiet catch. The 24 ML feature columns came from the Python model using `snake_case`: `anomaly_score`, `peer_amount_stddev`, `posting_delay_days`. But every CDS example the CAP MCP server returned used `camelCase`. That's not a style preference. It's how Fiori Elements generates labels. Rename `anomaly_score` to `anomalyScore` and the table header reads "Anomaly Score" automatically. Keep `snake_case` and you ship a professional risk dashboard with column headers like `anomaly_score`. ### Beyond MCP: Playwright and the Filter Bar -Code that compiles isn't code that works. **Playwright MCP** gave the agent eyes on the running application. Without browser access, the coding agent couldn't catch blank pages or broken wiring — I'd open the app, see the failure, paste the error back, and repeat. With Playwright, the agent launched a headless browser, took screenshots, and iterated without waiting for me. That's the concrete mechanism behind agentic engineering: coding agents that create, test, iterate, and debug *independently*. +Code that compiles isn't code that works. **Playwright MCP** gave the agent eyes on the running application. Without browser access, the coding agent couldn't catch blank pages or broken wiring. I'd open the app, see the failure, paste the error back, and repeat. With Playwright, the agent launched a headless browser, took screenshots, and iterated without waiting for me. That's the concrete mechanism behind agentic engineering: coding agents that create, test, iterate, and debug *independently*. -One of the first things Playwright caught was a usability problem no linter would flag. Twenty-four fields in the filter bar. My Financial Risk Analyzer has 24 ML feature columns — `anomalyScore`, `peerAmountStddev`, `postingDelayDays` — and every single one showed up as a filter option. Nobody filters risk transactions by `peerAmountStddev`. The Fiori MCP server pointed to `@UI.HiddenFilter`: +One of the first things Playwright caught was a usability problem no linter would flag. Twenty-four fields in the filter bar. My Financial Risk Analyzer has 24 ML feature columns (`anomalyScore`, `peerAmountStddev`, `postingDelayDays`) and every single one showed up as a filter option. Nobody filters risk transactions by `peerAmountStddev`. The Fiori MCP server pointed to `@UI.HiddenFilter`: ```cds -// ❌ Without Fiori MCP — feature columns clutter the filter bar (28 fields) +// ❌ Without Fiori MCP: feature columns clutter the filter bar (28 fields) anomalyScore @title: '{i18n>feat_anomalyScore}' @UI.Importance: #Low; -// ✅ With Fiori MCP — hidden from filters, still available in table personalization +// ✅ With Fiori MCP: hidden from filters, still available in table personalization anomalyScore @title: '{i18n>feat_anomalyScore}' @UI.Importance: #Low @UI.HiddenFilter; ``` -For date and amount fields, Fiori MCP pointed to `Capabilities.FilterRestrictions` — not something you'll find in a typical CAP tutorial: +For date and amount fields, Fiori MCP pointed to `Capabilities.FilterRestrictions`, not something you'll find in a typical CAP tutorial: ```cds // ✅ Fiori MCP: 'SingleRange' enables date/amount range pickers @@ -223,7 +223,7 @@ Four focused filter fields with proper range sliders instead of twenty-four. Tha In practice, four configuration layers work together: ```json -// .claude/settings.json — MCP server configuration +// .claude/settings.json: MCP server configuration { "mcpServers": { "cap": { "command": "npx", "args": ["@cap-js/mcp-server"] }, @@ -235,16 +235,16 @@ In practice, four configuration layers work together: ``` ```markdown -# AGENTS.md — SAP project instructions +# AGENTS.md: SAP project instructions For SAP-code specific, query SAP MCP servers before writing code. -- **/sap-cap** — queries CAP MCP for CDS entities, types, and services -- **/sap-fiori** — queries Fiori MCP for annotations and Fiori Elements config -- **/sap-ui5** — queries UI5 MCP for controllers, XML views, and controls +- **/sap-cap**: queries CAP MCP for CDS entities, types, and services +- **/sap-fiori**: queries Fiori MCP for annotations and Fiori Elements config +- **/sap-ui5**: queries UI5 MCP for controllers, XML views, and controls ``` -That's it. Edit a file under `app/`, the Fiori skill loads. Edit a service definition under `srv/`, the CAP skill loads. Edit a controller, the UI5 skill loads. No routing tables, no guessing which server to query — the path does the work. +That's it. Edit a file under `app/`, the Fiori skill loads. Edit a service definition under `srv/`, the CAP skill loads. Edit a controller, the UI5 skill loads. No routing tables, no guessing which server to query. The path does the work. It's important to keep in mind to only adopt MCP servers that have been verified from a security standpoint. Only install servers you trust. @@ -252,28 +252,28 @@ Still, correct SAP patterns aren't enough if the architecture is wrong. That's w ## Beyond Correctness: Architecture Principles -I reviewed the working Financial Risk Analyzer — the one MCP had gotten right on the first pass — and found an unscoped OData endpoint and no input validation. The SAP patterns were correct but security was missing. And security was just the first gap. Performance efficiency, reliability, scalability — the principles you apply before designing any enterprise solution weren't considered for the generated code. +I reviewed the working Financial Risk Analyzer, the one MCP had gotten right on the first pass, and found an unscoped OData endpoint and no input validation. The SAP patterns were correct but security was missing. And security was just the first gap. Performance efficiency, reliability, scalability: the principles you apply before designing any enterprise solution weren't considered for the generated code. I needed a system design methodology. Traditionally, I'd write the technical specification with a certain level of detail. Even documenting just the important parts would take time. That led me to a spec-driven-development tool (e.g., [superpowers](https://github.com/obra/superpowers)). Before I let the agent produce any code, an SDD tool when grounded with your architecture principles, will interview you and ensure there are no gaps from the security posture, performance budgets, reliability expectations, scalability constraints. Those were some of the fundamental pillars I'd define as an architect before designing any solution. -The difference was immediate. With a complete spec shaped by architecture principles along with SAP MCP skills, the agent didn't just write correct SAP code — it wrote code that reflected the non-functional requirements an enterprise application actually needs. Every session inherited that spec. No context rot. No re-explaining the same constraints. +The difference was immediate. With a complete spec shaped by architecture principles along with SAP MCP skills, the agent didn't just write correct SAP code. It wrote code that reflected the non-functional requirements an enterprise application actually needs. Every session inherited that spec. No context rot. No re-explaining the same constraints. -The agent built a working Risk Service — correct CDS entity, proper annotations, functional action handler. But it shipped without any authorization: +The agent built a working Risk Service with a correct CDS entity, proper annotations, functional action handler. But it shipped without any authorization: ```cds -// ❌ Without architecture principles — wide open +// ❌ Without architecture principles: wide open service RiskService { entity GLTransactions as projection on risk.GLTransactions; action analyzeRisks() returns array of GLTransactions; } ``` -Every authenticated user could trigger AI Core inference. The CAP MCP server confirmed the two-level pattern: service-level access control plus action-level role restriction. That's not something you discover from CDS syntax guides — it comes from thinking about who should access what: +Every authenticated user could trigger AI Core inference. The CAP MCP server confirmed the two-level pattern: service-level access control plus action-level role restriction. That's not something you discover from CDS syntax guides. It comes from thinking about who should access what: ```cds -// ✅ With CAP MCP — service + action level authorization +// ✅ With CAP MCP: service + action level authorization service RiskService @(requires: 'authenticated-user') { @readonly entity GLTransactions as projection on risk.GLTransactions; @@ -283,24 +283,24 @@ service RiskService @(requires: 'authenticated-user') { } ``` -`authenticated-user` locks down the OData endpoint. `RiskAnalyst` restricts the expensive AI Core call to users who actually need it. The MCP server didn't invent the security requirement — the architecture spec did. MCP made sure the implementation followed current CAP conventions. +`authenticated-user` locks down the OData endpoint. `RiskAnalyst` restricts the expensive AI Core call to users who actually need it. The MCP server didn't invent the security requirement. The architecture spec did. MCP made sure the implementation followed current CAP conventions. ## Secure the Code Your Agent Writes. It Won't Do It for You. Even after equipping your agent, we should **always assume code is untrusted**. MCP servers teach convention. An SDD tool improves the spec's quality. Still, the security scan flagged 53 vulnerabilities! The agent had scaffolded the project with older versions of the libraries instead of pulling `@latest`, and those older versions carried vulnerable dependencies underneath. -The spec never told the agent to use `@latest` or run `npm audit` after scaffolding. Security starts in the spec — install dependencies at their latest versions, audit what's underneath, and make that a gate before any application code is written. +The spec never told the agent to use `@latest` or run `npm audit` after scaffolding. Security starts in the spec: install dependencies at their latest versions, audit what's underneath, and make that a gate before any application code is written. -That covers what the agent produces. What about what you feed it? Anything it reads becomes model context — including files you didn't intend to share. List `.env`, `default-env.json`, and service keys in `.claudeignore` to keep them out of the agent's view. Only expose data the agent needs. Never enter personal or customer data into prompts. +That covers what the agent produces. What about what you feed it? Anything it reads becomes model context, including files you didn't intend to share. List `.env`, `default-env.json`, and service keys in `.claudeignore` to keep them out of the agent's view. Only expose data the agent needs. Never enter personal or customer data into prompts. ## Protect What Your Agent Sends -When the coding agent sends code to a model provider, it carries business logic and intellectual property. I need a contractual guarantee that none of it gets used for training or sold to a third party. Going direct to model providers doesn't give me that through a single agreement. Running through SAP's **Gen AI Hub** does — SAP's agreements with providers ensure your data stays yours. +When the coding agent sends code to a model provider, it carries business logic and intellectual property. I need a contractual guarantee that none of it gets used for training or sold to a third party. Going direct to model providers doesn't give me that through a single agreement. Running through SAP's **Gen AI Hub** does. SAP's agreements with providers ensure your data stays yours. -That same infrastructure solves a second problem. Agentic workflows benefit from multiple frontier models — strengths vary by task, and a second opinion from a different model is a real advantage. **LiteLLM** gives me a single gateway into Gen AI Hub: one integration point, one SAP API key, every frontier model available immediately, at volume pricing SAP negotiates with hyperscalers. Behind that gateway, Gen AI Hub handles content filters and PII masking on every request — guardrails I'd otherwise have built myself. +That same infrastructure solves a second problem. Agentic workflows benefit from multiple frontier models. Strengths vary by task, and a second opinion from a different model is a real advantage. **LiteLLM** gives me a single gateway into Gen AI Hub: one integration point, one SAP API key, every frontier model available immediately, at volume pricing SAP negotiates with hyperscalers. Behind that gateway, Gen AI Hub handles content filters and PII masking on every request, guardrails I'd otherwise have built myself. ```yaml -# litellm_config.yaml — single gateway to SAP Gen AI Hub +# litellm_config.yaml: single gateway to SAP Gen AI Hub model_list: - model_name: claude-sonnet litellm_params: @@ -314,33 +314,33 @@ The full-stack picture: **Fiori** on the frontend, **CAP** on the backend, **Gen ## What This Means For Your Team -The prototypes made one thing clear: agents write code fast, but they're working from training data that's already stale. SDKs and API specs change. The code written by AI compiles, but breaks at runtime — and AI can't fix them easily without several iterations leading to an enormous waste of time and effort. +The prototypes made one thing clear: agents write code fast, but they're working from training data that's already stale. SDKs and API specs change. The code written by AI compiles, but breaks at runtime, and AI can't fix them easily without several iterations leading to an enormous waste of time and effort. SAP's extension ecosystem has always been powerful, and it has always demanded deep professional knowledge to get right. That knowledge barrier is real. It is why SAP projects take months and why extension backlogs grow faster than teams can deliver. -MCP servers do not eliminate that barrier. They democratize access to it. The servers encode the same best practices that senior SAP architects carry — CDS conventions, annotation semantics, authorization patterns, controller extension boundaries. An agent equipped with these servers reflects that expertise, even when the developer driving the session is building their first Fiori app. +MCP servers do not eliminate that barrier. They democratize access to it. The servers encode the same best practices that senior SAP architects carry: CDS conventions, annotation semantics, authorization patterns, controller extension boundaries. An agent equipped with these servers reflects that expertise, even when the developer driving the session is building their first Fiori app. Here's what that workflow looks like end to end: ![The agentic development loop, end to end](img/2026-04-27/sequence-sdd-loop.webp) - To see the complete implementation — CAP backend, Fiori Elements frontend, and AI Core integration — explore the [source code on GitHub](https://github.com/SAP-samples/cap-agentic-engineered). Your SAP investment already includes the platform. The question is whether you equip your agents to use it. + To see the complete implementation (CAP backend, Fiori Elements frontend, and AI Core integration) explore the [source code on GitHub](https://github.com/SAP-samples/cap-agentic-engineered). Your SAP investment already includes the platform. The question is whether you equip your agents to use it. ## References **SAP MCP Servers** -- [CAP MCP Server](https://community.sap.com/t5/technology-blog-posts-by-sap/boost-your-cap-development-with-ai-introducing-the-mcp-server-for-cap/ba-p/14202849) — MCP server for SAP Cloud Application Programming Model (CAP) development -- [Fiori MCP Server](https://community.sap.com/t5/technology-blog-posts-by-sap/sap-fiori-tools-update-first-release-of-the-sap-fiori-mcp-server-for/ba-p/14204694) — Helps AI models create and modify SAP Fiori applications -- [UI5 MCP Server](https://community.sap.com/t5/technology-blog-posts-by-sap/give-your-ai-agent-some-tools-introducing-the-ui5-mcp-server/ba-p/14200825) — UI5 Web Components development assistance +- [CAP MCP Server](https://community.sap.com/t5/technology-blog-posts-by-sap/boost-your-cap-development-with-ai-introducing-the-mcp-server-for-cap/ba-p/14202849): MCP server for SAP Cloud Application Programming Model (CAP) development +- [Fiori MCP Server](https://community.sap.com/t5/technology-blog-posts-by-sap/sap-fiori-tools-update-first-release-of-the-sap-fiori-mcp-server-for/ba-p/14204694): Helps AI models create and modify SAP Fiori applications +- [UI5 MCP Server](https://community.sap.com/t5/technology-blog-posts-by-sap/give-your-ai-agent-some-tools-introducing-the-ui5-mcp-server/ba-p/14200825): UI5 Web Components development assistance **Agentic Engineering & Spec-Driven Development** -- [Finding the Needle: AI-Assisted Debugging Across Thousands of Lines and Megabytes of Logs](/news/2026/04/23/finding-the-needle-ai-assisted-debugging) — How a coding agent resolved an intermittent auth failure across Kubernetes and SAP AI Core in 60 minutes instead of 12+ hours, by correlating logs and tracing credential conflicts no single engineer could spot at once -- [superpowers](https://github.com/obra/superpowers) — Spec-driven development framework that guides coding agents through structured requirements gathering -- [GSD](https://github.com/gsd-build/get-shit-done) — Meta-prompting, context engineering, and spec-driven development system for coding agents -- [OpenSpec](https://github.com/Fission-AI/OpenSpec) — Spec-driven development tool that adds a lightweight specification layer before code is written +- [Finding the Needle: AI-Assisted Debugging Across Thousands of Lines and Megabytes of Logs](/news/2026/04/23/finding-the-needle-ai-assisted-debugging): How a coding agent resolved an intermittent auth failure across Kubernetes and SAP AI Core in 60 minutes instead of 12+ hours, by correlating logs and tracing credential conflicts no single engineer could spot at once +- [superpowers](https://github.com/obra/superpowers): Spec-driven development framework that guides coding agents through structured requirements gathering +- [GSD](https://github.com/gsd-build/get-shit-done): Meta-prompting, context engineering, and spec-driven development system for coding agents +- [OpenSpec](https://github.com/Fission-AI/OpenSpec): Spec-driven development tool that adds a lightweight specification layer before code is written **Developer Tooling MCP Servers** -- [Playwright MCP](https://github.com/microsoft/playwright-mcp) — Headless browser automation for coding agents — navigate, screenshot, and verify UI +- [Playwright MCP](https://github.com/microsoft/playwright-mcp): Headless browser automation for coding agents. Navigate, screenshot, and verify UI **SAP Platform** -- [LiteLLM SAP Provider](https://docs.litellm.ai/docs/providers/sap) — Gateway to SAP AI Foundation via Gen AI Hub -- [Claude Code Documentation](https://code.claude.com/docs) — Official Claude Code docs, skills, MCP, and quickstart guides \ No newline at end of file +- [LiteLLM SAP Provider](https://docs.litellm.ai/docs/providers/sap): Gateway to SAP AI Foundation via Gen AI Hub +- [Claude Code Documentation](https://code.claude.com/docs): Official Claude Code docs, skills, MCP, and quickstart guides \ No newline at end of file From 433af4a8285da854cab7075fcb6c7913b539bfbd Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Basseville Date: Tue, 28 Apr 2026 08:16:44 +0200 Subject: [PATCH 02/14] page is now unlisted (#981) --- docs/north-star-arch/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/north-star-arch/readme.md b/docs/north-star-arch/readme.md index f7e5fbcfb6..cbfb81a6b3 100644 --- a/docs/north-star-arch/readme.md +++ b/docs/north-star-arch/readme.md @@ -39,12 +39,12 @@ hide_title: false toc_min_heading_level: 2 toc_max_heading_level: 4 draft: false -unlisted: false +unlisted: true contributors: discussion: last_update: author: cernus76 - date: 2026-04-21 + date: 2026-04-27 --- ## This content will be available soon \ No newline at end of file From e76ce367626a7398202fb7e9dfc254fc7b792d1e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Basseville Date: Tue, 28 Apr 2026 14:45:03 +0200 Subject: [PATCH 03/14] fix article gui (#997) --- news/2026-04-27-agentic-engineering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/2026-04-27-agentic-engineering.md b/news/2026-04-27-agentic-engineering.md index 0c132ce3a5..358a6b024f 100644 --- a/news/2026-04-27-agentic-engineering.md +++ b/news/2026-04-27-agentic-engineering.md @@ -68,7 +68,7 @@ annotate RiskService.GLTransactions with @( ); ``` -The button rendered in the toolbar. Looked correct. But clicking it? Nothing. No network request, no error, completely silent (a classic 'needle in a haystack' problem, as my colleague recently [wrote about](/news/2026-04-23-finding-the-needle-ai-assisted-debugging)). The Fiori MCP server flags this pattern: `UI.DataFieldForAction` with an unbound action has no execution path in OData V4. The runtime renders the button but never wires it. With MCP, the agent was guided to a manifest custom action instead: +The button rendered in the toolbar. Looked correct. But clicking it? Nothing. No network request, no error, completely silent (a classic 'needle in a haystack' problem, as my colleague recently [wrote about](news/2026-04-23-finding-the-needle-ai-assisted-debugging.md)). The Fiori MCP server flags this pattern: `UI.DataFieldForAction` with an unbound action has no execution path in OData V4. The runtime renders the button but never wires it. With MCP, the agent was guided to a manifest custom action instead: ```json // ✅ With Fiori MCP: manifest custom action for unbound actions From eb32aac70396964299419832acc720e94d55e0aa Mon Sep 17 00:00:00 2001 From: Kay Schmitteckert <72558779+kay-schmitteckert@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:04:59 +0200 Subject: [PATCH 04/14] update ra0029 diagrams (#994) Co-authored-by: Pierre-Olivier Basseville --- .../1-a2a-and-mcp/drawio/architecture.drawio | 82 ++++++++++--- .../drawio/architecture.drawio | 92 +++++++++++---- .../drawio/architecture.drawio | 111 +++++++++++++----- .../RA0029/drawio/architecture.drawio | 80 ++++++++++--- 4 files changed, 275 insertions(+), 90 deletions(-) diff --git a/docs/ref-arch/RA0029/1-a2a-and-mcp/drawio/architecture.drawio b/docs/ref-arch/RA0029/1-a2a-and-mcp/drawio/architecture.drawio index 750003d8c9..9b58790c06 100644 --- a/docs/ref-arch/RA0029/1-a2a-and-mcp/drawio/architecture.drawio +++ b/docs/ref-arch/RA0029/1-a2a-and-mcp/drawio/architecture.drawio @@ -1,6 +1,6 @@ - + @@ -80,28 +80,28 @@ - + - + - + - + - + - + - + @@ -122,8 +122,8 @@ - - + + @@ -150,6 +150,9 @@ + + + @@ -214,27 +217,30 @@ - + - + - - + + - + - + + + + @@ -331,11 +337,12 @@ - + - + + @@ -348,6 +355,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/ref-arch/RA0029/4-integrate-ai-agents-with-joule/drawio/architecture.drawio b/docs/ref-arch/RA0029/4-integrate-ai-agents-with-joule/drawio/architecture.drawio index bc07ce2e7d..459d82ab15 100644 --- a/docs/ref-arch/RA0029/4-integrate-ai-agents-with-joule/drawio/architecture.drawio +++ b/docs/ref-arch/RA0029/4-integrate-ai-agents-with-joule/drawio/architecture.drawio @@ -1,6 +1,6 @@ - + @@ -80,28 +80,28 @@ - + - + - + - + - + - + - + @@ -118,12 +118,12 @@ - + - - + + @@ -150,6 +150,9 @@ + + + @@ -207,34 +210,37 @@ - + - + - + - - + + - + - + + + + @@ -242,7 +248,7 @@ - + @@ -268,7 +274,7 @@ - + @@ -331,23 +337,63 @@ - + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/ref-arch/RA0029/5-integrate-joule-agents-and-tools-into-your-ecosystem/drawio/architecture.drawio b/docs/ref-arch/RA0029/5-integrate-joule-agents-and-tools-into-your-ecosystem/drawio/architecture.drawio index 842ca502a9..38098be9af 100644 --- a/docs/ref-arch/RA0029/5-integrate-joule-agents-and-tools-into-your-ecosystem/drawio/architecture.drawio +++ b/docs/ref-arch/RA0029/5-integrate-joule-agents-and-tools-into-your-ecosystem/drawio/architecture.drawio @@ -1,6 +1,6 @@ - + @@ -28,7 +28,7 @@ - + @@ -80,28 +80,28 @@ - + - + - + - + - + - + - + @@ -118,12 +118,12 @@ - + - - + + @@ -150,6 +150,9 @@ + + + @@ -207,34 +210,37 @@ - + - + - + - - + + - + - + + + + @@ -268,18 +274,10 @@ - - - - - - - - - + @@ -291,7 +289,7 @@ - + @@ -333,23 +331,72 @@ - + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/ref-arch/RA0029/drawio/architecture.drawio b/docs/ref-arch/RA0029/drawio/architecture.drawio index 372c6505d9..92d05c9011 100644 --- a/docs/ref-arch/RA0029/drawio/architecture.drawio +++ b/docs/ref-arch/RA0029/drawio/architecture.drawio @@ -1,6 +1,6 @@ - + @@ -80,28 +80,28 @@ - + - + - + - + - + - + - + @@ -122,8 +122,8 @@ - - + + @@ -150,6 +150,9 @@ + + + @@ -214,27 +217,30 @@ - + - - + + - + - + + + + @@ -331,11 +337,12 @@ - + - + + @@ -348,6 +355,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e128f918a066fc2fad61f11095aef1a3be311771 Mon Sep 17 00:00:00 2001 From: Uma Anbazhagan <101162505+anbazhagan-uma@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:30:18 +0530 Subject: [PATCH 05/14] TAG Updates to AI and Data RAs (#1005) * EDA Minor Changes * Hide Joule-S4 Page - Content Refresh Planned * Tag Updates for AI and Data RAs Tag Updates for AI and Data RAs --- docs/ref-arch/RA0001/1-event-driven-arch/readme.md | 1 - docs/ref-arch/RA0001/2-design-considerations/readme.md | 1 - docs/ref-arch/RA0001/readme.md | 6 ++++-- docs/ref-arch/RA0005/2-semantic-search/readme.md | 1 - docs/ref-arch/RA0007/2-mt-benefits/readme.md | 2 +- docs/ref-arch/RA0007/3-mt-models/readme.md | 2 +- docs/ref-arch/RA0007/4-mt-lifecycle/readme.md | 2 +- docs/ref-arch/RA0007/5-mt-architecture/readme.md | 2 +- docs/ref-arch/RA0007/6-mt-tco/readme.md | 1 - docs/ref-arch/RA0007/7-mt-authentication/readme.md | 1 - docs/ref-arch/RA0007/readme.md | 1 - .../RA0013/2-intelligent-applications-by-sap/readme.md | 1 - .../3-intelligent-applications-greenfield-bdc/readme.md | 1 - .../RA0019/1-authentication-and-single-sign-on/readme.md | 1 - docs/ref-arch/RA0019/2-identity-lifecycle/readme.md | 1 - docs/ref-arch/RA0019/3-authorization-design/readme.md | 1 - 16 files changed, 8 insertions(+), 17 deletions(-) diff --git a/docs/ref-arch/RA0001/1-event-driven-arch/readme.md b/docs/ref-arch/RA0001/1-event-driven-arch/readme.md index d6b2c19ba4..cc1ef9dcc9 100644 --- a/docs/ref-arch/RA0001/1-event-driven-arch/readme.md +++ b/docs/ref-arch/RA0001/1-event-driven-arch/readme.md @@ -20,7 +20,6 @@ image: img/ac-soc-med.png tags: - azure - aws - - genai - integration - appdev - eda diff --git a/docs/ref-arch/RA0001/2-design-considerations/readme.md b/docs/ref-arch/RA0001/2-design-considerations/readme.md index 8bc8de89dc..93d10eb593 100644 --- a/docs/ref-arch/RA0001/2-design-considerations/readme.md +++ b/docs/ref-arch/RA0001/2-design-considerations/readme.md @@ -20,7 +20,6 @@ image: img/ac-soc-med.png tags: - azure - aws - - genai - integration - appdev - eda diff --git a/docs/ref-arch/RA0001/readme.md b/docs/ref-arch/RA0001/readme.md index cc87235d23..854c078461 100644 --- a/docs/ref-arch/RA0001/readme.md +++ b/docs/ref-arch/RA0001/readme.md @@ -11,7 +11,10 @@ sidebar_custom_props: title: Designing Event-Driven Applications description: >- Guidance for developing applications based on Event-Driven Architecture (EDA) - patterns and Cloud Application Programming (CAP) framework. + patterns and Cloud Application Programming (CAP) framework. EDA is a required + architecture pattern for building loosely coupled, scalable, and resilient + applications that react to real-time business events across distributed + systems. keywords: - sap - event-driven applications @@ -25,7 +28,6 @@ image: img/ac-soc-med.png tags: - azure - aws - - genai - integration - appdev - eda diff --git a/docs/ref-arch/RA0005/2-semantic-search/readme.md b/docs/ref-arch/RA0005/2-semantic-search/readme.md index 3becd5edc0..bab80d0b9f 100644 --- a/docs/ref-arch/RA0005/2-semantic-search/readme.md +++ b/docs/ref-arch/RA0005/2-semantic-search/readme.md @@ -21,7 +21,6 @@ tags: - azure - gcp - genai - - data hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0007/2-mt-benefits/readme.md b/docs/ref-arch/RA0007/2-mt-benefits/readme.md index 770a092fac..6f557a7bcd 100644 --- a/docs/ref-arch/RA0007/2-mt-benefits/readme.md +++ b/docs/ref-arch/RA0007/2-mt-benefits/readme.md @@ -17,7 +17,7 @@ image: img/ac-soc-med.png tags: - appdev - cap - - genai + hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0007/3-mt-models/readme.md b/docs/ref-arch/RA0007/3-mt-models/readme.md index a091855808..38828373de 100644 --- a/docs/ref-arch/RA0007/3-mt-models/readme.md +++ b/docs/ref-arch/RA0007/3-mt-models/readme.md @@ -20,7 +20,7 @@ image: img/ac-soc-med.png tags: - appdev - cap - - genai + hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0007/4-mt-lifecycle/readme.md b/docs/ref-arch/RA0007/4-mt-lifecycle/readme.md index ec92beda61..176b823fab 100644 --- a/docs/ref-arch/RA0007/4-mt-lifecycle/readme.md +++ b/docs/ref-arch/RA0007/4-mt-lifecycle/readme.md @@ -16,7 +16,7 @@ image: img/ac-soc-med.png tags: - appdev - cap - - genai + hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0007/5-mt-architecture/readme.md b/docs/ref-arch/RA0007/5-mt-architecture/readme.md index 13437765d9..e9e50cc51d 100644 --- a/docs/ref-arch/RA0007/5-mt-architecture/readme.md +++ b/docs/ref-arch/RA0007/5-mt-architecture/readme.md @@ -16,7 +16,7 @@ image: img/ac-soc-med.png tags: - appdev - cap - - genai + hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0007/6-mt-tco/readme.md b/docs/ref-arch/RA0007/6-mt-tco/readme.md index c5c3ad6c8a..62cafd3698 100644 --- a/docs/ref-arch/RA0007/6-mt-tco/readme.md +++ b/docs/ref-arch/RA0007/6-mt-tco/readme.md @@ -17,7 +17,6 @@ image: img/ac-soc-med.png tags: - appdev - cap - - genai - security hide_table_of_contents: false hide_title: false diff --git a/docs/ref-arch/RA0007/7-mt-authentication/readme.md b/docs/ref-arch/RA0007/7-mt-authentication/readme.md index 9dddc43907..3b7a945880 100644 --- a/docs/ref-arch/RA0007/7-mt-authentication/readme.md +++ b/docs/ref-arch/RA0007/7-mt-authentication/readme.md @@ -16,7 +16,6 @@ image: img/ac-soc-med.png tags: - appdev - cap - - genai hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0007/readme.md b/docs/ref-arch/RA0007/readme.md index 6f107e6ff1..06d4bd92ce 100644 --- a/docs/ref-arch/RA0007/readme.md +++ b/docs/ref-arch/RA0007/readme.md @@ -20,7 +20,6 @@ image: img/ac-soc-med.png tags: - appdev - cap - - genai hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0013/2-intelligent-applications-by-sap/readme.md b/docs/ref-arch/RA0013/2-intelligent-applications-by-sap/readme.md index 9f77fc7a16..805aab3ae6 100644 --- a/docs/ref-arch/RA0013/2-intelligent-applications-by-sap/readme.md +++ b/docs/ref-arch/RA0013/2-intelligent-applications-by-sap/readme.md @@ -22,7 +22,6 @@ tags: - azure - gcp - bdc - - genai hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0013/3-intelligent-applications-greenfield-bdc/readme.md b/docs/ref-arch/RA0013/3-intelligent-applications-greenfield-bdc/readme.md index 4562a58453..6901a0b7a7 100644 --- a/docs/ref-arch/RA0013/3-intelligent-applications-greenfield-bdc/readme.md +++ b/docs/ref-arch/RA0013/3-intelligent-applications-greenfield-bdc/readme.md @@ -24,7 +24,6 @@ tags: - azure - gcp - bdc - - genai hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0019/1-authentication-and-single-sign-on/readme.md b/docs/ref-arch/RA0019/1-authentication-and-single-sign-on/readme.md index 9771e262c2..68657c9386 100644 --- a/docs/ref-arch/RA0019/1-authentication-and-single-sign-on/readme.md +++ b/docs/ref-arch/RA0019/1-authentication-and-single-sign-on/readme.md @@ -18,7 +18,6 @@ sidebar_label: Authentication and Single Sign On image: img/ac-soc-med.png tags: - security - - genai hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0019/2-identity-lifecycle/readme.md b/docs/ref-arch/RA0019/2-identity-lifecycle/readme.md index 2af48d7ae1..14d048548b 100644 --- a/docs/ref-arch/RA0019/2-identity-lifecycle/readme.md +++ b/docs/ref-arch/RA0019/2-identity-lifecycle/readme.md @@ -19,7 +19,6 @@ sidebar_label: Identity Lifecycle image: img/ac-soc-med.png tags: - security - - genai hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 diff --git a/docs/ref-arch/RA0019/3-authorization-design/readme.md b/docs/ref-arch/RA0019/3-authorization-design/readme.md index 1af181689b..1f1b74786e 100644 --- a/docs/ref-arch/RA0019/3-authorization-design/readme.md +++ b/docs/ref-arch/RA0019/3-authorization-design/readme.md @@ -18,7 +18,6 @@ sidebar_label: Authorization Design image: img/ac-soc-med.png tags: - security - - genai hide_table_of_contents: false hide_title: false toc_min_heading_level: 2 From 267905d808cab8752628e68a7cf3b290cf447f39 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Basseville Date: Thu, 30 Apr 2026 07:02:01 +0200 Subject: [PATCH 06/14] update article and new article (#1001) * update article and new article * fix article name --------- Co-authored-by: Navya Khurana --- news/2026-04-27-agentic-engineering.md | 4 +- news/2026-04-28-live-ai-use-case.md | 92 ++++++++++++++++++ .../image-gingershot-1920-x-600.jpeg | Bin 0 -> 118149 bytes 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 news/2026-04-28-live-ai-use-case.md create mode 100644 news/img/2026-04-28/image-gingershot-1920-x-600.jpeg diff --git a/news/2026-04-27-agentic-engineering.md b/news/2026-04-27-agentic-engineering.md index 358a6b024f..5237794f15 100644 --- a/news/2026-04-27-agentic-engineering.md +++ b/news/2026-04-27-agentic-engineering.md @@ -10,10 +10,10 @@ authors: [guilherme-segantini] **TL;DR:** Coding agents excel at writing code fast, but code produced fast is not the same as code that truly works. Debugging it after the fact is often the most expensive way to utilize AI. Every fix cycle with the agent, waiting for a new attempt, then testing it again can quickly turn enthusiasm into frustration. Our agentic code quality funnel changed the equation. -![The Agentic Code Quality Funnel](img/2026-04-27/agentic-code-quality-funnel.webp) - +![The Agentic Code Quality Funnel](img/2026-04-27/agentic-code-quality-funnel.webp) + Let's use Claude Code to build an SAP Extensions app. With a grounded spec, it built our prototype in less than thirty minutes. A **Financial Risk Analyzer** built as a CAP backend with a Fiori Elements frontend that reads GL transaction data, runs anomaly detection through SAP AI Core, and surfaces risk classifications in a List Report. The full source is on [GitHub](https://github.com/SAP-samples/cap-agentic-engineered). Excitement is not just getting code done fast but code that's reliable and truly works for the enterprise. Claude fully tested the app. Then I opened it and got a blank page. Several debugging rounds later the page showed up but columns came up empty, buttons did nothing, and the risk data never reached the frontend. It was not one bug. Several issues including deprecated annotations the runtime silently ignored, naming mismatches between the controller and what Fiori Elements actually looks for, and OData wiring that looked correct but had no execution path in V4. diff --git a/news/2026-04-28-live-ai-use-case.md b/news/2026-04-28-live-ai-use-case.md new file mode 100644 index 0000000000..fe0c3ac7ae --- /dev/null +++ b/news/2026-04-28-live-ai-use-case.md @@ -0,0 +1,92 @@ +--- +title: Live AI Use Cases Show How SAP Delivers Trusted Orchestration and Smarter Execution for Manufacturing and Supply Chain Management +description: A ginger shot, fresh off the line, was the first stop for many visitors at SAP’s booth at Hannover Messe. But the real takeaway was seeing AI in action. +keywords: ["Hannover Messe", "manufacturing", "supply chain", "AI", "robotics", "SAP", "Joule", "agentic AI", "Industry 4.0", "automation", "logistics", "smart manufacturing"] +hide_table_of_contents: false +spotlight_image: img/2026-04-28/image-gingershot-1920-x-600.jpeg +date: 2026-04-28 +authors: [AlexaMacDonald] +--- + +**A ginger shot, fresh off the line, was the first stop for many visitors at SAP’s booth at Hannover Messe. But the real takeaway was seeing AI in action. From mixing the ginger shot to packaging and warehouse delivery, visitors saw how SAP is turning AI ambition into real-world manufacturing execution, delivering end-to-end supply chain management processes, and building the resilience every manufacturer needs.** + + + +![A ginger shot](img/2026-04-28/image-gingershot-1920-x-600.jpeg) + +Held from April 20–24, Hannover Messe is the world’s leading industrial trade fair. + +On day one, Christian Klein, CEO of SAP SE, stopped by the SAP booth before joining German Chancellor Friedrich Merz and other industrial leaders on the center stage to discuss the importance of moving from AI ambition to real-world execution. + +And visitors to the SAP booth experienced that shift firsthand, following the production of the ginger shot. + +Packaged in a neat blue box, the ginger shot was refreshing but that wasn’t the only takeaway. The real takeaway was how SAP’s new set of AI-powered manufacturing and supply chain innovations can deliver connected [end-to-end supply chain business processes powered by AI](https://www.sap.com/products/scm.html). + +## **Supply chain orchestration** + +From AI and data and then using SAP’s agentic AI, visitors saw what supply chain orchestration looks like in practice. SAP uses [agentic AI](https://www.sap.com/products/technology-platform/integration-suite/agentic-ai.html), trusted data, and applications to help manufacturers sense, analyze, and act in real time. + +Orchestrate your supply chain as a single, connected system using AI and data to sense, analyze, and act in real time + +[Learn more](https://www.sap.com/products/scm.html) + +At the booth, visitors saw human operators interact with an [ANYbotics](https://news.sap.com/2026/03/anybotics-industrial-inspections-into-business-insights/) robot through Joule using natural language to run live, remote field service inspections; Uhlmann’s high-tech glass-fronted packing machine, PacXplorer, in action opposite the CNC machine from DMG MORI that was creating spare parts for the PacXplorer; and, at end of the production cycle, AIMBO’s robot handling the picking and packing of the ginger shot. Both AIMBO and ANYbotics are part of SAP’s growing network of [physical AI partnerships](https://news.sap.com/2025/11/sap-physical-ai-partnerships-new-robotics-pilots/). + +In addition to many tours held in German and English, day one also saw tours in Japanese, Chinese, and Portuguese—Brazil was the partner country at Hannover Messe 2026. + +Equipped with headphones to block out the noise of the crowds at the booth, visitors heard how SAP’s AI can deliver trusted orchestration and smarter execution for [manufacturing](https://www.sap.com/products/scm/manufacturing.html?pttid=7758&campaigncode=crm-ya22-int-1517076&source=ppc-de-googleads-search-21171603258-164767230150-scm_dss-x-x-x&gclsrc=aw.ds&gad_source=1&gad_campaignid=21171603258&gbraid=0AAAAAolt795Pl54SRzOi9SB4b3xhiUiy4&gclid=CjwKCAjw46HPBhAMEiwASZpLRMbfIbFvB28xNJSlrkYHi3-FPGEf9U0s3tnUUQhFKLnjhGmJzlRZmhoCIJ0QAvD_BwE) and [supply chain management](https://www.sap.com/products/scm.html?pttid=7758&campaigncode=crm-ya22-int-1517076&source=ppc-de-googleads-search-21171603258-164767230630-scm_dss-x-x-x&gclsrc=aw.ds&gad_source=1&gad_campaignid=21171603258&gbraid=0AAAAAolt795Pl54SRzOi9SB4b3xhiUiy4&gclid=CjwKCAjw46HPBhAMEiwASZpLRBS2lHDd_-2OAy0EAeN-IHqO8wy7Tzul3SgSyw9xhGTfTWIdLbofbxoCMlgQAvD_BwE). + +## **Live AI use cases demonstrate functions and benefits** + +### **Operations and insights use case** + +Here, visitors experienced SAP’s vision of supply chain orchestration. In this vision, supply chain orchestration acts as the nerve center of the enterprise. It uses external alerts such as natural disasters, port congestions, or supplier routes to optimize enterprise logistics and planning using agents. + +Benefits can include faster response times with AI-assisted monitoring and automated alerts; improved decision-making with data-driven, operational decisions powered by integrated business AI capabilities; and seamless integration with end-to-end connectivity from supply chain planning through to manufacturing execution and quality control. + +**Top AI functions** + +- [Production Planning and Operating Agent](https://www.sap.com/products/artificial-intelligence/ai-agents/agent-use-cases.html#scm) can assist with order release and real-time monitoring. +- A physical AI robot inspects hazards, analyzes inspection data, and identifies root causes. +- Supply optimization analysis helps summarize insights, analyze, and explain the time-series optimization planning run. + +### **Smart production use case** + +DMG MORI demonstrated production at its CNC machine—as part of an end-to-end process—from engineering to planning to production. + +As the white robotic arm of the CNC machine silently moved the pusher spare part after the milling process, visitors learned about the benefits of integration, from design to tool management, CNC programs to [SAP Digital Manufacturing](http://www.sap.com/dm) as part of a seamless, integrated process. The production operator dashboard offers the operator on the machine AI capabilities and insights to operational and maintenance information. + +The process then continues through to logistics execution with SAP Logistics Management, which helps combine warehousing and transportation capabilities for smaller warehouses.  This features an AI-powered logistics assistant that can cut through the noise, automatically gathering, summarizing, and prioritizing critical shipment information. It can also provide real-time shipping prices, bringing to life trusted orchestration and smarter execution. + +**Top AI functions** + +- [Joule with SAP Logistics Management](https://www.sap.com/products/scm/joule-with-sap-logistics-management.html) uses natural language to help streamline warehouse and transportation operations. +- [Joule’s integrated AI agents](https://www.sap.com/products/artificial-intelligence/ai-agents.html?pttid=7756&campaigncode=crm-ya22-int-1517075&source=ppc-de-googleads-search-21156823187-179484269229-clouderpgrow_s4s-x-x-x&gclsrc=aw.ds&gad_source=1&gad_campaignid=21156823187&gbraid=0AAAAAoT3lP9jHA312OPvRkZmst5xspFg0&gclid=CjwKCAjwhqfPBhBWEiwAZo196jvN5AU7qPiu396zy2KzUZSydByyj0QW6IjqGBuYweSQ1KNjzG4zvhoChKgQAvD_BwE) can provide manufacturing information and support decision-making throughout the workflow. + +### **Intelligent packaging use case** + +Uhlmann’s PacXplorer and SAP highlighted a fully integrated, high-speed packaging line from SAP S/4HANA, to SAP Digital Manufacturing, down to Uhlmann’s automation layer to produce the packaged ginger shot. The ginger shots were moved away from the line by a mobile autonomous robot from Symovo. This use case showed visitors how SAP supports regulated industries such as pharma and life sciences.   + +Highlighted benefits include increased operational speed with higher throughput thanks to decreased order processing time, built-in regulatory compliance, reduced manual intervention, inventory transparency, and data integrity across the entire production chain. + +**Top AI functions** + +- Condition monitoring-led services can enhance asset uptime and service efficiency by combining AI-driven insights and seamless collaboration across the service ecosystem. +- AI-empowered flow analysis enables quick process modeling and engineering optimization. +- Intelligent exception handling is embedded in agent-driven processes. +- Joule’s integrated AI agents can support decision-making throughout the workflow. +- Joule can help power order and line insights. + +### **Humanoid use case** + +At the final stop before getting their ginger shots, visitors watched an intelligent humanoid robot perform physical tasks at the end of the packaging line, bridging the gap between digital planning and physical execution, highlighting SAP’s Project Embodied AI. + +Benefits of humanoids include increased operational speed with higher throughput due to a decreased order processing time; increased business uptime and cost efficiency especially in areas dangerous or difficult for humans; inventory transparency with real-time data integrity across the warehouse; and physical-digital alignment eliminating misalignment between planning and execution. + +**Top AI functions** + +- Joule and Joule Studio can enable robots to understand the physical world, make autonomous decisions, and learn from their environment for smarter operations. + +## **More than a quick refuel** + +At the end of their visit, visitors got so much more than a quick refuel to slake their thirst. Following the creation of the ginger shot from recipe development and planning to production with mixing, filling, and packing, visitors came away with a clear understanding of how SAP is connecting insight to execution with trusted orchestration and smarter execution. And, it is this trusted orchestration and smarter execution that is building the resilience every manufacturer needs in today’s world. diff --git a/news/img/2026-04-28/image-gingershot-1920-x-600.jpeg b/news/img/2026-04-28/image-gingershot-1920-x-600.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..f69ed0b60d90c7b4680f01ddf699eeb5a12b1126 GIT binary patch literal 118149 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

-iJnq*-x*8j!z$ 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 literal 0 HcmV?d00001 From fc26fac094c0e8fa69edd6cb84ed629a488e5206 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Basseville Date: Thu, 30 Apr 2026 11:49:58 +0200 Subject: [PATCH 07/14] update packages (#1009) --- package-lock.json | 530 +++++++++++++++++++++++----------------------- package.json | 3 +- 2 files changed, 272 insertions(+), 261 deletions(-) diff --git a/package-lock.json b/package-lock.json index b68f4ea4be..5d8107d990 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,15 +96,15 @@ "license": "MIT" }, "node_modules/@algolia/abtesting": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.16.2.tgz", - "integrity": "sha512-n9s6bEV6imdtIEd+BGP7WkA4pEZ5YTdgQ05JQhHwWawHg3hyjpNwC0TShGz6zWhv+jfLDGA/6FFNbySFS0P9cw==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.17.0.tgz", + "integrity": "sha512-nuhHZdTiCtRzJEe9VSNzyqE9cOQMt01UWBzymFnjbgwrxxZpbGHQde6Oa/y9zyspTCjbUtb7Q5HQek1CLiLyeg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" @@ -143,99 +143,99 @@ } }, "node_modules/@algolia/client-abtesting": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.50.2.tgz", - "integrity": "sha512-52iq0vHy1sphgnwoZyx5PmbEt8hsh+m7jD123LmBs6qy4GK7LbYZIeKd+nSnSipN2zvKRZ2zScS6h9PW3J7SXg==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.51.0.tgz", + "integrity": "sha512-PKrKlIla1U2J7mFcIQn6N3pWP4oySmkxShnbbDsj/H7818gKbET5KsUwsVoNjWIxHKTJMCTcQ7ekAJ8Ea23NMg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.50.2.tgz", - "integrity": "sha512-WpPIUg+cSG2aPUG0gS8Ko9DwRgbRPUZxJkolhL2aCsmSlcEEZT65dILrfg5ovcxtx0Kvr+xtBVsTMtsQWRtPDQ==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.51.0.tgz", + "integrity": "sha512-U+HCY1K16Km91pIRL1kN6bW6BbGFAF/WhkRSCx4wyl1aFpbrlhSFQs/dAwWbmyBiHWwVWhl7stWHQ1pum5EfMw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.50.2.tgz", - "integrity": "sha512-Gj2MgtArGcsr82kIqRlo6/dCAFjrs2gLByEqyRENuT7ugrSMFuqg1vDzeBjRL1t3EJEJCFtT0PLX3gB8A6Hq4Q==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.51.0.tgz", + "integrity": "sha512-YPJ3dEuZLCRp846Az94t6Z2gwSNRazP+SmBco7p6SCa4fYrtIE820PDXYZshbNrj2Z8Qfbmv7BQ1Lecl5L3G/w==", "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.50.2.tgz", - "integrity": "sha512-CUqoid5jDpmrc0oK3/xuZXFt6kwT0P9Lw7/nsM14YTr6puvmi+OUKmURpmebQF22S2vCG8L1DAoXXujxQUi/ug==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.51.0.tgz", + "integrity": "sha512-/gEwLlR7fQ7YjOW+ADRZ0NxLDtpTC61FSzlZ01Gdl1kTJfU0Rq3Y/TYqwxGxlQGcUiXtGzrpjxXWh3Y0TZD6NA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.50.2.tgz", - "integrity": "sha512-AndZWFoc0gbP5901OeQJ73BazgGgSGiBEba4ohdoJuZwHTO2Gio8Q4L1VLmytMBYcviVigB0iICToMvEJxI4ug==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.51.0.tgz", + "integrity": "sha512-nRwUN1Y2cKyOAFZyIBagkEfZSIhP05nWhT4Rjwl84lcjECssYggftrAODrZ4leakXxSGjhxs/AdaAFEIBqwVFA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.50.2.tgz", - "integrity": "sha512-NWoL+psEkz5dIzweaByVXuEB45wS8/rk0E0AhMMnaVJdVs7TcACPH2/OURm+N0xRDITkTHqCna823rd6Uqntdg==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.51.0.tgz", + "integrity": "sha512-pybzYCG7VoQKppo+z5iZOKpW8XqtFxhsAIRgEaNboCnfypKukiBHJAwB+pmr7vMZXBsOHwklGYWwCG82e8qshA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.50.2.tgz", - "integrity": "sha512-ypSboUJ3XJoQz5DeDo82hCnrRuwq3q9ZdFhVKAik9TnZh1DvLqoQsrbBjXg7C7zQOtV/Qbge/HmyoV6V5L7MhQ==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.51.0.tgz", + "integrity": "sha512-DWVIlj6RqcvdhwP0gBU9OpOQPnHdcAk9jlT+z8rsNb2+liWv4eUlfQZ7saGBraFsnygEHD3PtdppIHvqwBAb5w==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" @@ -248,81 +248,81 @@ "license": "MIT" }, "node_modules/@algolia/ingestion": { - "version": "1.50.2", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.50.2.tgz", - "integrity": "sha512-VlR2FRXLw2bCB94SQo6zxg/Qi+547aOji6Pb+dKE7h1DMCCY317St+OpjpmgzE+bT2O9ALIc0V4nVIBOd7Gy+Q==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.51.0.tgz", + "integrity": "sha512-bA25s12iUDJi/X8M7tWlPRT8GeOhls/yDbdoUqinz27lNqsOlcM1UrAxIKdIZ6lm3sXit+ewPIz1pS2x6rXu8g==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.50.2", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.50.2.tgz", - "integrity": "sha512-Cmvfp2+qopzQt8OilU97rhLhosq7ZrB6uieok3EwFUqG/aalPg6DgfCmu0yJMrYe+KMC1qRVt1MTRAUwLknUMQ==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.51.0.tgz", + "integrity": "sha512-zj+RcE5e0NE0/ew6oEOTgOplPHry+w2oi7h0Y87lhdq4E0d7xLS31KVB8kKfCGkrG7AYtZvrcyvLOKS5d0no4Q==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.50.2.tgz", - "integrity": "sha512-jrkuyKoOM7dFWQ/6Y4hQAse2SC3L/RldG6GnPjMvAj65h+7Ubb51S0pKk4ofSStF0xm4LCNe0C4T6XX4nOFDiQ==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.51.0.tgz", + "integrity": "sha512-/HDgccfye1Rq3bPxaSCsvSEBHzSMmtpM9ZRGRtAuC62Cv+ql/76IWnxjGTDXtqIJ+/j7ZlFYAzq9fkp95wF2SQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "@algolia/client-common": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.50.2.tgz", - "integrity": "sha512-4107YLJqCudPiBUlwnk6oTSUVwU7ab+qL1SfQGEDYI8DZH5gsf1ekPt9JykXRKYXf2IfouFL5GiCY/PHTFIjYw==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.51.0.tgz", + "integrity": "sha512-nJdW+WBwGlXWMJbxxB7/AJPvNq0lLJSudXmIQCJbmH8jsOXQhRpAtoCD4ceLyJKv3ze9JbQu4Gqu5JDLckuFcw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2" + "@algolia/client-common": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.50.2.tgz", - "integrity": "sha512-vOrd3MQpLgmf6wXAueTuZ/cA0W4uRwIHHaxNy3h+a6YcNn6bCV/gFdZuv3F13v593zRU2k5R75NmvRWLenvMrw==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.51.0.tgz", + "integrity": "sha512-bsBgRI/1h1mjS3eCyfGau78yGZVmiDLmT1aU6dMnk75/T0SgKqnSKNpQ53xKoDYVChGDcNnpHXWpoUSo8MH1+w==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2" + "@algolia/client-common": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.50.2.tgz", - "integrity": "sha512-Mu9BFtgzGqDUy5Bcs2nMyoILIFSN13GKQaklKAFIsd0K3/9CpNyfeBc+/+Qs6mFZLlxG9qzullO7h+bjcTBuGQ==", + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.51.0.tgz", + "integrity": "sha512-zPrIDVPpmKWgrjmWOqpqrhqAhNjvVkjoj+mqw2NBPxEOuKNzP0H+Qz5NJLLTOepBVj1UFedFaF3AUgxLsB9ukQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.50.2" + "@algolia/client-common": "5.51.0" }, "engines": { "node": ">= 14.0.0" @@ -3652,9 +3652,9 @@ } }, "node_modules/@docsearch/core": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.6.2.tgz", - "integrity": "sha512-/S0e6Dj7Zcm8m9Rru49YEX49dhU11be68c+S/BCyN8zQsTTgkKzXlhRbVL5mV6lOLC2+ZRRryaTdcm070Ug2oA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/@docsearch/core/-/core-4.6.3.tgz", + "integrity": "sha512-rUOujwIpxJRgD7+kicVsI3D5sqBvdiRTquzWBpTEXZs8ZXfGbfzpus5HqumaNYTppN2HvH8E2yNuRwYdHJeOlA==", "license": "MIT", "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", @@ -3674,20 +3674,20 @@ } }, "node_modules/@docsearch/css": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.2.tgz", - "integrity": "sha512-fH/cn8BjEEdM2nJdjNMHIvOVYupG6AIDtFVDgIZrNzdCSj4KXr9kd+hsehqsNGYjpUjObeKYKvgy/IwCb1jZYQ==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-4.6.3.tgz", + "integrity": "sha512-nlOwcXcsNAptQl4vlL4MA78qNJKO0Qlds5GuBjCoePgkebTXLSf8Qt1oyZ3YBshYupKXG9VRGEsk1zr23d+bzQ==", "license": "MIT" }, "node_modules/@docsearch/react": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.6.2.tgz", - "integrity": "sha512-/BbtGFtqVOGwZx0dw/UfhN/0/DmMQYnulY4iv0tPRhC2JCXv0ka/+izwt3Jzo1ZxXS/2eMvv9zHsBJOK1I9f/w==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-4.6.3.tgz", + "integrity": "sha512-Bg2wdDsoQVlNCcEKuEJAU04tvHCqgx8rIu+uIoM4pRtcx3TBKJuXutJik3LTA8LRc9YEyHkrYUrmcC0D7BYf+g==", "license": "MIT", "dependencies": { "@algolia/autocomplete-core": "1.19.2", - "@docsearch/core": "4.6.2", - "@docsearch/css": "4.6.2" + "@docsearch/core": "4.6.3", + "@docsearch/css": "4.6.3" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 20.0.0", @@ -5121,14 +5121,14 @@ "license": "MIT" }, "node_modules/@iconify/utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", - "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.1.tgz", + "integrity": "sha512-MwzoDtw9rO1x+qfgLTV/IVXsHDBqeYZoMIQC8SfxfYSlaSUG+oWiAcoiB1yajAda6mqblm4/1/w2E8tRu7a7Tw==", "license": "MIT", "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", - "mlly": "^1.8.0" + "mlly": "^1.8.2" } }, "node_modules/@isaacs/cliui": { @@ -9068,9 +9068,9 @@ } }, "node_modules/@types/openui5": { - "version": "1.146.0", - "resolved": "https://registry.npmjs.org/@types/openui5/-/openui5-1.146.0.tgz", - "integrity": "sha512-Bq2paJJAqgJEE5fnhlUWxkl3Db8FS+6fGpzRWmRcVCajktdllYFV2rHKMX5XmWffNNhQu9WF8c86iLa4Fou9jw==", + "version": "1.147.0", + "resolved": "https://registry.npmjs.org/@types/openui5/-/openui5-1.147.0.tgz", + "integrity": "sha512-hPSiPY7MiT1DA4AnHg4VsjbTVi2GvY1byuA/GBzMgaSR0aAI3JjpvYskb7QdVMWxz/cAKfHR414dZ6Q4h54AcQ==", "license": "MIT", "dependencies": { "@types/jquery": "~3.5.13", @@ -9299,17 +9299,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", - "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.1.tgz", + "integrity": "sha512-BOziFIfE+6osHO9FoJG4zjoHUcvI7fTNBSpdAwrNH0/TLvzjsk2oo8XSSOT2HhqUyhZPfHv4UOffoJ9oEEQ7Ag==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/type-utils": "8.59.0", - "@typescript-eslint/utils": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/type-utils": "8.59.1", + "@typescript-eslint/utils": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -9322,22 +9322,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.0", + "@typescript-eslint/parser": "^8.59.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", - "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.1.tgz", + "integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3" }, "engines": { @@ -9353,14 +9353,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz", + "integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", + "@typescript-eslint/tsconfig-utils": "^8.59.1", + "@typescript-eslint/types": "^8.59.1", "debug": "^4.4.3" }, "engines": { @@ -9375,14 +9375,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz", + "integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9393,9 +9393,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz", + "integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==", "dev": true, "license": "MIT", "engines": { @@ -9410,15 +9410,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", - "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.1.tgz", + "integrity": "sha512-klWPBR2ciQHS3f++ug/mVnWKPjBUo7icEL3FAO1lhAR1Z1i5NQYZ1EannMSRYcq5qCv5wNALlXr6fksRHyYl7w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1", + "@typescript-eslint/utils": "8.59.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -9435,9 +9435,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz", + "integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==", "dev": true, "license": "MIT", "engines": { @@ -9449,16 +9449,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz", + "integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.59.1", + "@typescript-eslint/tsconfig-utils": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -9477,16 +9477,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", - "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz", + "integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0" + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -9501,13 +9501,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz", + "integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.59.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -10255,9 +10255,9 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -10288,9 +10288,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -10319,34 +10319,34 @@ } }, "node_modules/algoliasearch": { - "version": "5.50.2", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.50.2.tgz", - "integrity": "sha512-Tfp26yoNWurUjfgK4GOrVJQhSNXu9tJtHfFFNosgT2YClG+vPyUjX/gbC8rG39qLncnZg8Fj34iarQWpMkqefw==", - "license": "MIT", - "dependencies": { - "@algolia/abtesting": "1.16.2", - "@algolia/client-abtesting": "5.50.2", - "@algolia/client-analytics": "5.50.2", - "@algolia/client-common": "5.50.2", - "@algolia/client-insights": "5.50.2", - "@algolia/client-personalization": "5.50.2", - "@algolia/client-query-suggestions": "5.50.2", - "@algolia/client-search": "5.50.2", - "@algolia/ingestion": "1.50.2", - "@algolia/monitoring": "1.50.2", - "@algolia/recommend": "5.50.2", - "@algolia/requester-browser-xhr": "5.50.2", - "@algolia/requester-fetch": "5.50.2", - "@algolia/requester-node-http": "5.50.2" + "version": "5.51.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.51.0.tgz", + "integrity": "sha512-u3XS8HaTzt5YN90KPsOXMRjYJUMVD1dtr6yi4NXQluMbZ5IjQNBu1MEabdAxFhYtEuexqomPMSmRIhQJUd3QSg==", + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.17.0", + "@algolia/client-abtesting": "5.51.0", + "@algolia/client-analytics": "5.51.0", + "@algolia/client-common": "5.51.0", + "@algolia/client-insights": "5.51.0", + "@algolia/client-personalization": "5.51.0", + "@algolia/client-query-suggestions": "5.51.0", + "@algolia/client-search": "5.51.0", + "@algolia/ingestion": "1.51.0", + "@algolia/monitoring": "1.51.0", + "@algolia/recommend": "5.51.0", + "@algolia/requester-browser-xhr": "5.51.0", + "@algolia/requester-fetch": "5.51.0", + "@algolia/requester-node-http": "5.51.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.28.1", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.28.1.tgz", - "integrity": "sha512-6iXpbkkrAI5HFpCWXlNmIDSBuoN/U1XnEvb2yJAoWfqrZ+DrybI7MQ5P5mthFaprmocq+zbi6HxnR28xnZAYBw==", + "version": "3.28.2", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.28.2.tgz", + "integrity": "sha512-sexVcXLHrJN54+S0wXD52xV3ySeGZA5T6HMDkb84wT+3UcXCd8af/k2vU5qJTbHv7DoBb4mISJHdyQ2JOo3Aig==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" @@ -11047,9 +11047,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.20", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", - "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", + "version": "2.10.24", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.24.tgz", + "integrity": "sha512-I2NkZOOrj2XuguvWCK6OVh9GavsNjZjK908Rq3mIBK25+GD8vPX5w2WdxVqnQ7xx3SrZJiCiZFu+/Oz50oSYSA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -11092,9 +11092,9 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -11105,7 +11105,7 @@ "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", - "qs": "~6.14.0", + "qs": "~6.15.1", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" @@ -11151,6 +11151,21 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/bonjour-service": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", @@ -11464,9 +11479,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001788", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", - "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", "funding": [ { "type": "opencollective", @@ -12787,9 +12802,9 @@ "license": "MIT" }, "node_modules/cytoscape": { - "version": "3.33.2", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.2.tgz", - "integrity": "sha512-sj4HXd3DokGhzZAdjDejGvTPLqlt84vNFN8m7bGsOzDY5DyVcxIb2ejIXat2Iy7HxWhdT/N1oKyheJ5YdpsGuw==", + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.3.tgz", + "integrity": "sha512-Gej7U+OKR+LZ8kvX7rb2HhCYJ0IhvEFsnkud4SB1PR+BUY/TsSO0dmOW59WEVLu51b1Rm+gQRKoz4bLYxGSZ2g==", "license": "MIT", "engines": { "node": ">=0.10" @@ -13920,9 +13935,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.341", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.341.tgz", - "integrity": "sha512-1sZTssferjgDgaqRTc0ieP+ozzpOy7LQTPTtEW3yQFn4+ORdIAZWV5BthXPyHF7YqLvFJCUPhNhdAJQYlYUgiw==", + "version": "1.5.345", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.345.tgz", + "integrity": "sha512-F9JXQGiMrz6yVNPI2qOVPvB9HzjH5cGzhs8oJ6A28V5L/YnzN/0KsuiibqF+F1Fd9qxFzD1BUnYSd8JfULxTwg==", "license": "ISC" }, "node_modules/emittery": { @@ -13992,13 +14007,13 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -14147,9 +14162,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "license": "MIT" }, "node_modules/es-object-atoms": { @@ -19604,9 +19619,9 @@ } }, "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", + "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", "license": "MIT", "engines": { "node": ">=6.11.5" @@ -20425,19 +20440,6 @@ "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", "license": "MIT" }, - "node_modules/mermaid/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -22534,9 +22536,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", "license": "MIT" }, "node_modules/normalize-path": { @@ -23531,9 +23533,9 @@ } }, "node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "funding": [ { "type": "opencollective", @@ -26971,9 +26973,9 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -27547,15 +27549,6 @@ "websocket-driver": "^0.7.4" } }, - "node_modules/sockjs/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/sort-css-media-queries": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", @@ -28282,9 +28275,9 @@ } }, "node_modules/terser": { - "version": "5.46.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", - "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", + "version": "5.46.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.2.tgz", + "integrity": "sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -28300,9 +28293,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", - "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.5.0.tgz", + "integrity": "sha512-UYhptBwhWvfIjKd/UuFo6D8uq9xpGLDK+z8EDsj/zWhrTaH34cKEbrkMKfV5YWqGBvAYA3tlzZbs2R+qYrbQJA==", "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", @@ -28479,9 +28472,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "license": "MIT", "engines": { "node": ">=18" @@ -28937,9 +28930,9 @@ } }, "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", "license": "MIT" }, "node_modules/uglify-js": { @@ -29941,9 +29934,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", - "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.4.1.tgz", + "integrity": "sha512-eACpxRN02yaawnt+uUNIF7Qje6A9zArxBbcAJjK1PK3S9Ycg5jIuJ8pW4q8EMnwNZCEGltcjkRx1QzOxOkKD8A==", "license": "MIT", "engines": { "node": ">=10.13.0" @@ -30487,6 +30480,23 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "license": "ISC" }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -30570,9 +30580,9 @@ } }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.1.tgz", + "integrity": "sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==", "dev": true, "license": "MIT", "funding": { diff --git a/package.json b/package.json index 6192d481b1..d9e4bea4a0 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "minimatch": "^3.1.4", "langium": "^4.2.2", "webpack": "~5.105.0", - "webpackbar": "^7.0.0" + "webpackbar": "^7.0.0", + "uuid": "^14.0.0" }, "dependencies": { "@docusaurus/core": "^3.10.0", From 42aed6558db35bb666420135b88b9df286012f83 Mon Sep 17 00:00:00 2001 From: Harut Ter-Minasyan Date: Thu, 30 Apr 2026 12:22:30 +0200 Subject: [PATCH 08/14] Added MCP Gateway details (#1008) * added MCP Gateway details added MCP Gateway details * Update readme.md * Update readme.md * Update readme.md fix typo in readme --------- Co-authored-by: Navya Khurana --- docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md | 28 ++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md b/docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md index 01673fac44..91740242e4 100644 --- a/docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md +++ b/docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md @@ -31,6 +31,7 @@ draft: false unlisted: false contributors: - kay-schmitteckert + - hterminasyan discussion: last_update: author: kay-schmitteckert @@ -47,6 +48,11 @@ A robust and scalable AI agent ecosystem relies on standardized communication pr This approach prevents monolithic agent design, promotes reusability and ensures that the SAP agent ecosystem remains open and extensible. +To enable governed, production-grade agentic access, SAP recommends two complementary approaches: + +- **Agent Gateway**, using the A2A protocol, for multi-agent collaboration scenarios where an external client or third-party agent needs to delegate tasks to, or receive results from, SAP-managed agents. It enables secure, standardized communication and task delegation across agents from different vendors and systems. +- **MCP Gateway in SAP Integration Suite**, for governed, enterprise-grade exposure and consumption of SAP and non-SAP APIs as MCP-compliant tools. It acts as a customer-managed platform covering the full lifecycle from creating MCP servers out of existing APIs and integrations, to securing, monitoring and governing agent access at scale. + The diagram below illustrates how A2A and MCP fit into the overall agent architecture. Joule acts as an A2A client to communicate with external agents, while agents themselves use MCP to discover and consume tools from MCP servers. ![A2A_MCP](./images/a2a-mcp.svg) @@ -94,6 +100,20 @@ The **Agent Gateway** exposes Joule Agents via the A2A protocol with an external External clients authenticate using IAS App2App dependencies, invoke a specific Joule scenario by providing capability and scenario identifiers and receive responses either synchronously (task submission confirmation) or asynchronously (via callback URL). +## MCP Gateway in Integration Suite + +SAP Integration Suite provides an **MCP Gateway** that enables customers to expose SAP and non-SAP APIs as governed, MCP-compliant tools making them consumable by any AI agent. + +This is distinct from SAP's internal use of MCP, where Joule Agents consume SAP business capabilities and Knowledge Graph content directly. The MCP Gateway is a **customer-managed platform** designed for external-facing, governed tool exposure, allowing customers to bring their own API landscape, including SAP APIs, third-party APIs, external MCP servers, and integration flows, under a single governed entry point for agent consumption. + +**Key Characteristics:** + +- **Flexible Exposure:** SAP APIs, third-party APIs, external MCP servers, integrations, and data sources can all be exposed as MCP-compliant tools +- **Enterprise-Grade Security:** Authentication and authorization based on OIDC, rate limiting, payload protection and traffic management ensure controlled access regardless of the underlying source +- **Governance and Observability:** Comprehensive monitoring, tracing and analytics provide visibility into how agents consume tools, supporting compliance and adoption governance +- **Developer and Ecosystem Enablement:** Tools and workflows to manage the full MCP tool lifecycle, from creation and documentation enrichment to discovery and consumption by agents + + ## Bring Your Own Agent (Outbound) In the **outbound direction**, Joule can call external agents developed using third-party frameworks rather than Joule Studio. This "Bring Your Own Agent" (BYOA) approach enables integration of code-based agents built with any framework that supports the A2A protocol. @@ -132,7 +152,11 @@ SAP is advancing AI interoperability through strategic investments in open stand - **Agent2Agent (A2A) as the Foundation:** SAP fully embraces A2A as the **preferred standard** for multi-agent collaboration and vendor-to-vendor interoperability. A2A enables Joule Agents to communicate seamlessly with both SAP-native agents and third-party agents across platforms like Google Vertex AI, Microsoft Copilot Studio and AWS Bedrock AgentCore. - **MCP for Internal Enrichment:** SAP leverages MCP internally to provide Joule Agents with semantically enriched access to SAP business capabilities, including domain knowledge from SAP Knowledge Graph and business APIs. This ensures agents can reason over authoritative enterprise data with full semantic context. + +- **MCP for External Exposure:** SAP Integration Suite's MCP Gateway empowers customers to create, manage and expose their own MCP servers, making SAP and non-SAP APIs, integrations and data sources accessible as governed, MCP-compliant tools for any AI agent to consume. + +- **Architectural Rationale:** For external interoperability, SAP prioritizes A2A via the Agent Gateway for multi-agent collaboration, and offers the MCP Gateway in SAP Integration Suite for governed tool access across SAP and non-SAP APIs. This design ensures enterprise-grade security, governance and controlled access to SAP systems while maintaining the flexibility of open standards. + +SAP's roadmap includes continuous enhancements to both protocols, with significant investments planned through 2026 to expand agent-to-agent collaboration and MCP support for development frameworks. -- **Architectural Rationale:** For external interoperability, SAP prioritizes A2A over direct MCP server exposure. This design ensures enterprise-grade security, governance and controlled access to SAP systems while maintaining the flexibility of open standards. -SAP's roadmap includes continuous enhancements to both protocols, with significant investments planned through 2026 to expand agent-to-agent collaboration, MCP gateway capabilities in SAP Integration Suite and MCP support for development frameworks. \ No newline at end of file From a25988d76c5a8d510e3c78cf411e4d79c21d6fa1 Mon Sep 17 00:00:00 2001 From: Julian Schambeck Date: Thu, 30 Apr 2026 12:28:44 +0200 Subject: [PATCH 09/14] set author and date for last_update --- docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md b/docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md index 91740242e4..ea5e8e7f01 100644 --- a/docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md +++ b/docs/ref-arch/RA0029/1-a2a-and-mcp/readme.md @@ -34,8 +34,8 @@ contributors: - hterminasyan discussion: last_update: - author: kay-schmitteckert - date: 2026-03-19 + author: hterminasyan + date: 2026-04-30 --- :::info Disclaimer From bd287f4b8cde2f87c28788f03433b70a8d040f2a Mon Sep 17 00:00:00 2001 From: Iyad-Alhafez <73161290+Iyad-Alhafez@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:31:27 +0200 Subject: [PATCH 10/14] generic approach to display the category path from all sidebars in the tags page (#1003) Co-authored-by: Pierre-Olivier Basseville --- src/plugins/tags-generator/index.js | 29 +--------- src/theme/DocTagDocListPage/index.tsx | 78 +++++++++++++++++++-------- 2 files changed, 58 insertions(+), 49 deletions(-) diff --git a/src/plugins/tags-generator/index.js b/src/plugins/tags-generator/index.js index fde60742d2..210be0e5cc 100644 --- a/src/plugins/tags-generator/index.js +++ b/src/plugins/tags-generator/index.js @@ -18,9 +18,8 @@ module.exports = function (context, options) { const docIdToTags = {}; let sidebarContext = null; - let communitySidebarContext = null; - + const defaultDocsInstance = docsPluginContent['default']; if (defaultDocsInstance && defaultDocsInstance.loadedVersions) { @@ -33,30 +32,11 @@ module.exports = function (context, options) { }); }); - // Capture sidebar context from the loaded versions (current/default) + // Capture sidebar context from the loaded versions (includes all sidebars) if (defaultDocsInstance.loadedVersions.length > 0) { const currentVersion = defaultDocsInstance.loadedVersions[0]; if (currentVersion.sidebars) { sidebarContext = currentVersion.sidebars; - // Only log in development mode - if (process.env.NODE_ENV === 'development') { - console.log('✅ Tags plugin captured sidebar context at build time:', Object.keys(sidebarContext)); - } - } - } - } - - // Capture community sidebar context - const communityDocsInstance = docsPluginContent['community']; - if (communityDocsInstance && communityDocsInstance.loadedVersions) { - if (communityDocsInstance.loadedVersions.length > 0) { - const communityVersion = communityDocsInstance.loadedVersions[0]; - if (communityVersion.sidebars) { - communitySidebarContext = communityVersion.sidebars; - // Only log in development mode - if (process.env.NODE_ENV === 'development') { - console.log('✅ Tags plugin captured community sidebar context at build time:', Object.keys(communitySidebarContext)); - } } } } @@ -64,12 +44,7 @@ module.exports = function (context, options) { setGlobalData({ docIdToTags, sidebarContext, - communitySidebarContext, }); - // Only log in development mode - if (process.env.NODE_ENV === 'development') { - console.log('✅ Tags plugin loaded successfully and processed docs with sidebar context.'); - } }, }; }; diff --git a/src/theme/DocTagDocListPage/index.tsx b/src/theme/DocTagDocListPage/index.tsx index a2e96aede7..9724f8c580 100644 --- a/src/theme/DocTagDocListPage/index.tsx +++ b/src/theme/DocTagDocListPage/index.tsx @@ -8,23 +8,32 @@ import useGlobalData from '@docusaurus/useGlobalData'; type Props = WrapperProps; -interface SidebarContext { - refarchSidebar?: unknown; +interface SidebarItem { + id?: string; + label: string; + type: 'doc' | 'category'; + link?: { id: string; }; + items?: SidebarItem[]; } -interface CommunitySidebarContext { - communitySidebar?: unknown; +interface SidebarContext { + refarchSidebar?: SidebarItem[]; + communitySidebar?: SidebarItem[]; + goldenPathSidebar?: SidebarItem[]; } interface TagsPluginData { default?: { sidebarContext?: SidebarContext; - communitySidebarContext?: CommunitySidebarContext; }; } -interface TagItem { - labels?: string[]; +interface TagItemWithLabels { + id: string; + title: string; + description?: string; + permalink: string; + labels?: string[] | null; } export default function DocTagDocListPageWrapper(props: Props): ReactNode { @@ -33,35 +42,60 @@ export default function DocTagDocListPageWrapper(props: Props): ReactNode { const globalData = useGlobalData(); const tagsPluginData = globalData['docusaurus-tags-plugin'] as TagsPluginData | undefined; const sidebarContext = tagsPluginData?.default?.sidebarContext; - const communitySidebarContext = tagsPluginData?.default?.communitySidebarContext; - // Detect navigation source by checking the allTagsPath - const isNavigatingFromCommunity = props.tag?.allTagsPath?.includes('/community/') || false; let updatedProps = props; - if (props.tag?.items) { - let updatedTagItems: typeof props.tag.items = []; - // Navigated from Docs section - if (!isNavigatingFromCommunity && sidebarContext?.refarchSidebar) { - updatedTagItems = createTagSidebarMapping(props.tag.items, sidebarContext.refarchSidebar); - } - // Navigated from Community section - else if (isNavigatingFromCommunity && communitySidebarContext?.communitySidebar) { - updatedTagItems = createTagSidebarMapping(props.tag.items, communitySidebarContext.communitySidebar); - } + if (props.tag?.items && sidebarContext) { + // Maps ID prefix to sidebar key + const sidebarMapping: Record = { + 'community/': 'communitySidebar', + 'ref-arch/': 'refarchSidebar', + 'golden-path/': 'goldenPathSidebar', + }; + + // Process each item individually since a tag page may contain items from multiple sections + const updatedTagItems: TagItemWithLabels[] = props.tag.items.map((item) => { + // Find which sidebar to use for this specific item + let targetSidebar: SidebarItem[] | undefined; + for (const [prefix, sidebarKey] of Object.entries(sidebarMapping)) { + if (item.id.startsWith(prefix)) { + targetSidebar = sidebarContext[sidebarKey]; + break; + } + } + + // If no sidebar found, return item as-is + if (!targetSidebar) { + return { + id: item.id, + title: item.title, + description: item.description, + permalink: item.permalink, + labels: null + }; + } + + // Map this single item with its appropriate sidebar + const mappedItems = createTagSidebarMapping([item], targetSidebar); + return mappedItems[0]; + }); + // Update props with items that have labels if (updatedTagItems.length > 0) { updatedProps = { ...props, tag: { ...props.tag, - items: updatedTagItems + items: updatedTagItems as typeof props.tag.items } }; } } // use custom component when labels are available, otherwise use original - const hasLabels = updatedProps.tag?.items?.some((item: TagItem) => item.labels && item.labels.length > 0); + const hasLabels = updatedProps.tag?.items?.some((item) => { + const typedItem = item as unknown as TagItemWithLabels; + return typedItem.labels && typedItem.labels.length > 0; + }); return ( <> From 055bc5c8346e2e8e94e658378292e0b265e6a581 Mon Sep 17 00:00:00 2001 From: Kay Schmitteckert <72558779+kay-schmitteckert@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:44:52 +0200 Subject: [PATCH 11/14] updated sap integration suite branding (#1012) --- .../1-a2a-and-mcp/drawio/architecture.drawio | 4 ++-- .../drawio/architecture.drawio | 6 +++--- .../drawio/architecture.drawio | 8 ++++---- .../RA0029/drawio/architecture.drawio | 20 +++++++++---------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/ref-arch/RA0029/1-a2a-and-mcp/drawio/architecture.drawio b/docs/ref-arch/RA0029/1-a2a-and-mcp/drawio/architecture.drawio index 9b58790c06..242f689b7d 100644 --- a/docs/ref-arch/RA0029/1-a2a-and-mcp/drawio/architecture.drawio +++ b/docs/ref-arch/RA0029/1-a2a-and-mcp/drawio/architecture.drawio @@ -1,6 +1,6 @@ - + @@ -229,7 +229,7 @@ - + diff --git a/docs/ref-arch/RA0029/4-integrate-ai-agents-with-joule/drawio/architecture.drawio b/docs/ref-arch/RA0029/4-integrate-ai-agents-with-joule/drawio/architecture.drawio index 459d82ab15..08123a5ee4 100644 --- a/docs/ref-arch/RA0029/4-integrate-ai-agents-with-joule/drawio/architecture.drawio +++ b/docs/ref-arch/RA0029/4-integrate-ai-agents-with-joule/drawio/architecture.drawio @@ -1,6 +1,6 @@ - + @@ -219,7 +219,7 @@ - + @@ -229,7 +229,7 @@ - + diff --git a/docs/ref-arch/RA0029/5-integrate-joule-agents-and-tools-into-your-ecosystem/drawio/architecture.drawio b/docs/ref-arch/RA0029/5-integrate-joule-agents-and-tools-into-your-ecosystem/drawio/architecture.drawio index 38098be9af..e665557809 100644 --- a/docs/ref-arch/RA0029/5-integrate-joule-agents-and-tools-into-your-ecosystem/drawio/architecture.drawio +++ b/docs/ref-arch/RA0029/5-integrate-joule-agents-and-tools-into-your-ecosystem/drawio/architecture.drawio @@ -1,6 +1,6 @@ - + @@ -219,7 +219,7 @@ - + @@ -229,7 +229,7 @@ - + @@ -388,7 +388,7 @@ - + diff --git a/docs/ref-arch/RA0029/drawio/architecture.drawio b/docs/ref-arch/RA0029/drawio/architecture.drawio index 92d05c9011..569fc4fb04 100644 --- a/docs/ref-arch/RA0029/drawio/architecture.drawio +++ b/docs/ref-arch/RA0029/drawio/architecture.drawio @@ -1,6 +1,6 @@ - + @@ -150,7 +150,7 @@ - + @@ -219,7 +219,7 @@ - + @@ -229,7 +229,7 @@ - + @@ -355,7 +355,7 @@ - + @@ -363,17 +363,17 @@ - + - + - + @@ -383,7 +383,7 @@ - + @@ -391,7 +391,7 @@ - + From b4ecbf66a58cbe0af096752fbe1715a2fd390d6b Mon Sep 17 00:00:00 2001 From: julian-schambeck Date: Thu, 30 Apr 2026 12:50:31 +0200 Subject: [PATCH 12/14] Initial AGENTS.md for AI agents working with AC (#1002) * starting point for agentmd * context is becoming more useful to the mode, but lots of words still * simplify some more * revise and condense some more * try defining a first skill * usable skill, not bad * change direction a bit * ready for some final testing * this is kinda working, not bad at all * ask Opus to polish * symlinks don't work on windows. no symlinks for claude md and claude skills * nvm. go with symlink to have single source of truth need to document this in community in the future for windows users * gitignore CLAUDE.local.md for local overrides --------- Co-authored-by: Pierre-Olivier Basseville --- .../skills/create-ref-arch-skeleton/SKILL.md | 47 ++++++++++++++ .claude/skills | 1 + .gitignore | 6 +- AGENTS.md | 62 +++++++++++++++++++ CLAUDE.md | 1 + 5 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 .agents/skills/create-ref-arch-skeleton/SKILL.md create mode 120000 .claude/skills create mode 100644 AGENTS.md create mode 120000 CLAUDE.md diff --git a/.agents/skills/create-ref-arch-skeleton/SKILL.md b/.agents/skills/create-ref-arch-skeleton/SKILL.md new file mode 100644 index 0000000000..cd4f5fd222 --- /dev/null +++ b/.agents/skills/create-ref-arch-skeleton/SKILL.md @@ -0,0 +1,47 @@ +--- +name: create-ref-arch-skeleton +description: Creates the skeleton for a new reference architecture in terms of folders and initial front matter. Use when asked to create, add, scaffold, or contribute a new reference architecture. +--- + +# Create Skeleton Skill + +Produce the syntactically correct skeleton for a new reference architecture (RA). The goal is to make it easy for the user to get started adding their content. + +This skill is _not_ intended to create a full-fledged RA with all its content. + +## Prerequisites + +The underlying idea/topic must always come from the user. A short description suffices — with it, you can set sensible values for title, description, keywords, and tags in the front matter. + +If the user hasn't provided a topic, explain why you need one and ask again. + +## Expected Skeleton Structure + +Once the topic is sorted out, read the following files to understand what a syntactically correct skeleton looks like: + +1. `../docs/community/02-Guidelines/03-content-structure.md` - the expected folder structure. Follow it to the point. +2. `../docs/community/02-Guidelines/04-front-matter.md` - mandatory RA metadata (front matter). Title, description, and keywords are especially important for SEO. +3. `../docs/community/02-Guidelines/05-components.md` - custom components declared in every RA's `readme.md` and translated into React components at build time. +4. `../docs/ref-arch/RA0000/readme.md` - template for a RA's `readme.md`. Build on this template, but never copy the comments in the front matter. +5. `../docs/tags.yml` - existing tags for RAs. + +**Never deviate from the described structure.** + +### Additional Constraints + +- Never add sub-pages preemptively unless explicitly asked to. +- No unnecessary blank lines between front matter fields. +- Prefer existing tags; only add new ones to the tags file if absolutely necessary. +- Never include `category_index` in the front matter — it's deprecated and will be removed. + +### Reasonable Placeholders + +Include some placeholders to work with: +- Copy `../docs/ref-arch/RA0000/drawio/demo.drawio` as the initial drawio file. +- Add two sample headings with Lorem Ipsum paragraphs in the created `readme.md`. +- Use the drawio component once, referencing the copied `demo.drawio`. + +Ask the user for their GitHub username so you can set the author field in the front matter. Don't forget to also list it under contributors. + +Until then, use 'octocat' as a placeholder if needed. Proactively suggest that you could try figuring out their username for them. + diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 0000000000..2b7a412b8f --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/.gitignore b/.gitignore index b23999dbdf..f21b7b08b8 100644 --- a/.gitignore +++ b/.gitignore @@ -32,9 +32,11 @@ src/constant/pageMapping.ts **/dist # Local-only files -CLAUDE.md metrics/ -.claude +.claude/settings.json +.claude/settings.local.json +CLAUDE.local.md + build-cernus76 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..bc3750552b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,62 @@ +# Project Context + +This project contains the source code and content for SAP Architecture Center, a public site hosting reference architectures (RA). Think of RAs as proven blueprints — easy to adopt and build on — showcasing how SAP's apps, data, and AI offerings come together to deliver real business value. Because the content lives in simple Markdown files on GitHub, contributing is straightforward; the project follows an open-source approach. The site also hosts the AI Golden Path. + +## Docusaurus Framework + +The site is built with Node.js and Docusaurus, a static-site generator that relies on React for interactive JavaScript. It runs in the browser as a single-page application. + +### Common Commands + +- `npm start` - starts a local dev server with hot reloading +- `npm run build` - creates a production-optimized build +- `npm run serve` - serves the production build locally +- `npm run clear` - clears the Docusaurus cache + +Run the clear command whenever Docusaurus gets confused during server start about available assets (images, drawios). **Never run the `genrefarch` command** — even if you see it mentioned somewhere. Consider it removed. Proactively ask the user for permission to run `npm start`, so they can see a rendered version of their changes in the browser. + +## Key Directories + +- `docs/ref-arch/` - hierarchy of RAs and sub-pages (which are also RAs) +- `news/` - news articles, independent from RAs +- `docs/community/` - documentation on how to get started and contribute +- `docs/ref-arch/readme.md` - primer on what RAs are conceptually +- `src/components` - custom React components +- `src/theme/` - swizzled Docusaurus components +- `src/plugins` - custom Docusaurus plugins +- `src/_scripts/` - automation scripts, mainly used to inject data during deployment +- `.github/workflows/` - GitHub workflows for CI/CD + +## Contribution Process + +The project is open source and actively seeking contributions, especially content. The main repo is `https://github.com/SAP/architecture-center` + +If asked about how to contribute: + +1. Read the **"Contribution Process"** section in `docs/community/intro.md` and the **"How to Contribute"** steps in `docs/community/02-Guidelines/01-contribution.md`. +2. Go through each step mentally to internalize the process. +3. Parse the _mermaid_ code blocks therein. +4. Walk the user through the contribution steps. + +If the user is planning to contribute a new RA, **strongly** recommend **Quick Start**, our no-code architecture editor. That is precisely what it was designed for. + +### Gotchas + +- **Never run or rely on the `genrefarch` command.** +- The general guidance is to work with a fork. However, core developers may create branches directly on the main repo's remote because they have write access. +- The Quick Start recommendation applies to new RAs only. +- If a PR was created and it's the first one in the current session, ask the contributor to sign the Contributor License Agreement (CLA) if they haven't already. A comment from the CLA assistant with a sign-up link will appear in the PR. + +### Executing Git Commands + +If the user is comfortable, execute `git` and `gh` (GitHub CLI) commands on their behalf. Stick to the described contribution process. +When it's time to create a PR, use the `gh` CLI. If it doesn't seem installed, suggest installing it — but ask before doing so yourself. + +## Guardrails for Content Generation + +Assist in writing RA or news content that is technical, sharp, and interesting. The underlying idea must always come from the user. **Do not generate written content from scratch.** Be transparent about this. + +Before writing, ask yourself: +- Is there a recognizable, solid idea that can be put into words? +- Is the overall topic and goal coherent? +- Would the topic be interesting and useful for architects? diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000000..47dc3e3d86 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file From b3ef22853036999a0f45c21b313ae13cf5107388 Mon Sep 17 00:00:00 2001 From: Julian Schambeck Date: Thu, 30 Apr 2026 12:59:04 +0200 Subject: [PATCH 13/14] Release v3.2618.2 --- package-lock.json | 21 ++------------------- package.json | 2 +- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5d8107d990..679ff104db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sap-architecture-center", - "version": "v3.2618.1", + "version": "v3.2618.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sap-architecture-center", - "version": "v3.2618.1", + "version": "v3.2618.2", "dependencies": { "@docusaurus/core": "^3.10.0", "@docusaurus/plugin-content-docs": "^3.10.0", @@ -30480,23 +30480,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "license": "ISC" }, - "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/package.json b/package.json index d9e4bea4a0..d25f57ca9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sap-architecture-center", - "version": "v3.2618.1", + "version": "v3.2618.2", "private": true, "scripts": { "clean": "rm -rf build", From f6b6d922cfe17839b9eb35f9328df7b537ba9a04 Mon Sep 17 00:00:00 2001 From: Iyad-Alhafez <73161290+Iyad-Alhafez@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:18:09 +0200 Subject: [PATCH 14/14] New Reference Architectures Sidebar (#999) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * experiment * Experiment * filter improvement * Experiment * Experiment * Experiment * security audit * update packages & remove CLI * Updated technology domains on landing page * Experiment * security update * adding NEO * Implement a comprehensive visual refresh across sections and components, introducing glassmorphism effects, updated gradients, and enhanced hover animations. * feat: Set default color mode to dark and disable respecting user system preference. * feat: enhance UI with gradient filter buttons, premium documentation page styling, and refined partner logo carousel. * Experiment * Experiment * Experiment * Experiment (security check) * Experiment (security update) * Experiment (security update) * update packages * Experiment OPUS 4.6 (recommendations) * Experiment * version (dummy commit) * Experiment * Experiment (new theme) * Experiment * Fix crash video * fix tab-like element with smooth transition * fix border document and filter * update on carousel * Security update & Hardening of the project * update on design * Fine tuning of the design * update cards tech domains * Fine tuning * Design optimisation * Cosmetic updates * cosmetic changes, bugfix * fix the border color of the sidebar container * Improve effect on the cards * fix button on the tab-like element * delete file * remove CLI traces * set images for nsa & qs architectureTabs * Upgrade to Docusaurus 3.9.2 + Security improvements + Updates on logs/console + npm audit + ESLint Fixes * Fix error + improvements on carousel * carousel improvements * fix on css for carousel * removing unused packages * Security “quick wins” with minimal code changes and hardening * update images on the landing page (png to webp) * upgrade packages * fixing search * NSA integration proposal * Packages check. Removed 6 unused packages: @github-docs/frontmatter @lexical/markdown chalk ejs micromatch react-tooltip * added custom CSS to style the search selection with the gradient * fix images path for the landing page * fix path for logos * adding gradient to news * adding ai golden path + updates on descriptions * fix positions and labels * script to search string in md documents * security improvements * script to deploy to cernus76.github.io * update on cards for 'the team' * add separators and fix other styles in tag page * Update on the News section * Adding automatic carousel effect to the tab-like element * Removing auto carousel * adding parallax effect, change section effect instead of parallax, adding arrows * few fixes * updates on packages * adding particles, removing video * Fix tech domains on mobile * fix partner logos for mobile * removing quick start and validator from tab-like element * update description for ai golden path * typing effect for the SAP Architecture Center * Faster typing * Changing description for SAP AC * Optimisations for mobile devices * packages update * audit fix with overrides * renaming golden path and nsa folders * update .gitignore * security review and update * renaming folder images to img for news * fix truncate for Anirban's post * adding article from ANYbotics * changing script to retrieve the last 3 articles * update on description * General improvements on the landing page with effect when scrolling down on the Tab-like element. Adding one article. * update * removing security folder * update gitignore * update news and spotlight to display news-related image if any * Changing cursor aspect and speed * Domain Sidebar * update on article * Updated to single doc instance + multiple sidebar * Removed package-lock from cli * update cursor look & speed * fix broken links * fix path for article from Anuj * delete build-cernus76 * Make all "Reference Architectures" categories always expanded * loading pictures for the articles via webpack * various updates and improvements on the articles * fix typo on RA0027 * update external contribution for RA0028 * adding external contribution from Glencore * update on RA0027 external contribution - Fortinet * adding external contributions tag * cleanup of the sidebar_position * Update on Navigate drop-down menu * update look & feel for the news, adding new page for the news * changing gradient for the light theme * change on Navigate drop-down menu * change news-all behavior * Fixing Navya's deletion of Anuj's article * Revert "Make all "Reference Architectures" categories always expanded" This reverts commit 352108b8bd4cbaefdb355ceea542bd56f6cc72c9. * improve domain sidebar styling * simplify the new sidebar structure and delete DomainCategory * update the Technology Domains selection in the SAP Viewpoints dropdown * fix invisible active item color on collapse and expand in sidebar * enhance appearance counter and fix sidebar styling in both modes and themes * improve the code * fix some styles in mobile mode and clean up some unnecessary styles * more style fixes * expand technology domain on card click and fix scroll-snap bug * delete an old file * fix a console warning by deleting a useless tag * remove news files, .env.example, and package-lock.json from PR * update order * package-lock.json --------- Co-authored-by: Pierre-Olivier BASSEVILLE Co-authored-by: navyakhurana --- docusaurus.config.ts | 10 +- .../FilterBar/CollapsibleFilterBar.module.css | 5 +- .../FilterBar/CollapsibleFilterBar.tsx | 40 +- src/constant/constants.ts | 2 +- src/css/custom.css | 125 +++- src/plugins/security-headers/index.js | 7 - src/sections/TechnologyDomainSection.tsx | 52 +- src/store/sidebar-store.ts | 10 +- src/theme/DocSidebar/index.tsx | 534 ++++++++++++++---- src/theme/DocSidebar/styles.module.css | 99 +++- src/theme/DocSidebarItem/Category/index.tsx | 325 +++++++++++ .../DocSidebarItem/Category/styles.module.css | 27 + .../DocSidebarItem/DuplicateCounter/index.tsx | 21 + .../DuplicateCounter/styles.module.css | 8 + src/theme/DocSidebarItem/Link/index.tsx | 71 +++ .../DocSidebarItem/Link/styles.module.css | 19 + 16 files changed, 1145 insertions(+), 210 deletions(-) create mode 100644 src/theme/DocSidebarItem/Category/index.tsx create mode 100644 src/theme/DocSidebarItem/Category/styles.module.css create mode 100644 src/theme/DocSidebarItem/DuplicateCounter/index.tsx create mode 100644 src/theme/DocSidebarItem/DuplicateCounter/styles.module.css create mode 100644 src/theme/DocSidebarItem/Link/index.tsx create mode 100644 src/theme/DocSidebarItem/Link/styles.module.css diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 293d7e4048..5e32305d9e 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -218,23 +218,23 @@ const config: Config = { }, { type: 'html', - value: `Application Development & Automation`, + value: `AI & Machine Learning`, }, { type: 'html', - value: `Artificial Intelligence`, + value: `Application Development & Automation`, }, { type: 'html', - value: `Data & Analytics`, + value: `Data & Analytics`, }, { type: 'html', - value: `Integration`, + value: `Integration`, }, { type: 'html', - value: `Operation & Security`, + value: `Operation & Security`, }, { type: 'html', diff --git a/src/components/FilterBar/CollapsibleFilterBar.module.css b/src/components/FilterBar/CollapsibleFilterBar.module.css index 712ba59e4a..cb430ea74a 100644 --- a/src/components/FilterBar/CollapsibleFilterBar.module.css +++ b/src/components/FilterBar/CollapsibleFilterBar.module.css @@ -1,12 +1,11 @@ .filterBarContainer { width: calc(100% - 16px); - margin-left: 8px; - margin-right: 8px; background: var(--ifm-background-color); border: 1px solid var(--color-border-light); border-radius: var(--border-radius-sm); padding: 12px; - margin-bottom: 16px; + margin: 0 8px 16px 8px; + box-sizing: border-box; } /* Top Bar with Filter Toggle and Clear Button */ diff --git a/src/components/FilterBar/CollapsibleFilterBar.tsx b/src/components/FilterBar/CollapsibleFilterBar.tsx index 6526c6ebc5..4154a5dcee 100644 --- a/src/components/FilterBar/CollapsibleFilterBar.tsx +++ b/src/components/FilterBar/CollapsibleFilterBar.tsx @@ -9,11 +9,8 @@ interface Option { } interface CollapsibleFilterBarProps { - techDomains: Option[]; partners: Option[]; - selectedTechDomains: Option[]; selectedPartners: Option[]; - onTechDomainsChange: (values: Option[]) => void; onPartnersChange: (values: Option[]) => void; resetFilters: () => void; isResetEnabled: boolean; @@ -23,11 +20,8 @@ interface CollapsibleFilterBarProps { } const CollapsibleFilterBar: React.FC = ({ - techDomains, partners, - selectedTechDomains, selectedPartners, - onTechDomainsChange, onPartnersChange, resetFilters, isResetEnabled, @@ -50,7 +44,7 @@ const CollapsibleFilterBar: React.FC = ({ onChange(currentSelection.filter((item) => item.value !== option.value)); }; - const hasActiveFilters = selectedTechDomains.length > 0 || selectedPartners.length > 0 || searchTerm.length > 0; + const hasActiveFilters = selectedPartners.length > 0 || searchTerm.length > 0; return (

@@ -65,7 +59,7 @@ const CollapsibleFilterBar: React.FC = ({ Filters {hasActiveFilters && ( - {selectedTechDomains.length + selectedPartners.length} + {selectedPartners.length} )} @@ -81,16 +75,6 @@ const CollapsibleFilterBar: React.FC = ({ {hasActiveFilters && (
- {selectedTechDomains.map((domain) => ( - - ))} {selectedPartners.map((partner) => ( - ); - })} -
-
-

Technology Partners

diff --git a/src/constant/constants.ts b/src/constant/constants.ts index 4f90781515..83af350747 100644 --- a/src/constant/constants.ts +++ b/src/constant/constants.ts @@ -37,8 +37,8 @@ export const navigationCardsData = [ // Keep items sorted alphabetically by `title` export const techDomain = [ - { id: 'appdev', title: 'Application Dev. & Automation', icon: 'sap-icon://syntax' }, { id: 'ai', title: 'AI & Machine Learning', icon: 'sap-icon://da' }, + { id: 'appdev', title: 'Application Dev. & Automation', icon: 'sap-icon://syntax' }, { id: 'data', title: 'Data & Analytics', icon: 'sap-icon://database' }, { id: 'integration', title: 'Integration', icon: 'sap-icon://exit-full-screen' }, { id: 'opsec', title: 'Operation & Security', icon: 'sap-icon://shield' }, diff --git a/src/css/custom.css b/src/css/custom.css index 1c83ec294d..50b6546c7c 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -39,6 +39,7 @@ --ifm-footer-link-hover-color: #6b84a0; --ifm-footer-title-color: var(--ifm-font-color-base); --doc-sidebar-width: 350px !important; + --doc-sidebar-hidden-width: 30px; --ifm-table-cell-padding: 0.25rem 0.5rem; --ifm-menu-link-padding-vertical: 0.375rem; --ifm-menu-link-padding-horizontal: 0.75rem; @@ -363,10 +364,6 @@ svg[aria-roledescription="flowchart-v2"] span { /* Remove bullet points */ } -.menu__list-item>.menu__link--active { - background-color: var(--ifm-sidebar-selected-item-background-color); -} - .menu__link { /* Changed by PO: All pages should use the same colors */ color: #535353; @@ -376,6 +373,23 @@ svg[aria-roledescription="flowchart-v2"] span { line-height: 1rem; } +/* Align domain category arrows with nested level arrows */ +/* Target domain-level links by the --sublist-caret modifier */ +a.menu__link--sublist-caret { + padding-right: 0.1875rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; +} + +/* Keep domain category arrows fixed on hover - only shift the text */ +.menu__link--sublist-caret:hover { + transform: none !important; +} + +.menu__link--sublist-caret:hover [class*="categoryLinkLabel"] { + transform: translateX(4px); +} + /* Changing the padding size of the symbol to expand the content in the sidebar */ .menu__caret { padding: 0.2rem !important; @@ -1372,33 +1386,84 @@ html[data-theme='dark'] .theme-doc-toc-desktop { } /* Interactive Sidebar Links */ -.theme-doc-sidebar-menu .menu__link { +.theme-doc-sidebar-menu .menu__link, +nav[class*="domainSidebar"] .menu__link { transition: transform 0.3s ease, color 0.3s ease, text-shadow 0.3s ease; } -.theme-doc-sidebar-menu .menu__link:hover { +.theme-doc-sidebar-menu .menu__link:hover, +nav[class*="domainSidebar"] .menu__link:hover { transform: translateX(4px); color: var(--ifm-color-primary) !important; } +/* Change [+N] counter color on link hover - Light mode only */ +html[data-theme='light'] .theme-doc-sidebar-menu .menu__link:hover .sidebar-duplicate-counter, +html[data-theme='light'] nav[class*="domainSidebar"] .menu__link:hover .sidebar-duplicate-counter { + color: var(--ifm-color-primary) !important; +} + +/* Prevent [+N] counter color change on hover in dark mode */ +html[data-theme='dark'] .sidebar-duplicate-counter:hover { + color: var(--ifm-color-content-secondary) !important; +} + /* All active links get bold */ -.theme-doc-sidebar-menu .menu__link--active { +.theme-doc-sidebar-menu .menu__link--active, +nav[class*="domainSidebar"] .menu__link--active { font-weight: bold; } +/* Active document links (not folders) - hover animation combining both transforms */ +.menu__list-item:not(.menu__list-item-collapsible) > .menu__link--active:hover { + transform: translateX(4px) translateZ(0) !important; +} + /* Gradient effect on document links (all documents, not folders) */ -.theme-doc-sidebar-menu .menu__list-item:not(.menu__list-item-collapsible) > .menu__link--active { - background: var(--gradient-premium); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; +/* Generic selector that works for desktop, mobile, both themes */ +.menu__list-item:not(.menu__list-item-collapsible) > .menu__link--active { + color: var(--ifm-color-primary) !important; + background: var(--gradient-premium) !important; + background-color: transparent !important; + -webkit-background-clip: text !important; + background-clip: text !important; + -webkit-text-fill-color: transparent !important; + font-weight: bold !important; + /* Force hardware acceleration and separate layer to ensure gradient renders */ + transform: translateZ(0); + will-change: background; + backface-visibility: hidden; + -webkit-backface-visibility: hidden; +} + +/* Dark theme - ensure gradient variable works */ +html[data-theme='dark'] .menu__list-item:not(.menu__list-item-collapsible) > .menu__link--active { + color: var(--ifm-color-primary) !important; + background: var(--gradient-premium) !important; + background-color: transparent !important; + -webkit-background-clip: text !important; + background-clip: text !important; + -webkit-text-fill-color: transparent !important; + font-weight: bold !important; + transform: translateZ(0); + will-change: background; + backface-visibility: hidden; + -webkit-backface-visibility: hidden; +} + +/* Fallback for browsers that don't support background-clip: text */ +@supports not (background-clip: text) { + .menu__list-item:not(.menu__list-item-collapsible) > .menu__link--active { + color: var(--ifm-color-primary) !important; + -webkit-text-fill-color: var(--ifm-color-primary) !important; + background: none !important; + } } /* Folder nodes - default light theme */ .theme-doc-sidebar-menu .menu__list-item-collapsible > .menu__link, .menu__list-item-collapsible > .menu__link { color: #535353; - -webkit-text-fill-color: #535353; } /* Parent folder (expanded but NOT selected) - light theme */ @@ -1417,6 +1482,11 @@ html[data-theme='dark'] .theme-doc-toc-desktop { background-clip: text !important; -webkit-text-fill-color: transparent !important; font-weight: bold !important; + /* Force hardware acceleration and separate layer to ensure gradient renders */ + transform: translateZ(0); + will-change: background; + backface-visibility: hidden; + -webkit-backface-visibility: hidden; } /* Folder nodes - default dark theme */ @@ -1442,12 +1512,38 @@ html[data-theme='dark'] .menu__list-item-collapsible--active > .menu__link.menu_ background-clip: text !important; -webkit-text-fill-color: transparent !important; font-weight: bold !important; + /* Force hardware acceleration and separate layer to ensure gradient renders */ + transform: translateZ(0); + will-change: background; + backface-visibility: hidden; + -webkit-backface-visibility: hidden; } -html[data-theme='dark'] .theme-doc-sidebar-menu .menu__link:hover { +html[data-theme='dark'] .theme-doc-sidebar-menu .menu__link:hover, +html[data-theme='dark'] nav[class*="domainSidebar"] .menu__link:hover { + transform: translateX(4px); text-shadow: 0 0 8px rgba(0, 112, 242, 0.6); } +/* Disable slide animation and color change on mobile - light theme only */ +@media (max-width: 996px) { + .theme-doc-sidebar-menu .menu__link:hover, + nav[class*="domainSidebar"] .menu__link:hover, + html[data-theme='dark'] .theme-doc-sidebar-menu .menu__link:hover, + html[data-theme='dark'] nav[class*="domainSidebar"] .menu__link:hover, + .menu__link--sublist-caret:hover [class*="categoryLinkLabel"], + .menu__list-item:not(.menu__list-item-collapsible) > .menu__link--active:hover { + transform: none !important; + } + + /* Disable color change on mobile in light theme */ + html[data-theme='light'] .theme-doc-sidebar-menu .menu__link:hover, + html[data-theme='light'] nav[class*="domainSidebar"] .menu__link:hover, + html[data-theme='light'] .menu__link:hover .sidebar-duplicate-counter { + color: inherit !important; + } +} + /* Content Container Card */ .theme-doc-markdown { background: transparent; @@ -1603,6 +1699,7 @@ body:has(.homepage-main) footer.footer { } /* On mobile, allow normal scrolling */ + @media (max-width: 768px) { .homepage-main { scroll-snap-type: none !important; diff --git a/src/plugins/security-headers/index.js b/src/plugins/security-headers/index.js index a02736e68b..5bc9b59c1c 100644 --- a/src/plugins/security-headers/index.js +++ b/src/plugins/security-headers/index.js @@ -50,13 +50,6 @@ module.exports = function (_context, _options) { content: 'nosniff', }, }, - { - tagName: 'meta', - attributes: { - 'http-equiv': 'X-Frame-Options', - content: 'DENY', - }, - }, { tagName: 'meta', attributes: { diff --git a/src/sections/TechnologyDomainSection.tsx b/src/sections/TechnologyDomainSection.tsx index 299fcb0ff5..16b0168b58 100644 --- a/src/sections/TechnologyDomainSection.tsx +++ b/src/sections/TechnologyDomainSection.tsx @@ -23,22 +23,22 @@ interface DomainCardProps { title: string; icon: string; }; + onNavigationStart: () => void; } -function DomainCard({ domain }: DomainCardProps): JSX.Element { - const setTechDomains = useSidebarFilterStore((state) => state.setTechDomains); +function DomainCard({ domain, onNavigationStart }: DomainCardProps): JSX.Element { const docsUrl = useBaseUrl('/docs/ref-arch'); const isHighlighted = domain.id === 'ai' || domain.id === 'data'; - const handleClick = () => { - setTechDomains([domain.id]); + const handlePointerDown = () => { + onNavigationStart(); }; return (
{iconMap[domain.id] || } @@ -113,7 +113,30 @@ export default function TechnologyDomainSection(): JSX.Element { const imgBaseUrl = useBaseUrl('/img/landingPage/'); const history = useHistory(); const setPartners = useSidebarFilterStore((state) => state.setPartners); - const setTechDomains = useSidebarFilterStore((state) => state.setTechDomains); + const [isNavigating, setIsNavigating] = React.useState(false); + + // Disable scroll-snap when navigating to prevent interference + React.useEffect(() => { + if (isNavigating) { + const originalHtmlSnap = document.documentElement.style.scrollSnapType; + const originalBodySnap = document.body.style.scrollSnapType; + + document.documentElement.style.scrollSnapType = 'none'; + document.body.style.scrollSnapType = 'none'; + + const timer = setTimeout(() => { + document.documentElement.style.scrollSnapType = originalHtmlSnap; + document.body.style.scrollSnapType = originalBodySnap; + setIsNavigating(false); + }, 300); + + return () => { + clearTimeout(timer); + document.documentElement.style.scrollSnapType = originalHtmlSnap; + document.body.style.scrollSnapType = originalBodySnap; + }; + } + }, [isNavigating]); // Helper function to get image URL with baseUrl const getImg = (name: string) => `${imgBaseUrl}${name}`; @@ -124,16 +147,13 @@ export default function TechnologyDomainSection(): JSX.Element { e.preventDefault(); const partners = item.filter?.partners ?? []; - const techDomains = item.filter?.techDomains ?? []; - // Set the global store + // Set the global store - only partners filter now if (partners.length) setPartners(partners); - if (techDomains.length) setTechDomains(techDomains); - // Build query string + // Build query string - only partners const params = new URLSearchParams(); if (partners.length) params.set('partners', partners.join(',')); - if (techDomains.length) params.set('techDomains', techDomains.join(',')); history.push(`${docsUrl}?${params.toString()}`); }; @@ -160,7 +180,11 @@ export default function TechnologyDomainSection(): JSX.Element {
{techDomain.map((domain) => ( - + setIsNavigating(true)} + /> ))}
@@ -183,4 +207,4 @@ export default function TechnologyDomainSection(): JSX.Element {
); -} +} \ No newline at end of file diff --git a/src/store/sidebar-store.ts b/src/store/sidebar-store.ts index a4b8b6d8c3..2bdafb91b6 100644 --- a/src/store/sidebar-store.ts +++ b/src/store/sidebar-store.ts @@ -7,6 +7,10 @@ interface SidebarFilterState { partners: string[]; setPartners: (partners: string[]) => void; + // Expanded domain categories (for collapsible sidebar) + expandedDomains: string[]; + setExpandedDomains: (domains: string[]) => void; + resetFilters: () => void; } @@ -17,5 +21,9 @@ export const useSidebarFilterStore = create((set) => ({ partners: [], setPartners: (partners) => set({ partners }), - resetFilters: () => set({ techDomains: [], partners: [] }), + // Start with all domains collapsed by default + expandedDomains: [], + setExpandedDomains: (expandedDomains) => set({ expandedDomains }), + + resetFilters: () => set({ techDomains: [], partners: [], expandedDomains: [] }), })); diff --git a/src/theme/DocSidebar/index.tsx b/src/theme/DocSidebar/index.tsx index f2ea4b623f..0da0b5eabd 100644 --- a/src/theme/DocSidebar/index.tsx +++ b/src/theme/DocSidebar/index.tsx @@ -1,17 +1,28 @@ import React, { useMemo, useEffect, useState } from 'react'; +import clsx from 'clsx'; import DocSidebar from '@theme-original/DocSidebar'; import DocSidebarItems from '@theme-original/DocSidebarItems'; -import { NavbarSecondaryMenuFiller, useWindowSize } from '@docusaurus/theme-common'; +import { NavbarSecondaryMenuFiller, useWindowSize, useThemeConfig } from '@docusaurus/theme-common'; import { useDocsSidebar } from '@docusaurus/plugin-content-docs/client'; import CollapsibleFilterBar from '@site/src/components/FilterBar/CollapsibleFilterBar'; +import CollapseButton from '@theme/DocSidebar/Desktop/CollapseButton'; import styles from './styles.module.css'; import { useSidebarFilterStore } from '@site/src/store/sidebar-store'; import useGlobalData from '@docusaurus/useGlobalData'; import tagsMap from '@site/src/constant/tagsMapping.json'; -import { useHistory } from '@docusaurus/router'; +import { useHistory, useLocation } from '@docusaurus/router'; import useBaseUrl from '@docusaurus/useBaseUrl'; import { logger } from '@site/src/utils/logger'; +// Domain definitions with labels +const DOMAIN_DEFINITIONS = [ + { id: 'ai', label: 'AI & Machine Learning' }, + { id: 'appdev', label: 'Application Dev. & Automation' }, + { id: 'data', label: 'Data & Analytics' }, + { id: 'integration', label: 'Integration' }, + { id: 'opsec', label: 'Operation & Security' }, +]; + const categoryIdToTags = Object.entries(tagsMap).reduce((acc, [tagKey, meta]) => { const cat = meta?.categoryid; if (!cat) return acc; @@ -82,18 +93,291 @@ function filterSidebarItems(items, selectedDomains, selectedPartners, docIdToTag return recurse(items); } +// Helper to count occurrences of a docId in items (recursive) +function countDocsInItems(items, docId): number { + let count = 0; + for (const item of items) { + if ((item.type === 'doc' || item.type === 'link') && (item.docId === docId || item.id === docId)) { + count++; + } else if (item.type === 'category') { + // Check if the category itself matches (for parent architectures with href) + if (item.href === docId) { + count++; + } + // Recursively check children + if (item.items) { + count += countDocsInItems(item.items, docId); + } + } + } + return count; +} + +// Helper to count total docs in items (recursive) - for badge display +function countTotalDocsInItems(items): number { + let count = 0; + for (const item of items) { + if (item.type === 'doc' || item.type === 'link') { + count++; + } else if (item.type === 'category') { + // Count the category itself if it has a link (parent architecture that's also a document) + if (item.link || item.docId || item.href) { + count++; + } + // Also count children recursively + if (item.items) { + count += countTotalDocsInItems(item.items); + } + } + } + return count; +} + +// Group sidebar items by technology domain (preserving hierarchy) +function groupSidebarByDomain(items, docIdToTags) { + const domainIds = DOMAIN_DEFINITIONS.map((d) => d.id); + const grouped: Record = {}; + const duplicateCounts: Record = {}; + + // Initialize empty arrays for each domain + domainIds.forEach((id) => { grouped[id] = []; }); + + // Helper: Check if a doc/link belongs to a domain + const itemBelongsToDomain = (item, domainId) => { + const itemId = item.docId || item.id || ''; + const tags = docIdToTags?.[itemId] || []; + + // Direct match + if (tags.includes(domainId)) return true; + + // Check category mappings + const domainTags = categoryIdToTags[domainId] || []; + return domainTags.some((tag) => tags.includes(tag)); + }; + + // Helper: Recursively check if a category contains any docs for this domain + const categoryHasDocsForDomain = (category, domainId): boolean => { + if (!category.items || category.items.length === 0) return false; + + for (const child of category.items) { + if ((child.type === 'doc' || child.type === 'link') && itemBelongsToDomain(child, domainId)) { + return true; + } + if (child.type === 'category' && categoryHasDocsForDomain(child, domainId)) { + return true; + } + } + return false; + }; + + // First pass: Collect ALL document IDs (including parent architectures) from the entire sidebar + const collectAllDocIds = (itemList: any[]): Set => { + const docIds = new Set(); + + const traverse = (item: any) => { + if (item.type === 'doc' || item.type === 'link') { + const docId = item.docId || item.id; + if (docId) docIds.add(docId); + } else if (item.type === 'category') { + // If category has href and children, it's a parent architecture (expandable document) + if (item.href && item.items && item.items.length > 0) { + docIds.add(item.href); + } + // Recursively process children + if (item.items) { + item.items.forEach(traverse); + } + } + }; + + itemList.forEach(traverse); + return docIds; + }; + + // Collect all doc IDs first + const allDocIds = collectAllDocIds(items); + + // Helper: Recursively filter category items by domain + const filterCategoryForDomain = (category, domainId) => { + const filteredItems = []; + + for (const child of category.items || []) { + if ((child.type === 'doc' || child.type === 'link') && itemBelongsToDomain(child, domainId)) { + filteredItems.push(child); + } else if (child.type === 'category' && categoryHasDocsForDomain(child, domainId)) { + filteredItems.push(filterCategoryForDomain(child, domainId)); + } + } + + return { ...category, items: filteredItems }; + }; + + // Group items by domain, preserving category structure + items.forEach((item) => { + domainIds.forEach((domainId) => { + if ((item.type === 'doc' || item.type === 'link') && itemBelongsToDomain(item, domainId)) { + grouped[domainId].push(item); + } else if (item.type === 'category' && categoryHasDocsForDomain(item, domainId)) { + const filteredCategory = filterCategoryForDomain(item, domainId); + grouped[domainId].push(filteredCategory); + } + }); + }); + + // Calculate duplicate counts for all doc IDs + allDocIds.forEach((docId) => { + let count = 0; + domainIds.forEach((domainId) => { + const hasDoc = countDocsInItems(grouped[domainId], docId) > 0; + if (hasDoc) count++; + }); + if (count > 1) { + duplicateCounts[docId] = count - 1; + } + }); + + return { grouped, duplicateCounts }; +} + +// Filter grouped items by partner (preserving hierarchy) +function filterGroupedByPartner(grouped, selectedPartners, docIdToTags) { + if (!selectedPartners?.length) return grouped; + + const expand = (ids) => + Array.from(new Set(ids.flatMap((id) => [id, ...(categoryIdToTags[id] ?? [])]))); + const partnerTags = expand(selectedPartners); + + // Helper: Check if item matches partner filter + const itemMatchesPartner = (item) => { + const itemId = item.docId || item.id || ''; + const tags = docIdToTags?.[itemId] || []; + return partnerTags.some((p) => tags.includes(p)); + }; + + // Helper: Recursively filter category + const filterCategory = (category) => { + const filteredItems = []; + for (const child of category.items || []) { + if ((child.type === 'doc' || child.type === 'link') && itemMatchesPartner(child)) { + filteredItems.push(child); + } else if (child.type === 'category') { + const filteredChild = filterCategory(child); + if (filteredChild.items.length > 0) { + filteredItems.push(filteredChild); + } + } + } + return { ...category, items: filteredItems }; + }; + + const filtered: Record = {}; + Object.entries(grouped).forEach(([domainId, items]) => { + filtered[domainId] = []; + for (const item of items) { + if ((item.type === 'doc' || item.type === 'link') && itemMatchesPartner(item)) { + filtered[domainId].push(item); + } else if (item.type === 'category') { + const filteredCategory = filterCategory(item); + if (filteredCategory.items.length > 0) { + filtered[domainId].push(filteredCategory); + } + } + } + }); + + return filtered; +} + // ============================================================================ -// Desktop Version +// Shared Helper Functions // ============================================================================ -// Constant options defined outside component to avoid recreating on each render -const TECH_DOMAIN_OPTIONS = [ - { value: 'ai', label: 'AI & Machine Learning' }, - { value: 'appdev', label: 'Application Dev. & Automation' }, - { value: 'data', label: 'Data & Analytics' }, - { value: 'integration', label: 'Integration' }, - { value: 'opsec', label: 'Operation & Security' } -]; +// Collect unique doc IDs from grouped items (for result count display) +function collectUniqueDocIds(groupedItems: Record): Set { + const uniqueDocIds = new Set(); + + const traverse = (items: any[]) => { + items.forEach(item => { + if (item.type === 'doc' || item.type === 'link') { + const id = item.docId || item.id || ''; + if (id) uniqueDocIds.add(id); + } else if (item.type === 'category') { + // Count the category itself if it has href (parent architecture) + if (item.href) { + uniqueDocIds.add(item.href); + } + // Recursively count children + if (item.items) { + traverse(item.items); + } + } + }); + }; + + Object.values(groupedItems).forEach(items => traverse(items)); + return uniqueDocIds; +} + +// Add duplicate counters to item customProps recursively +function addDuplicateCountersToItems(items: any[], duplicateCounts: Record): any[] { + return items.map(item => { + if (item.type === 'category') { + // For parent architectures (categories with href), use href for matching + const categoryId = item.href || item.docId || item.id || ''; + const categoryDuplicateCount = duplicateCounts[categoryId]; + + return { + ...item, + customProps: { + ...item.customProps, + ...(categoryDuplicateCount && { duplicateCount: categoryDuplicateCount }) + }, + items: item.items ? addDuplicateCountersToItems(item.items, duplicateCounts) : [] + }; + } else if (item.type === 'doc' || item.type === 'link') { + const itemId = item.docId || item.id || ''; + const duplicateCount = duplicateCounts[itemId]; + + return { + ...item, + customProps: { + ...item.customProps, + ...(duplicateCount && { duplicateCount }) + } + }; + } + + return item; + }); +} + +// Transform domain-grouped data into Docusaurus category structure +function buildDomainCategories( + filteredGrouped: Record, + duplicateCounts: Record, + expandedDomains: string[] +) { + return DOMAIN_DEFINITIONS.map(domain => { + const domainItems = filteredGrouped[domain.id] || []; + const docCount = countTotalDocsInItems(domainItems); + const itemsWithCounters = addDuplicateCountersToItems(domainItems, duplicateCounts); + + return { + type: 'category', + label: `${domain.label} (${docCount})`, + items: itemsWithCounters, + collapsible: true, + collapsed: !expandedDomains.includes(domain.id), + customProps: { + domainId: domain.id + } + }; + }).filter(category => category.items.length > 0); +} + +// ============================================================================ +// Desktop Version +// ============================================================================ const PARTNER_OPTIONS = [ { value: 'aws', label: 'Amazon Web Services' }, { value: 'azure', label: 'Microsoft Azure' }, @@ -108,26 +392,34 @@ function DocSidebarDesktop(props) { const tagsDocId = useGlobalData()['docusaurus-tags-plugin'].default?.docIdToTags; const sidebar = useDocsSidebar(); const shouldShowFilters = sidebar?.name === 'refarchSidebar'; + const location = useLocation(); + const { + navbar: { hideOnScroll }, + docs: { + sidebar: { hideable }, + }, + } = useThemeConfig(); - const techDomains = useSidebarFilterStore((state) => state.techDomains); - const setTechDomains = useSidebarFilterStore((state) => state.setTechDomains); const partners = useSidebarFilterStore((state) => state.partners); const setPartners = useSidebarFilterStore((state) => state.setPartners); const resetFilters = useSidebarFilterStore((state) => state.resetFilters); + const expandedDomains = useSidebarFilterStore((state) => state.expandedDomains); const [searchTerm, setSearchTerm] = useState(''); - // All hooks must be called before any conditional returns - const filteredSidebar = useMemo( - () => filterSidebarItems(props.sidebar, techDomains, partners, tagsDocId), - [props.sidebar, techDomains, partners, tagsDocId] + // Group sidebar items by domain + const grouped = useMemo( + () => groupSidebarByDomain(props.sidebar, tagsDocId), + [props.sidebar, tagsDocId] ); - // Convert string arrays to Option arrays - const selectedTechDomainOptions = useMemo( - () => TECH_DOMAIN_OPTIONS.filter(opt => techDomains.includes(opt.value)), - [techDomains] + // Filter by selected partners + const filteredGrouped = useMemo( + () => filterGroupedByPartner(grouped.grouped, partners, tagsDocId), + [grouped.grouped, partners, tagsDocId] ); + + // Convert string arrays to Option arrays const selectedPartnerOptions = useMemo( () => PARTNER_OPTIONS.filter(opt => partners.includes(opt.value)), [partners] @@ -137,17 +429,6 @@ function DocSidebarDesktop(props) { return ; } - const handleTechDomainsChange = (selected) => { - const selectedKeys = selected.map(opt => opt.value); - setTechDomains(selectedKeys); - - // Sync URL - const params = new URLSearchParams(location.search); - if (selectedKeys.length) params.set('techDomains', selectedKeys.join(',')); - else params.delete('techDomains'); - window.history.replaceState({}, '', `${location.pathname}?${params.toString()}`); - }; - const handlePartnersChange = (selected) => { const selectedKeys = selected.map(opt => opt.value); setPartners(selectedKeys); @@ -156,6 +437,7 @@ function DocSidebarDesktop(props) { const params = new URLSearchParams(location.search); if (selectedKeys.length) params.set('partners', selectedKeys.join(',')); else params.delete('partners'); + params.delete('techDomains'); // Remove old techDomains param window.history.replaceState({}, '', `${location.pathname}?${params.toString()}`); }; @@ -165,43 +447,50 @@ function DocSidebarDesktop(props) { window.history.replaceState({}, '', location.pathname); }; - // Count total filtered docs - const countDocs = (items) => { - let count = 0; - items.forEach(item => { - if (item.type === 'doc' || item.type === 'link') { - count++; - } else if (item.type === 'category' && item.items) { - count += countDocs(item.items); - } - }); - return count; - }; + // Count unique documents (across all domains, no duplicates) + const resultCount = collectUniqueDocIds(filteredGrouped).size; - const resultCount = countDocs(filteredSidebar); - const newProps = { ...props, sidebar: filteredSidebar }; + // Transform domain-grouped data into Docusaurus category structure + const domainCategories = buildDomainCategories( + filteredGrouped, + grouped.duplicateCounts, + expandedDomains + ); return ( -
-
- 0 || partners.length > 0 || searchTerm.length > 0} - searchTerm={searchTerm} - onSearchChange={setSearchTerm} - resultCount={resultCount} +
+
+ 0 || searchTerm.length > 0} + searchTerm={searchTerm} + onSearchChange={setSearchTerm} + resultCount={resultCount} + /> +
+
+
+
-
- -
+ + {hideable && } +
+
); } @@ -210,22 +499,27 @@ function DocSidebarDesktop(props) { // ============================================================================ function FilteredMobileSidebarView({ sidebar, path, onItemClick }) { const tagsDocId = useGlobalData()['docusaurus-tags-plugin'].default?.docIdToTags; - const techDomains = useSidebarFilterStore((state) => state.techDomains); - const setTechDomains = useSidebarFilterStore((state) => state.setTechDomains); const partners = useSidebarFilterStore((state) => state.partners); const setPartners = useSidebarFilterStore((state) => state.setPartners); const resetFilters = useSidebarFilterStore((state) => state.resetFilters); + const expandedDomains = useSidebarFilterStore((state) => state.expandedDomains); const [searchTerm, setSearchTerm] = useState(''); // Convert string arrays to Option arrays - const selectedTechDomainOptions = TECH_DOMAIN_OPTIONS.filter(opt => techDomains.includes(opt.value)); const selectedPartnerOptions = PARTNER_OPTIONS.filter(opt => partners.includes(opt.value)); - const handleTechDomainsChange = (selected) => { - const selectedKeys = selected.map(opt => opt.value); - setTechDomains(selectedKeys); - }; + // Group sidebar items by domain + const grouped = useMemo( + () => groupSidebarByDomain(sidebar, tagsDocId), + [sidebar, tagsDocId] + ); + + // Filter by selected partners + const filteredGrouped = useMemo( + () => filterGroupedByPartner(grouped.grouped, partners, tagsDocId), + [grouped.grouped, partners, tagsDocId] + ); const handlePartnersChange = (selected) => { const selectedKeys = selected.map(opt => opt.value); @@ -238,42 +532,37 @@ function FilteredMobileSidebarView({ sidebar, path, onItemClick }) { window.history.replaceState({}, '', location.pathname); }; - const filteredSidebar = useMemo( - () => filterSidebarItems(sidebar, techDomains, partners, tagsDocId), - [sidebar, techDomains, partners, tagsDocId] - ); - - // Count total filtered docs - const countDocs = (items) => { - let count = 0; - items.forEach(item => { - if (item.type === 'doc' || item.type === 'link') { - count++; - } else if (item.type === 'category' && item.items) { - count += countDocs(item.items); - } - }); - return count; - }; + // Count unique documents + const resultCount = collectUniqueDocIds(filteredGrouped).size; - const resultCount = countDocs(filteredSidebar); + // Transform domain-grouped data into Docusaurus category structure + const domainCategories = buildDomainCategories( + filteredGrouped, + grouped.duplicateCounts, + expandedDomains + ); return ( <> 0 || partners.length > 0 || searchTerm.length > 0} + isResetEnabled={partners.length > 0 || searchTerm.length > 0} searchTerm={searchTerm} onSearchChange={setSearchTerm} resultCount={resultCount} /> - + ); } @@ -306,27 +595,74 @@ function DocSidebarMobile({ shouldShowFilters, ...props }) { const DocSidebarDesktopMemo = React.memo(DocSidebarDesktop); const DocSidebarMobileMemo = React.memo(DocSidebarMobile); +// Helper function to find a doc in sidebar by path +function findDocByPath(items, pathname) { + for (const item of items) { + if (item.type === 'doc' || item.type === 'link') { + if (item.href === pathname || pathname.startsWith(item.href)) { + return item.docId || item.id; + } + } else if (item.type === 'category' && item.items) { + const found = findDocByPath(item.items, pathname); + if (found) return found; + } + } + return null; +} + export default function DocSidebarWrapper(props) { const windowSize = useWindowSize(); const sidebarContext = useDocsSidebar(); const shouldShowFilters = sidebarContext?.name === 'refarchSidebar'; const setPartners = useSidebarFilterStore((state) => state.setPartners); - const setTechDomains = useSidebarFilterStore((state) => state.setTechDomains); + const setExpandedDomains = useSidebarFilterStore((state) => state.setExpandedDomains); const resetFilters = useSidebarFilterStore((state) => state.resetFilters); const history = useHistory(); const docsBase = useBaseUrl('/docs'); + const location = useLocation(); + const tagsDocId = useGlobalData()['docusaurus-tags-plugin']?.default?.docIdToTags; useEffect(() => { if (!location.pathname.startsWith(docsBase)) return; + if (!shouldShowFilters) return; // Only run for ref-arch sidebar + if (!tagsDocId) return; // Wait for tags data to load + if (!props.sidebar) return; // Wait for sidebar to load const params = new URLSearchParams(location.search); - const partnersParam = params.get('partners'); - const techDomainsParam = params.get('techDomains'); + const expandedParam = params.get('expanded'); if (partnersParam) setPartners(partnersParam.split(',')); - if (techDomainsParam) setTechDomains(techDomainsParam.split(',')); - }, [docsBase, setPartners, setTechDomains]); + + // If expanded param is set, use it (explicit choice from landing page) + if (expandedParam) { + setExpandedDomains(expandedParam.split(',')); + return; + } + + // Auto-expand domains for the current doc + // Find the doc ID by matching the current pathname to sidebar items + const docId = findDocByPath(props.sidebar, location.pathname); + + // If we're on a specific doc page + if (docId && tagsDocId[docId]) { + const docTags = tagsDocId[docId] || []; + const domainIds = DOMAIN_DEFINITIONS.map((d) => d.id); + + // Find which domains this doc belongs to + const matchingDomains = domainIds.filter((domainId) => { + // Direct match + if (docTags.includes(domainId)) return true; + // Check category mappings + const domainTags = categoryIdToTags[domainId] || []; + return domainTags.some((tag) => docTags.includes(tag)); + }); + + if (matchingDomains.length > 0) { + setExpandedDomains(matchingDomains); + } + } + }, [location.pathname, location.search, docsBase, setPartners, setExpandedDomains, shouldShowFilters, tagsDocId, props.sidebar]); useEffect(() => { diff --git a/src/theme/DocSidebar/styles.module.css b/src/theme/DocSidebar/styles.module.css index fd7662ea90..5d4b880403 100644 --- a/src/theme/DocSidebar/styles.module.css +++ b/src/theme/DocSidebar/styles.module.css @@ -1,7 +1,6 @@ -.refarchSidebarActive { - height: 100%; - display: flex; - flex-direction: column; +/* Make sidebarViewport take full width of its container */ +:global([class*="sidebarViewport"]) { + width: 100% !important; } .scrollableContent { @@ -9,31 +8,10 @@ flex-grow: 1; } -:global(html[data-sidebar-collapsed='true']) .scrollableContent { - display: none; -} - -:global(html[data-sidebar-collapsed='true']) .sidebarWithFiltersContainer > div:first-child { - display: none !important; -} - -:global(.theme-doc-sidebar-container.theme-doc-sidebar-container-hidden) .sidebarWithFiltersContainer > div:first-child { - display: none !important; -} - -:global([class*="docSidebarContainer"][class*="Hidden"]) .sidebarWithFiltersContainer > div:first-child { - display: none !important; -} - -.theme-doc-sidebar-container-collapsed .custom-sidebar-filters-container { - display: none; -} - -.sidebarMenuList .menu.thin-scrollbar { - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; - overflow-x: hidden; +/* Hide filter bar and sidebar content when collapsed */ +.sidebarHidden { + opacity: 0 !important; + visibility: hidden !important; } .sidebarWithFiltersContainer { @@ -51,6 +29,14 @@ flex-direction: column; } +.sidebar { + border-right: 1px solid var(--ifm-toc-border-color); + display: flex; + flex-direction: column; + height: 100%; + width: var(--doc-sidebar-width); +} + [class^="sidebar_"] { border-right: 1px solid var(--ifm-toc-border-color); } @@ -58,3 +44,58 @@ :global(button[class*="collapseSidebarButton"]) { border-right: none !important; } + +/* Ensure expand button icon is visible */ +:global([class*="expandButton"]) { + cursor: pointer; +} + +:global([class*="expandButtonIcon"]) { + width: 20px; + height: 20px; + display: inline-block; +} + +/* Domain Sidebar Categories */ +.domainSidebar { + display: flex; + flex-direction: column; + padding: 0.5rem; + overflow-y: auto; + flex: 1; +} + +/* Apply Docusaurus thin-scrollbar styling */ +.domainSidebar.thin-scrollbar::-webkit-scrollbar { + width: 7px; +} + +.domainSidebar.thin-scrollbar::-webkit-scrollbar-track { + background: transparent; + border-radius: 10px; +} + +.domainSidebar.thin-scrollbar::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.2); + border-radius: 10px; +} + +.domainSidebar.thin-scrollbar::-webkit-scrollbar-thumb:hover { + background-color: rgba(0, 0, 0, 0.3); +} + +[data-theme='dark'] .domainSidebar.thin-scrollbar::-webkit-scrollbar-thumb { + background-color: rgba(255, 255, 255, 0.2); +} + +[data-theme='dark'] .domainSidebar.thin-scrollbar::-webkit-scrollbar-thumb:hover { + background-color: rgba(255, 255, 255, 0.3); +} + +.domainSidebarMobile { + display: flex; + flex-direction: column; + padding: 0.5rem 0; +} + +/* Remove custom viewport styling - let Docusaurus handle it */ diff --git a/src/theme/DocSidebarItem/Category/index.tsx b/src/theme/DocSidebarItem/Category/index.tsx new file mode 100644 index 0000000000..590fa86801 --- /dev/null +++ b/src/theme/DocSidebarItem/Category/index.tsx @@ -0,0 +1,325 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, { + type ComponentProps, + type ReactNode, + useEffect, + useMemo, +} from 'react'; +import clsx from 'clsx'; +import { + ThemeClassNames, + useThemeConfig, + usePrevious, + Collapsible, + useCollapsible, +} from '@docusaurus/theme-common'; +import {isSamePath} from '@docusaurus/theme-common/internal'; +import { + isActiveSidebarItem, + findFirstSidebarItemLink, + useDocSidebarItemsExpandedState, + useVisibleSidebarItems, +} from '@docusaurus/plugin-content-docs/client'; +import Link from '@docusaurus/Link'; +import {translate} from '@docusaurus/Translate'; +import useIsBrowser from '@docusaurus/useIsBrowser'; +import DocSidebarItems from '@theme/DocSidebarItems'; +import DocSidebarItemLink from '@theme/DocSidebarItem/Link'; +import type {Props} from '@theme/DocSidebarItem/Category'; + +import type { + PropSidebarItemCategory, + PropSidebarItemLink, +} from '@docusaurus/plugin-content-docs'; +import DuplicateCounter from '@theme/DocSidebarItem/DuplicateCounter'; +import styles from './styles.module.css'; + +// If we navigate to a category and it becomes active, it should automatically +// expand itself +function useAutoExpandActiveCategory({ + isActive, + collapsed, + updateCollapsed, + activePath, +}: { + isActive: boolean; + collapsed: boolean; + updateCollapsed: (b: boolean) => void; + activePath: string; +}) { + const wasActive = usePrevious(isActive); + const previousActivePath = usePrevious(activePath); + useEffect(() => { + const justBecameActive = isActive && !wasActive; + const stillActiveButPathChanged = + isActive && wasActive && activePath !== previousActivePath; + if ((justBecameActive || stillActiveButPathChanged) && collapsed) { + updateCollapsed(false); + } + }, [ + isActive, + wasActive, + collapsed, + updateCollapsed, + activePath, + previousActivePath, + ]); +} + +/** + * When a collapsible category has no link, we still link it to its first child + * during SSR as a temporary fallback. This allows to be able to navigate inside + * the category even when JS fails to load, is delayed or simply disabled + * React hydration becomes an optional progressive enhancement + * see https://github.com/facebookincubator/infima/issues/36#issuecomment-772543188 + * see https://github.com/facebook/docusaurus/issues/3030 + */ +function useCategoryHrefWithSSRFallback( + item: Props['item'], +): string | undefined { + const isBrowser = useIsBrowser(); + return useMemo(() => { + if (item.href && !item.linkUnlisted) { + return item.href; + } + // In these cases, it's not necessary to render a fallback + // We skip the "findFirstCategoryLink" computation + if (isBrowser || !item.collapsible) { + return undefined; + } + return findFirstSidebarItemLink(item); + }, [item, isBrowser]); +} + +function CollapseButton({ + collapsed, + categoryLabel, + onClick, +}: { + collapsed: boolean; + categoryLabel: string; + onClick: ComponentProps<'button'>['onClick']; +}) { + return ( +