Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions blocks/skills/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const TAB_PROMPTS = 'prompts';
const TAB_MCPS = 'mcps';
const TAB_MARKETPLACE = 'marketplace';
const TAB_MEMORY = 'memory';
const TAB_CONTEXT = 'context'; // UI label: "Enterprise Context"; embeds the Experience Governance MFE

/** Per-tab metadata rendered by the catalog tab strip. */
const CATALOG_TABS = [
Expand All @@ -14,6 +15,7 @@ const CATALOG_TABS = [
{ id: TAB_MCPS, label: 'MCPs' },
// { id: TAB_MARKETPLACE, label: 'Marketplace', disabled: true },
{ id: TAB_MEMORY, label: 'Memory' },
{ id: TAB_CONTEXT, label: 'Enterprise Context' },
];

const TAB_LABEL_MAP = Object.fromEntries(CATALOG_TABS.map((t) => [t.id, t.label]));
Expand All @@ -24,6 +26,7 @@ const TAB_DESCRIPTIONS = {
[TAB_SKILLS]: 'Markdown instructions that guide the assistant\'s behavior.',
[TAB_MCPS]: 'Model Context Protocol servers that give the assistant access to external tools.',
[TAB_MARKETPLACE]: 'Discover and install 1st and 3rd party plugins.',
[TAB_CONTEXT]: 'Brand governance context and enterprise content checks.',
};

/** Per-tab metadata for the "new" button label and the opener method name. */
Expand Down Expand Up @@ -126,6 +129,25 @@ const TOOLS_FILTER_THRESHOLD = 6;
const CHAT_DRAWER_WIDTH = 380;
const AGENT_USAGE_ICON = '\u26A1';

/**
* Experience Governance MFE embed config. SPA Pipeline serves the built bundle
* at this same fixed path on every environment, and only the host changes. See
* utils/egov-embed.js for how EMBED_URLS is picked at runtime.
*/
const EGOV_EMBED_PATH = '/solutions/Adobe-AEM-Foundation-experience-governance-mfe/static-assets/resources/embed.html';

const EGOV_MFE = {
CHANNEL: '@aemsec/experience-governance-mfe',
PROTOCOL: '@assets/microfrontend/MessageRpc',
VERSION: '1.0.0',
EMBED_URLS: {
local: `https://localhost.corp.adobe.com:8443${EGOV_EMBED_PATH}`,
qa: `https://experience-qa.adobe.com${EGOV_EMBED_PATH}`,
stage: `https://experience-stage.adobe.com${EGOV_EMBED_PATH}`,
prod: `https://experience.adobe.com${EGOV_EMBED_PATH}`,
},
};

export {
AGENT_USAGE_ICON,
BUILTIN_AGENTS,
Expand All @@ -136,6 +158,7 @@ export {
CATEGORY_OPTIONS,
CHAT_DRAWER_WIDTH,
DEP_TREE_MAX_TOOLS,
EGOV_MFE,
TAB_DESCRIPTIONS,
TAB_LABEL_MAP,
FRESH_FORM_STATE,
Expand All @@ -144,6 +167,7 @@ export {
STATUS_TYPE,
TAB_ACTIONS,
TAB_AGENTS,
TAB_CONTEXT,
TAB_MARKETPLACE,
TAB_MCPS,
TAB_MEMORY,
Expand Down
16 changes: 16 additions & 0 deletions blocks/skills/editor-panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,22 @@
overflow: hidden;
}

/* Context tab: fill the panel with the embedded governance MFE */
.editor-body-context {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}

.egov-mfe-iframe {
flex: 1;
min-height: 0;
width: 100%;
border: 0;
}

.editor-footer {
position: sticky;
bottom: 0;
Expand Down
50 changes: 49 additions & 1 deletion blocks/skills/nx-skills-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
FRESH_FORM_STATE,
STATUS,
STATUS_TYPE,
TAB_CONTEXT,
} from './constants.js';
import {
renderTopNav,
Expand All @@ -53,6 +54,9 @@ import { ensureSkillFrontmatter } from './utils/skill-frontmatter.js';
import {
createReadOnlyViewer, createEditor, replaceDoc, destroyEditor,
} from './utils/codemirror-loader.js';
import { setupEgovBridge } from './utils/egov-bridge.js';
import { resolveEgovMfeEnv, resolveEgovPath, setEgovPath } from './utils/egov-embed.js';
import { getToken } from './utils/da-fetch.js';
import {
onMessage,
sendMessage,
Expand Down Expand Up @@ -273,6 +277,7 @@ class NxSkillsEditor extends LitElement {
this._disposeCMModal();
this._disposeMemoryCM();
this._disposeSkillCM();
this._disposeEgovBridge();
}

async updated(changed) {
Expand Down Expand Up @@ -304,6 +309,16 @@ class NxSkillsEditor extends LitElement {
if ((changed?.has('_memory') || changed?.has('_catalogTab')) && this._catalogTab === 'memory' && this._memory) {
this.updateComplete.then(() => this._mountMemoryCM());
}
if (changed?.has('_catalogTab') && this._catalogTab === TAB_CONTEXT && !this._egovBridge) {
this.updateComplete.then(() => this._mountEgovBridge());
}
// The `_egovBridge` check narrows this to a genuine tab exit. No bridge
// exists on the initial tab assignment, where clearing `?egovPath=` would
// wipe an incoming deep link before it is read.
if (changed?.has('_catalogTab') && this._catalogTab !== TAB_CONTEXT && this._egovBridge) {
this._disposeEgovBridge();
setEgovPath('/');
}
// ── Skill body CM: mount when skill form appears, dispose when it hides ──
const skillFormVisible = this._isEditorOpen && this._catalogTab === 'skills'
&& !(this._viewingSkillId && !this._isFormEdit);
Expand Down Expand Up @@ -711,7 +726,7 @@ class NxSkillsEditor extends LitElement {
this._isFormDirty = true;
} else {
this._clearForm();
this._isEditorOpen = newTab === 'memory';
this._isEditorOpen = newTab === 'memory' || newTab === TAB_CONTEXT;
}

this._pushTabState(newTab);
Expand Down Expand Up @@ -1110,6 +1125,39 @@ class NxSkillsEditor extends LitElement {
this._memoryCM = null;
}

async _mountEgovBridge() {
this._disposeEgovBridge();
const iframe = this.shadowRoot.querySelector('.egov-mfe-iframe');
Comment thread
AlexRRR marked this conversation as resolved.
if (!iframe) return;
let imsOrg;
try { imsOrg = (await window.adobeIMS?.getProfile?.())?.ownerOrg; } catch { /* anonymous session */ }
this._egovBridge = setupEgovBridge({
iframe,
getProps: () => ({
path: resolveEgovPath(),
env: resolveEgovMfeEnv(),
imsToken: getToken(),
imsOrg,
}),
// The MFE says whether each navigation deserves its own history entry.
// We ignore that and always replace, so Back never lands inside this tab.
// Adding entries first needs a popstate listener that feeds the path back
// into the MFE. Without one, Back would change the URL while the iframe
// kept showing the same screen.
onNavigate: (path) => setEgovPath(path),
});
}

/**
* Tears down the bridge only. Clearing `?egovPath=` is the caller's job (see
* `updated`), since this also runs defensively from `_mountEgovBridge`, which
* is about to read that param.
*/
_disposeEgovBridge() {
this._egovBridge?.destroy();
this._egovBridge = null;
}

async _mountSkillCM() {
this._disposeSkillCM();
const host = this.shadowRoot.querySelector('.skill-body-cm-host');
Expand Down
29 changes: 22 additions & 7 deletions blocks/skills/renderers.js

Large diffs are not rendered by default.

Loading
Loading