@dataverse-kit/agent-kit 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -0
- package/dist/core.cjs +532 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +278 -0
- package/dist/core.d.ts +278 -0
- package/dist/core.mjs +482 -0
- package/dist/core.mjs.map +1 -0
- package/dist/host.cjs +113 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.d.cts +3 -0
- package/dist/host.d.ts +3 -0
- package/dist/host.mjs +74 -0
- package/dist/host.mjs.map +1 -0
- package/dist/index-DGN6eYnp.d.cts +103 -0
- package/dist/index-DGN6eYnp.d.ts +103 -0
- package/dist/index.cjs +3145 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +863 -0
- package/dist/index.d.ts +863 -0
- package/dist/index.mjs +3068 -0
- package/dist/index.mjs.map +1 -0
- package/dist/protocol-CZJ1v9Hq.d.cts +270 -0
- package/dist/protocol-CZJ1v9Hq.d.ts +270 -0
- package/dist/services.cjs +333 -0
- package/dist/services.cjs.map +1 -0
- package/dist/services.d.cts +118 -0
- package/dist/services.d.ts +118 -0
- package/dist/services.mjs +300 -0
- package/dist/services.mjs.map +1 -0
- package/package.json +89 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/host/index.ts","../src/host/AgentKitHostProvider.tsx","../src/theme/index.ts","../src/core/ids.ts","../src/host/idPrefix.ts"],"sourcesContent":["// @dataverse-kit/agent-kit/host — the provider chain a host needs, and nothing else.\n//\n// Separate entry so a PCF or custom-page host can import the wrapper without pulling the\n// component barrel.\nexport { AgentKitHostProvider } from './AgentKitHostProvider';\nexport type { AgentKitHostProviderProps } from './AgentKitHostProvider';\nexport { nextAgentIdPrefix } from './idPrefix';\nexport { resolveHostTheme, createAgentTheme, createAgentDarkTheme, agentTheme, agentDarkTheme } from '../theme';\n","import * as React from 'react';\nimport { FluentProvider, IdPrefixProvider, type Theme } from '@fluentui/react-components';\nimport { resolveHostTheme } from '../theme';\nimport { nextAgentIdPrefix } from './idPrefix';\n\nexport interface AgentKitHostProviderProps {\n children?: React.ReactNode;\n /** An explicit theme wins over everything. */\n theme?: Theme;\n /** The Dynamics host's theme — `context.fluentDesignLanguage?.tokenTheme` in a PCF. */\n hostTheme?: Theme;\n isDarkTheme?: boolean;\n /**\n * The document Fluent should treat as its own.\n *\n * ★★ THIS IS THE PCF ESCAPE HATCH, and it is why it is a prop rather than something the\n * kit decides. Fluent v9's tabster is a per-WINDOW singleton: `createTabster` returns\n * `win.__tabsterInstance` if one exists. A Dynamics form installs tabster 8.2.0, whose\n * core has no `attrHandlers`, while a bundled control ships 8.8.x — so the moment a\n * component reaches a `get*` factory (`TabList` -> `useArrowNavigationGroup` -> `getMover`),\n * `core.attrHandlers.set` throws and React unmounts the whole tree.\n *\n * `useTabster` reads `targetDocument` from Fluent's context and hands\n * `targetDocument.defaultView` to `createTabster`. So a host that passes a proxied\n * document with a private `__tabsterInstance` slot gets its own core, and everything\n * below inherits it — with no change to any agent-kit component.\n *\n * The proxy itself is NOT shipped here; it lives with the PCF gallery\n * (`controls/_shared/tabsterIsolation.ts`). A host writes one line:\n * `targetDocument={createIsolatedTargetDocument(document)}`\n *\n * ★ It isolates tabster and portal mount nodes, NOT Griffel: `FluentProvider` consumes\n * the ambient Griffel renderer rather than building one from this prop. That is\n * harmless for the proxy case (styles land in the real <head> either way) but means\n * this prop alone does NOT make the kit work in a separate document or shadow root —\n * that additionally needs `<RendererProvider renderer={createDOMRenderer(doc)}>`.\n */\n targetDocument?: Document;\n /** Override the generated prefix. Only for tests that need a stable value. */\n idPrefix?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * The host wrapper: `IdPrefixProvider` ▸ `FluentProvider` ▸ children.\n *\n * ★ No agent-kit COMPONENT renders a provider — that is surface-kit's convention and this\n * kit keeps it, so a host that already has a `FluentProvider` is not given a second one.\n * This is an opt-in export for hosts that do not, and it exists for a second reason: a\n * rule that lives only in a README is a rule nothing can test. Shipping the wrapper gives\n * the IdPrefix gate a subject inside `src/`, so its sabotage lands on real code instead of\n * on the test's own fixture.\n *\n * ★ Ordering is load-bearing: `IdPrefixProvider` must be OUTSIDE `FluentProvider`, because\n * the provider reads the prefix while generating the id for its own theme <style> tag.\n */\nexport function AgentKitHostProvider(props: AgentKitHostProviderProps): React.ReactElement {\n const { children, theme, hostTheme, isDarkTheme, targetDocument, className, style } = props;\n\n // Generated ONCE per mounted instance — not per render, and not shared across instances.\n const [generated] = React.useState(nextAgentIdPrefix);\n const idPrefix = props.idPrefix ?? generated;\n\n const resolved = React.useMemo(\n () => resolveHostTheme({ theme, hostTheme, isDarkTheme }),\n [theme, hostTheme, isDarkTheme],\n );\n\n return (\n <IdPrefixProvider value={idPrefix}>\n <FluentProvider\n theme={resolved}\n targetDocument={targetDocument}\n className={className}\n style={style}\n >\n {children}\n </FluentProvider>\n </IdPrefixProvider>\n );\n}\n","import type { BrandVariants, Theme } from '@fluentui/react-components';\nimport {\n createDynamicsV9Theme,\n createDynamicsV9DarkTheme,\n dynamicsBrandRamp,\n} from '@dataverse-kit/fluent-theme';\nimport { typography, radius } from '@dataverse-kit/design-tokens';\n\n/**\n * The Dynamics CE look, for conversational surfaces.\n *\n * ★ The brand ramp and the base builders are NOT declared here — they come from\n * `@dataverse-kit/fluent-theme`, the single declaration shared with grid-kit/v9 and\n * surface-kit/v9. agent-kit adds only the two things that differ for a chat surface.\n */\nfunction applyAgentOverrides(base: Theme): Theme {\n return {\n ...base,\n // Fluent's web theme is close to CE but does not pin the Web variant of Segoe.\n fontFamilyBase: typography.fontFamily,\n // CE is squarer than Fluent web's default. Bubbles opt back up to borderRadiusLarge.\n borderRadiusMedium: radius.card,\n };\n}\n\nexport function createAgentTheme(brand: BrandVariants = dynamicsBrandRamp as BrandVariants): Theme {\n return applyAgentOverrides(createDynamicsV9Theme(brand));\n}\n\nexport function createAgentDarkTheme(\n brand: BrandVariants = dynamicsBrandRamp as BrandVariants,\n): Theme {\n return applyAgentOverrides(createDynamicsV9DarkTheme(brand));\n}\n\n/** Memoized module singletons. */\nexport const agentTheme: Theme = createAgentTheme();\nexport const agentDarkTheme: Theme = createAgentDarkTheme();\n\nexport interface ResolveHostThemeInput {\n /** An explicit theme always wins. */\n theme?: Theme;\n /** The Dynamics host's own theme — `context.fluentDesignLanguage?.tokenTheme` in a PCF. */\n hostTheme?: Theme;\n isDarkTheme?: boolean;\n}\n\n/**\n * Decide which theme to render with. ONE ladder, called by both `AgentKitHostProvider` and\n * the Storybook decorator so the two cannot drift.\n *\n * ★ The host theme wins over the kit's. That is the rule worth a gate, and the test that can\n * see it must supply a host theme whose brand DIFFERS from the kit's — a resolver that\n * silently ignored `hostTheme` looks identical to a correct one when the two happen to\n * agree.\n *\n * ★ The fallback is the Dynamics theme, not stock `webLightTheme`. The gallery controls fall\n * back to `webLightTheme` because in-host they always receive a real theme; a standalone\n * SWA or custom page has none, and stock Fluent does not read as Dynamics CE.\n */\nexport function resolveHostTheme(input: ResolveHostThemeInput = {}): Theme {\n if (input.theme) return input.theme;\n if (input.hostTheme) return input.hostTheme;\n return input.isDarkTheme ? agentDarkTheme : agentTheme;\n}\n","/**\n * Build a per-instance id-prefix generator.\n *\n * ★ The FACTORY is exported (pure, testable); the module SINGLETON that a provider uses\n * lives in src/host/idPrefix.ts. That split matters: the gallery's measurement is that\n * there must be exactly ONE counter per bundle, so two instances of the same control get\n * different prefixes. A constant prefix is the wrong fix and a per-call factory is too.\n */\nexport function createIdPrefixCounter(tag: string): () => string {\n let n = 0;\n return () => `${tag}${++n}-`;\n}\n","import { createIdPrefixCounter } from '../core/ids';\n\n/**\n * ★★★ ONE counter per bundle. This module-level singleton is the whole mechanism.\n *\n * React 18's `useId` restarts its counter per root. Put two Fluent v9 React roots on one\n * Dynamics form and both name their theme `<style>` tag `fui-FluentProviderr0`; the second\n * provider ADOPTS the first's tag and writes no rules into it, and whichever unmounts first\n * `.remove()`s it unconditionally. The result is a `<style>` with zero CSS rules — invisible\n * inside the form, because custom properties inherit, and fatal for anything PORTALLED\n * (menu listbox, tooltip, dialog, toaster), which mounts on `document.body` and renders\n * untheméd.\n *\n * ★ A CONSTANT prefix does not fix it: two instances of THIS control on one form would\n * collide with each other. Measured in the gallery, where a constant prefix was a real\n * candidate fix and was wrong.\n * ★ No dev harness can show this — a harness mounts one v9 root, so there is nothing to\n * collide with. The gate is a unit test: two renders, assert the provider classes differ.\n */\nexport const nextAgentIdPrefix = createIdPrefixCounter('dvkak');\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AACvB,8BAA6D;;;ACA7D,0BAIO;AACP,2BAAmC;AASnC,SAAS,oBAAoB,MAAoB;AAC/C,SAAO;AAAA,IACL,GAAG;AAAA;AAAA,IAEH,gBAAgB,gCAAW;AAAA;AAAA,IAE3B,oBAAoB,4BAAO;AAAA,EAC7B;AACF;AAEO,SAAS,iBAAiB,QAAuB,uCAA2C;AACjG,SAAO,wBAAoB,2CAAsB,KAAK,CAAC;AACzD;AAEO,SAAS,qBACd,QAAuB,uCAChB;AACP,SAAO,wBAAoB,+CAA0B,KAAK,CAAC;AAC7D;AAGO,IAAM,aAAoB,iBAAiB;AAC3C,IAAM,iBAAwB,qBAAqB;AAuBnD,SAAS,iBAAiB,QAA+B,CAAC,GAAU;AACzE,MAAI,MAAM,MAAO,QAAO,MAAM;AAC9B,MAAI,MAAM,UAAW,QAAO,MAAM;AAClC,SAAO,MAAM,cAAc,iBAAiB;AAC9C;;;ACxDO,SAAS,sBAAsB,KAA2B;AAC/D,MAAI,IAAI;AACR,SAAO,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC;AAC3B;;;ACQO,IAAM,oBAAoB,sBAAsB,OAAO;;;AHoDxD;AAdC,SAAS,qBAAqB,OAAsD;AACzF,QAAM,EAAE,UAAU,OAAO,WAAW,aAAa,gBAAgB,WAAW,MAAM,IAAI;AAGtF,QAAM,CAAC,SAAS,IAAU,eAAS,iBAAiB;AACpD,QAAM,WAAW,MAAM,YAAY;AAEnC,QAAM,WAAiB;AAAA,IACrB,MAAM,iBAAiB,EAAE,OAAO,WAAW,YAAY,CAAC;AAAA,IACxD,CAAC,OAAO,WAAW,WAAW;AAAA,EAChC;AAEA,SACE,4CAAC,4CAAiB,OAAO,UACvB;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
|
package/dist/host.d.cts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { A as AgentKitHostProvider, a as AgentKitHostProviderProps, b as agentDarkTheme, c as agentTheme, d as createAgentDarkTheme, e as createAgentTheme, n as nextAgentIdPrefix, r as resolveHostTheme } from './index-DGN6eYnp.cjs';
|
|
2
|
+
import 'react';
|
|
3
|
+
import '@fluentui/react-components';
|
package/dist/host.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { A as AgentKitHostProvider, a as AgentKitHostProviderProps, b as agentDarkTheme, c as agentTheme, d as createAgentDarkTheme, e as createAgentTheme, n as nextAgentIdPrefix, r as resolveHostTheme } from './index-DGN6eYnp.js';
|
|
2
|
+
import 'react';
|
|
3
|
+
import '@fluentui/react-components';
|
package/dist/host.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/host/AgentKitHostProvider.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { FluentProvider, IdPrefixProvider } from "@fluentui/react-components";
|
|
4
|
+
|
|
5
|
+
// src/theme/index.ts
|
|
6
|
+
import {
|
|
7
|
+
createDynamicsV9Theme,
|
|
8
|
+
createDynamicsV9DarkTheme,
|
|
9
|
+
dynamicsBrandRamp
|
|
10
|
+
} from "@dataverse-kit/fluent-theme";
|
|
11
|
+
import { typography, radius } from "@dataverse-kit/design-tokens";
|
|
12
|
+
function applyAgentOverrides(base) {
|
|
13
|
+
return {
|
|
14
|
+
...base,
|
|
15
|
+
// Fluent's web theme is close to CE but does not pin the Web variant of Segoe.
|
|
16
|
+
fontFamilyBase: typography.fontFamily,
|
|
17
|
+
// CE is squarer than Fluent web's default. Bubbles opt back up to borderRadiusLarge.
|
|
18
|
+
borderRadiusMedium: radius.card
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function createAgentTheme(brand = dynamicsBrandRamp) {
|
|
22
|
+
return applyAgentOverrides(createDynamicsV9Theme(brand));
|
|
23
|
+
}
|
|
24
|
+
function createAgentDarkTheme(brand = dynamicsBrandRamp) {
|
|
25
|
+
return applyAgentOverrides(createDynamicsV9DarkTheme(brand));
|
|
26
|
+
}
|
|
27
|
+
var agentTheme = createAgentTheme();
|
|
28
|
+
var agentDarkTheme = createAgentDarkTheme();
|
|
29
|
+
function resolveHostTheme(input = {}) {
|
|
30
|
+
if (input.theme) return input.theme;
|
|
31
|
+
if (input.hostTheme) return input.hostTheme;
|
|
32
|
+
return input.isDarkTheme ? agentDarkTheme : agentTheme;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/core/ids.ts
|
|
36
|
+
function createIdPrefixCounter(tag) {
|
|
37
|
+
let n = 0;
|
|
38
|
+
return () => `${tag}${++n}-`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/host/idPrefix.ts
|
|
42
|
+
var nextAgentIdPrefix = createIdPrefixCounter("dvkak");
|
|
43
|
+
|
|
44
|
+
// src/host/AgentKitHostProvider.tsx
|
|
45
|
+
import { jsx } from "react/jsx-runtime";
|
|
46
|
+
function AgentKitHostProvider(props) {
|
|
47
|
+
const { children, theme, hostTheme, isDarkTheme, targetDocument, className, style } = props;
|
|
48
|
+
const [generated] = React.useState(nextAgentIdPrefix);
|
|
49
|
+
const idPrefix = props.idPrefix ?? generated;
|
|
50
|
+
const resolved = React.useMemo(
|
|
51
|
+
() => resolveHostTheme({ theme, hostTheme, isDarkTheme }),
|
|
52
|
+
[theme, hostTheme, isDarkTheme]
|
|
53
|
+
);
|
|
54
|
+
return /* @__PURE__ */ jsx(IdPrefixProvider, { value: idPrefix, children: /* @__PURE__ */ jsx(
|
|
55
|
+
FluentProvider,
|
|
56
|
+
{
|
|
57
|
+
theme: resolved,
|
|
58
|
+
targetDocument,
|
|
59
|
+
className,
|
|
60
|
+
style,
|
|
61
|
+
children
|
|
62
|
+
}
|
|
63
|
+
) });
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
AgentKitHostProvider,
|
|
67
|
+
agentDarkTheme,
|
|
68
|
+
agentTheme,
|
|
69
|
+
createAgentDarkTheme,
|
|
70
|
+
createAgentTheme,
|
|
71
|
+
nextAgentIdPrefix,
|
|
72
|
+
resolveHostTheme
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=host.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/host/AgentKitHostProvider.tsx","../src/theme/index.ts","../src/core/ids.ts","../src/host/idPrefix.ts"],"sourcesContent":["import * as React from 'react';\nimport { FluentProvider, IdPrefixProvider, type Theme } from '@fluentui/react-components';\nimport { resolveHostTheme } from '../theme';\nimport { nextAgentIdPrefix } from './idPrefix';\n\nexport interface AgentKitHostProviderProps {\n children?: React.ReactNode;\n /** An explicit theme wins over everything. */\n theme?: Theme;\n /** The Dynamics host's theme — `context.fluentDesignLanguage?.tokenTheme` in a PCF. */\n hostTheme?: Theme;\n isDarkTheme?: boolean;\n /**\n * The document Fluent should treat as its own.\n *\n * ★★ THIS IS THE PCF ESCAPE HATCH, and it is why it is a prop rather than something the\n * kit decides. Fluent v9's tabster is a per-WINDOW singleton: `createTabster` returns\n * `win.__tabsterInstance` if one exists. A Dynamics form installs tabster 8.2.0, whose\n * core has no `attrHandlers`, while a bundled control ships 8.8.x — so the moment a\n * component reaches a `get*` factory (`TabList` -> `useArrowNavigationGroup` -> `getMover`),\n * `core.attrHandlers.set` throws and React unmounts the whole tree.\n *\n * `useTabster` reads `targetDocument` from Fluent's context and hands\n * `targetDocument.defaultView` to `createTabster`. So a host that passes a proxied\n * document with a private `__tabsterInstance` slot gets its own core, and everything\n * below inherits it — with no change to any agent-kit component.\n *\n * The proxy itself is NOT shipped here; it lives with the PCF gallery\n * (`controls/_shared/tabsterIsolation.ts`). A host writes one line:\n * `targetDocument={createIsolatedTargetDocument(document)}`\n *\n * ★ It isolates tabster and portal mount nodes, NOT Griffel: `FluentProvider` consumes\n * the ambient Griffel renderer rather than building one from this prop. That is\n * harmless for the proxy case (styles land in the real <head> either way) but means\n * this prop alone does NOT make the kit work in a separate document or shadow root —\n * that additionally needs `<RendererProvider renderer={createDOMRenderer(doc)}>`.\n */\n targetDocument?: Document;\n /** Override the generated prefix. Only for tests that need a stable value. */\n idPrefix?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * The host wrapper: `IdPrefixProvider` ▸ `FluentProvider` ▸ children.\n *\n * ★ No agent-kit COMPONENT renders a provider — that is surface-kit's convention and this\n * kit keeps it, so a host that already has a `FluentProvider` is not given a second one.\n * This is an opt-in export for hosts that do not, and it exists for a second reason: a\n * rule that lives only in a README is a rule nothing can test. Shipping the wrapper gives\n * the IdPrefix gate a subject inside `src/`, so its sabotage lands on real code instead of\n * on the test's own fixture.\n *\n * ★ Ordering is load-bearing: `IdPrefixProvider` must be OUTSIDE `FluentProvider`, because\n * the provider reads the prefix while generating the id for its own theme <style> tag.\n */\nexport function AgentKitHostProvider(props: AgentKitHostProviderProps): React.ReactElement {\n const { children, theme, hostTheme, isDarkTheme, targetDocument, className, style } = props;\n\n // Generated ONCE per mounted instance — not per render, and not shared across instances.\n const [generated] = React.useState(nextAgentIdPrefix);\n const idPrefix = props.idPrefix ?? generated;\n\n const resolved = React.useMemo(\n () => resolveHostTheme({ theme, hostTheme, isDarkTheme }),\n [theme, hostTheme, isDarkTheme],\n );\n\n return (\n <IdPrefixProvider value={idPrefix}>\n <FluentProvider\n theme={resolved}\n targetDocument={targetDocument}\n className={className}\n style={style}\n >\n {children}\n </FluentProvider>\n </IdPrefixProvider>\n );\n}\n","import type { BrandVariants, Theme } from '@fluentui/react-components';\nimport {\n createDynamicsV9Theme,\n createDynamicsV9DarkTheme,\n dynamicsBrandRamp,\n} from '@dataverse-kit/fluent-theme';\nimport { typography, radius } from '@dataverse-kit/design-tokens';\n\n/**\n * The Dynamics CE look, for conversational surfaces.\n *\n * ★ The brand ramp and the base builders are NOT declared here — they come from\n * `@dataverse-kit/fluent-theme`, the single declaration shared with grid-kit/v9 and\n * surface-kit/v9. agent-kit adds only the two things that differ for a chat surface.\n */\nfunction applyAgentOverrides(base: Theme): Theme {\n return {\n ...base,\n // Fluent's web theme is close to CE but does not pin the Web variant of Segoe.\n fontFamilyBase: typography.fontFamily,\n // CE is squarer than Fluent web's default. Bubbles opt back up to borderRadiusLarge.\n borderRadiusMedium: radius.card,\n };\n}\n\nexport function createAgentTheme(brand: BrandVariants = dynamicsBrandRamp as BrandVariants): Theme {\n return applyAgentOverrides(createDynamicsV9Theme(brand));\n}\n\nexport function createAgentDarkTheme(\n brand: BrandVariants = dynamicsBrandRamp as BrandVariants,\n): Theme {\n return applyAgentOverrides(createDynamicsV9DarkTheme(brand));\n}\n\n/** Memoized module singletons. */\nexport const agentTheme: Theme = createAgentTheme();\nexport const agentDarkTheme: Theme = createAgentDarkTheme();\n\nexport interface ResolveHostThemeInput {\n /** An explicit theme always wins. */\n theme?: Theme;\n /** The Dynamics host's own theme — `context.fluentDesignLanguage?.tokenTheme` in a PCF. */\n hostTheme?: Theme;\n isDarkTheme?: boolean;\n}\n\n/**\n * Decide which theme to render with. ONE ladder, called by both `AgentKitHostProvider` and\n * the Storybook decorator so the two cannot drift.\n *\n * ★ The host theme wins over the kit's. That is the rule worth a gate, and the test that can\n * see it must supply a host theme whose brand DIFFERS from the kit's — a resolver that\n * silently ignored `hostTheme` looks identical to a correct one when the two happen to\n * agree.\n *\n * ★ The fallback is the Dynamics theme, not stock `webLightTheme`. The gallery controls fall\n * back to `webLightTheme` because in-host they always receive a real theme; a standalone\n * SWA or custom page has none, and stock Fluent does not read as Dynamics CE.\n */\nexport function resolveHostTheme(input: ResolveHostThemeInput = {}): Theme {\n if (input.theme) return input.theme;\n if (input.hostTheme) return input.hostTheme;\n return input.isDarkTheme ? agentDarkTheme : agentTheme;\n}\n","/**\n * Build a per-instance id-prefix generator.\n *\n * ★ The FACTORY is exported (pure, testable); the module SINGLETON that a provider uses\n * lives in src/host/idPrefix.ts. That split matters: the gallery's measurement is that\n * there must be exactly ONE counter per bundle, so two instances of the same control get\n * different prefixes. A constant prefix is the wrong fix and a per-call factory is too.\n */\nexport function createIdPrefixCounter(tag: string): () => string {\n let n = 0;\n return () => `${tag}${++n}-`;\n}\n","import { createIdPrefixCounter } from '../core/ids';\n\n/**\n * ★★★ ONE counter per bundle. This module-level singleton is the whole mechanism.\n *\n * React 18's `useId` restarts its counter per root. Put two Fluent v9 React roots on one\n * Dynamics form and both name their theme `<style>` tag `fui-FluentProviderr0`; the second\n * provider ADOPTS the first's tag and writes no rules into it, and whichever unmounts first\n * `.remove()`s it unconditionally. The result is a `<style>` with zero CSS rules — invisible\n * inside the form, because custom properties inherit, and fatal for anything PORTALLED\n * (menu listbox, tooltip, dialog, toaster), which mounts on `document.body` and renders\n * untheméd.\n *\n * ★ A CONSTANT prefix does not fix it: two instances of THIS control on one form would\n * collide with each other. Measured in the gallery, where a constant prefix was a real\n * candidate fix and was wrong.\n * ★ No dev harness can show this — a harness mounts one v9 root, so there is nothing to\n * collide with. The gate is a unit test: two renders, assert the provider classes differ.\n */\nexport const nextAgentIdPrefix = createIdPrefixCounter('dvkak');\n"],"mappings":";AAAA,YAAY,WAAW;AACvB,SAAS,gBAAgB,wBAAoC;;;ACA7D;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,YAAY,cAAc;AASnC,SAAS,oBAAoB,MAAoB;AAC/C,SAAO;AAAA,IACL,GAAG;AAAA;AAAA,IAEH,gBAAgB,WAAW;AAAA;AAAA,IAE3B,oBAAoB,OAAO;AAAA,EAC7B;AACF;AAEO,SAAS,iBAAiB,QAAuB,mBAA2C;AACjG,SAAO,oBAAoB,sBAAsB,KAAK,CAAC;AACzD;AAEO,SAAS,qBACd,QAAuB,mBAChB;AACP,SAAO,oBAAoB,0BAA0B,KAAK,CAAC;AAC7D;AAGO,IAAM,aAAoB,iBAAiB;AAC3C,IAAM,iBAAwB,qBAAqB;AAuBnD,SAAS,iBAAiB,QAA+B,CAAC,GAAU;AACzE,MAAI,MAAM,MAAO,QAAO,MAAM;AAC9B,MAAI,MAAM,UAAW,QAAO,MAAM;AAClC,SAAO,MAAM,cAAc,iBAAiB;AAC9C;;;ACxDO,SAAS,sBAAsB,KAA2B;AAC/D,MAAI,IAAI;AACR,SAAO,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC;AAC3B;;;ACQO,IAAM,oBAAoB,sBAAsB,OAAO;;;AHoDxD;AAdC,SAAS,qBAAqB,OAAsD;AACzF,QAAM,EAAE,UAAU,OAAO,WAAW,aAAa,gBAAgB,WAAW,MAAM,IAAI;AAGtF,QAAM,CAAC,SAAS,IAAU,eAAS,iBAAiB;AACpD,QAAM,WAAW,MAAM,YAAY;AAEnC,QAAM,WAAiB;AAAA,IACrB,MAAM,iBAAiB,EAAE,OAAO,WAAW,YAAY,CAAC;AAAA,IACxD,CAAC,OAAO,WAAW,WAAW;AAAA,EAChC;AAEA,SACE,oBAAC,oBAAiB,OAAO,UACvB;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Theme, BrandVariants } from '@fluentui/react-components';
|
|
3
|
+
|
|
4
|
+
interface AgentKitHostProviderProps {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
/** An explicit theme wins over everything. */
|
|
7
|
+
theme?: Theme;
|
|
8
|
+
/** The Dynamics host's theme — `context.fluentDesignLanguage?.tokenTheme` in a PCF. */
|
|
9
|
+
hostTheme?: Theme;
|
|
10
|
+
isDarkTheme?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The document Fluent should treat as its own.
|
|
13
|
+
*
|
|
14
|
+
* ★★ THIS IS THE PCF ESCAPE HATCH, and it is why it is a prop rather than something the
|
|
15
|
+
* kit decides. Fluent v9's tabster is a per-WINDOW singleton: `createTabster` returns
|
|
16
|
+
* `win.__tabsterInstance` if one exists. A Dynamics form installs tabster 8.2.0, whose
|
|
17
|
+
* core has no `attrHandlers`, while a bundled control ships 8.8.x — so the moment a
|
|
18
|
+
* component reaches a `get*` factory (`TabList` -> `useArrowNavigationGroup` -> `getMover`),
|
|
19
|
+
* `core.attrHandlers.set` throws and React unmounts the whole tree.
|
|
20
|
+
*
|
|
21
|
+
* `useTabster` reads `targetDocument` from Fluent's context and hands
|
|
22
|
+
* `targetDocument.defaultView` to `createTabster`. So a host that passes a proxied
|
|
23
|
+
* document with a private `__tabsterInstance` slot gets its own core, and everything
|
|
24
|
+
* below inherits it — with no change to any agent-kit component.
|
|
25
|
+
*
|
|
26
|
+
* The proxy itself is NOT shipped here; it lives with the PCF gallery
|
|
27
|
+
* (`controls/_shared/tabsterIsolation.ts`). A host writes one line:
|
|
28
|
+
* `targetDocument={createIsolatedTargetDocument(document)}`
|
|
29
|
+
*
|
|
30
|
+
* ★ It isolates tabster and portal mount nodes, NOT Griffel: `FluentProvider` consumes
|
|
31
|
+
* the ambient Griffel renderer rather than building one from this prop. That is
|
|
32
|
+
* harmless for the proxy case (styles land in the real <head> either way) but means
|
|
33
|
+
* this prop alone does NOT make the kit work in a separate document or shadow root —
|
|
34
|
+
* that additionally needs `<RendererProvider renderer={createDOMRenderer(doc)}>`.
|
|
35
|
+
*/
|
|
36
|
+
targetDocument?: Document;
|
|
37
|
+
/** Override the generated prefix. Only for tests that need a stable value. */
|
|
38
|
+
idPrefix?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
style?: React.CSSProperties;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The host wrapper: `IdPrefixProvider` ▸ `FluentProvider` ▸ children.
|
|
44
|
+
*
|
|
45
|
+
* ★ No agent-kit COMPONENT renders a provider — that is surface-kit's convention and this
|
|
46
|
+
* kit keeps it, so a host that already has a `FluentProvider` is not given a second one.
|
|
47
|
+
* This is an opt-in export for hosts that do not, and it exists for a second reason: a
|
|
48
|
+
* rule that lives only in a README is a rule nothing can test. Shipping the wrapper gives
|
|
49
|
+
* the IdPrefix gate a subject inside `src/`, so its sabotage lands on real code instead of
|
|
50
|
+
* on the test's own fixture.
|
|
51
|
+
*
|
|
52
|
+
* ★ Ordering is load-bearing: `IdPrefixProvider` must be OUTSIDE `FluentProvider`, because
|
|
53
|
+
* the provider reads the prefix while generating the id for its own theme <style> tag.
|
|
54
|
+
*/
|
|
55
|
+
declare function AgentKitHostProvider(props: AgentKitHostProviderProps): React.ReactElement;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* ★★★ ONE counter per bundle. This module-level singleton is the whole mechanism.
|
|
59
|
+
*
|
|
60
|
+
* React 18's `useId` restarts its counter per root. Put two Fluent v9 React roots on one
|
|
61
|
+
* Dynamics form and both name their theme `<style>` tag `fui-FluentProviderr0`; the second
|
|
62
|
+
* provider ADOPTS the first's tag and writes no rules into it, and whichever unmounts first
|
|
63
|
+
* `.remove()`s it unconditionally. The result is a `<style>` with zero CSS rules — invisible
|
|
64
|
+
* inside the form, because custom properties inherit, and fatal for anything PORTALLED
|
|
65
|
+
* (menu listbox, tooltip, dialog, toaster), which mounts on `document.body` and renders
|
|
66
|
+
* untheméd.
|
|
67
|
+
*
|
|
68
|
+
* ★ A CONSTANT prefix does not fix it: two instances of THIS control on one form would
|
|
69
|
+
* collide with each other. Measured in the gallery, where a constant prefix was a real
|
|
70
|
+
* candidate fix and was wrong.
|
|
71
|
+
* ★ No dev harness can show this — a harness mounts one v9 root, so there is nothing to
|
|
72
|
+
* collide with. The gate is a unit test: two renders, assert the provider classes differ.
|
|
73
|
+
*/
|
|
74
|
+
declare const nextAgentIdPrefix: () => string;
|
|
75
|
+
|
|
76
|
+
declare function createAgentTheme(brand?: BrandVariants): Theme;
|
|
77
|
+
declare function createAgentDarkTheme(brand?: BrandVariants): Theme;
|
|
78
|
+
/** Memoized module singletons. */
|
|
79
|
+
declare const agentTheme: Theme;
|
|
80
|
+
declare const agentDarkTheme: Theme;
|
|
81
|
+
interface ResolveHostThemeInput {
|
|
82
|
+
/** An explicit theme always wins. */
|
|
83
|
+
theme?: Theme;
|
|
84
|
+
/** The Dynamics host's own theme — `context.fluentDesignLanguage?.tokenTheme` in a PCF. */
|
|
85
|
+
hostTheme?: Theme;
|
|
86
|
+
isDarkTheme?: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Decide which theme to render with. ONE ladder, called by both `AgentKitHostProvider` and
|
|
90
|
+
* the Storybook decorator so the two cannot drift.
|
|
91
|
+
*
|
|
92
|
+
* ★ The host theme wins over the kit's. That is the rule worth a gate, and the test that can
|
|
93
|
+
* see it must supply a host theme whose brand DIFFERS from the kit's — a resolver that
|
|
94
|
+
* silently ignored `hostTheme` looks identical to a correct one when the two happen to
|
|
95
|
+
* agree.
|
|
96
|
+
*
|
|
97
|
+
* ★ The fallback is the Dynamics theme, not stock `webLightTheme`. The gallery controls fall
|
|
98
|
+
* back to `webLightTheme` because in-host they always receive a real theme; a standalone
|
|
99
|
+
* SWA or custom page has none, and stock Fluent does not read as Dynamics CE.
|
|
100
|
+
*/
|
|
101
|
+
declare function resolveHostTheme(input?: ResolveHostThemeInput): Theme;
|
|
102
|
+
|
|
103
|
+
export { AgentKitHostProvider as A, type ResolveHostThemeInput as R, type AgentKitHostProviderProps as a, agentDarkTheme as b, agentTheme as c, createAgentDarkTheme as d, createAgentTheme as e, nextAgentIdPrefix as n, resolveHostTheme as r };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Theme, BrandVariants } from '@fluentui/react-components';
|
|
3
|
+
|
|
4
|
+
interface AgentKitHostProviderProps {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
/** An explicit theme wins over everything. */
|
|
7
|
+
theme?: Theme;
|
|
8
|
+
/** The Dynamics host's theme — `context.fluentDesignLanguage?.tokenTheme` in a PCF. */
|
|
9
|
+
hostTheme?: Theme;
|
|
10
|
+
isDarkTheme?: boolean;
|
|
11
|
+
/**
|
|
12
|
+
* The document Fluent should treat as its own.
|
|
13
|
+
*
|
|
14
|
+
* ★★ THIS IS THE PCF ESCAPE HATCH, and it is why it is a prop rather than something the
|
|
15
|
+
* kit decides. Fluent v9's tabster is a per-WINDOW singleton: `createTabster` returns
|
|
16
|
+
* `win.__tabsterInstance` if one exists. A Dynamics form installs tabster 8.2.0, whose
|
|
17
|
+
* core has no `attrHandlers`, while a bundled control ships 8.8.x — so the moment a
|
|
18
|
+
* component reaches a `get*` factory (`TabList` -> `useArrowNavigationGroup` -> `getMover`),
|
|
19
|
+
* `core.attrHandlers.set` throws and React unmounts the whole tree.
|
|
20
|
+
*
|
|
21
|
+
* `useTabster` reads `targetDocument` from Fluent's context and hands
|
|
22
|
+
* `targetDocument.defaultView` to `createTabster`. So a host that passes a proxied
|
|
23
|
+
* document with a private `__tabsterInstance` slot gets its own core, and everything
|
|
24
|
+
* below inherits it — with no change to any agent-kit component.
|
|
25
|
+
*
|
|
26
|
+
* The proxy itself is NOT shipped here; it lives with the PCF gallery
|
|
27
|
+
* (`controls/_shared/tabsterIsolation.ts`). A host writes one line:
|
|
28
|
+
* `targetDocument={createIsolatedTargetDocument(document)}`
|
|
29
|
+
*
|
|
30
|
+
* ★ It isolates tabster and portal mount nodes, NOT Griffel: `FluentProvider` consumes
|
|
31
|
+
* the ambient Griffel renderer rather than building one from this prop. That is
|
|
32
|
+
* harmless for the proxy case (styles land in the real <head> either way) but means
|
|
33
|
+
* this prop alone does NOT make the kit work in a separate document or shadow root —
|
|
34
|
+
* that additionally needs `<RendererProvider renderer={createDOMRenderer(doc)}>`.
|
|
35
|
+
*/
|
|
36
|
+
targetDocument?: Document;
|
|
37
|
+
/** Override the generated prefix. Only for tests that need a stable value. */
|
|
38
|
+
idPrefix?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
style?: React.CSSProperties;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The host wrapper: `IdPrefixProvider` ▸ `FluentProvider` ▸ children.
|
|
44
|
+
*
|
|
45
|
+
* ★ No agent-kit COMPONENT renders a provider — that is surface-kit's convention and this
|
|
46
|
+
* kit keeps it, so a host that already has a `FluentProvider` is not given a second one.
|
|
47
|
+
* This is an opt-in export for hosts that do not, and it exists for a second reason: a
|
|
48
|
+
* rule that lives only in a README is a rule nothing can test. Shipping the wrapper gives
|
|
49
|
+
* the IdPrefix gate a subject inside `src/`, so its sabotage lands on real code instead of
|
|
50
|
+
* on the test's own fixture.
|
|
51
|
+
*
|
|
52
|
+
* ★ Ordering is load-bearing: `IdPrefixProvider` must be OUTSIDE `FluentProvider`, because
|
|
53
|
+
* the provider reads the prefix while generating the id for its own theme <style> tag.
|
|
54
|
+
*/
|
|
55
|
+
declare function AgentKitHostProvider(props: AgentKitHostProviderProps): React.ReactElement;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* ★★★ ONE counter per bundle. This module-level singleton is the whole mechanism.
|
|
59
|
+
*
|
|
60
|
+
* React 18's `useId` restarts its counter per root. Put two Fluent v9 React roots on one
|
|
61
|
+
* Dynamics form and both name their theme `<style>` tag `fui-FluentProviderr0`; the second
|
|
62
|
+
* provider ADOPTS the first's tag and writes no rules into it, and whichever unmounts first
|
|
63
|
+
* `.remove()`s it unconditionally. The result is a `<style>` with zero CSS rules — invisible
|
|
64
|
+
* inside the form, because custom properties inherit, and fatal for anything PORTALLED
|
|
65
|
+
* (menu listbox, tooltip, dialog, toaster), which mounts on `document.body` and renders
|
|
66
|
+
* untheméd.
|
|
67
|
+
*
|
|
68
|
+
* ★ A CONSTANT prefix does not fix it: two instances of THIS control on one form would
|
|
69
|
+
* collide with each other. Measured in the gallery, where a constant prefix was a real
|
|
70
|
+
* candidate fix and was wrong.
|
|
71
|
+
* ★ No dev harness can show this — a harness mounts one v9 root, so there is nothing to
|
|
72
|
+
* collide with. The gate is a unit test: two renders, assert the provider classes differ.
|
|
73
|
+
*/
|
|
74
|
+
declare const nextAgentIdPrefix: () => string;
|
|
75
|
+
|
|
76
|
+
declare function createAgentTheme(brand?: BrandVariants): Theme;
|
|
77
|
+
declare function createAgentDarkTheme(brand?: BrandVariants): Theme;
|
|
78
|
+
/** Memoized module singletons. */
|
|
79
|
+
declare const agentTheme: Theme;
|
|
80
|
+
declare const agentDarkTheme: Theme;
|
|
81
|
+
interface ResolveHostThemeInput {
|
|
82
|
+
/** An explicit theme always wins. */
|
|
83
|
+
theme?: Theme;
|
|
84
|
+
/** The Dynamics host's own theme — `context.fluentDesignLanguage?.tokenTheme` in a PCF. */
|
|
85
|
+
hostTheme?: Theme;
|
|
86
|
+
isDarkTheme?: boolean;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Decide which theme to render with. ONE ladder, called by both `AgentKitHostProvider` and
|
|
90
|
+
* the Storybook decorator so the two cannot drift.
|
|
91
|
+
*
|
|
92
|
+
* ★ The host theme wins over the kit's. That is the rule worth a gate, and the test that can
|
|
93
|
+
* see it must supply a host theme whose brand DIFFERS from the kit's — a resolver that
|
|
94
|
+
* silently ignored `hostTheme` looks identical to a correct one when the two happen to
|
|
95
|
+
* agree.
|
|
96
|
+
*
|
|
97
|
+
* ★ The fallback is the Dynamics theme, not stock `webLightTheme`. The gallery controls fall
|
|
98
|
+
* back to `webLightTheme` because in-host they always receive a real theme; a standalone
|
|
99
|
+
* SWA or custom page has none, and stock Fluent does not read as Dynamics CE.
|
|
100
|
+
*/
|
|
101
|
+
declare function resolveHostTheme(input?: ResolveHostThemeInput): Theme;
|
|
102
|
+
|
|
103
|
+
export { AgentKitHostProvider as A, type ResolveHostThemeInput as R, type AgentKitHostProviderProps as a, agentDarkTheme as b, agentTheme as c, createAgentDarkTheme as d, createAgentTheme as e, nextAgentIdPrefix as n, resolveHostTheme as r };
|