@matterfact/embed 0.4.0 → 0.6.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 +62 -6
- package/dist/{chunk-523BCS2Z.js → chunk-4Q2ROXLR.js} +2 -2
- package/dist/chunk-7I37ZFAJ.js +2 -0
- package/dist/{chunk-Z4WT3TIZ.js → chunk-AXKXNYRT.js} +39 -38
- package/dist/chunk-AXKXNYRT.js.map +1 -0
- package/dist/chunk-D7R6WVHG.js +2 -0
- package/dist/chunk-D7R6WVHG.js.map +7 -0
- package/dist/{chunk-SBNMM3ZV.js → chunk-RS6RMZ77.js} +39 -37
- package/dist/chunk-RS6RMZ77.js.map +1 -0
- package/dist/{context-432WQST4.js → context-WU2F5CBC.js} +6 -4
- package/dist/context-YX2KXFLI.js +3 -0
- package/dist/{context-KZAUOIR6.js.map → context-YX2KXFLI.js.map} +1 -1
- package/dist/embed.js +1 -1
- package/dist/embed.js.map +2 -2
- package/dist/index.cjs +58 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -3
- package/dist/index.d.ts +37 -3
- package/dist/index.js +22 -15
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +64 -50
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +35 -1
- package/dist/react.d.ts +35 -1
- package/dist/react.js +28 -15
- package/dist/react.js.map +1 -1
- package/dist/{snapshot-SOMSG2OF.js → snapshot-JOGZWESK.js} +2 -2
- package/dist/{snapshot-Q3CALMHD.js → snapshot-MUXE7KXX.js} +3 -3
- package/dist/{snapshot-Q3CALMHD.js.map → snapshot-MUXE7KXX.js.map} +1 -1
- package/examples/README.md +46 -0
- package/examples/demo-host/index.html +163 -0
- package/examples/demo-host/serve.mjs +62 -0
- package/examples/embed-demo/.env.example +24 -0
- package/examples/embed-demo/README.md +72 -0
- package/examples/embed-demo/index.html +12 -0
- package/examples/embed-demo/package.json +25 -0
- package/examples/embed-demo/src/App.tsx +239 -0
- package/examples/embed-demo/src/config.ts +43 -0
- package/examples/embed-demo/src/main.tsx +10 -0
- package/examples/embed-demo/src/styles.css +354 -0
- package/examples/embed-demo/tsconfig.json +16 -0
- package/examples/embed-demo/vite.config.ts +11 -0
- package/examples/host-panel.html +259 -0
- package/package.json +3 -2
- package/dist/chunk-SBNMM3ZV.js.map +0 -1
- package/dist/chunk-URIBHOOQ.js +0 -2
- package/dist/chunk-XLQ36ZMV.js +0 -2
- package/dist/chunk-XLQ36ZMV.js.map +0 -7
- package/dist/chunk-Z4WT3TIZ.js.map +0 -1
- package/dist/context-KZAUOIR6.js +0 -3
- /package/dist/{chunk-523BCS2Z.js.map → chunk-4Q2ROXLR.js.map} +0 -0
- /package/dist/{chunk-URIBHOOQ.js.map → chunk-7I37ZFAJ.js.map} +0 -0
- /package/dist/{context-432WQST4.js.map → context-WU2F5CBC.js.map} +0 -0
- /package/dist/{snapshot-SOMSG2OF.js.map → snapshot-JOGZWESK.js.map} +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# matterfact embed demo
|
|
2
|
+
|
|
3
|
+
A tiny React + Vite app that puts **both** matterfact embed surfaces on one page using
|
|
4
|
+
`@matterfact/embed`:
|
|
5
|
+
|
|
6
|
+
- **`<MatterfactAgent inline>`** — the chat, mounted in the page's right rail;
|
|
7
|
+
- **`<MatterfactArtifact>`** — a matterfact artifact embedded in the page.
|
|
8
|
+
|
|
9
|
+
Both on the page means the agent auto-detects the artifact by origin and can answer
|
|
10
|
+
questions about the data behind it — no wiring.
|
|
11
|
+
|
|
12
|
+
## Run it
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
cp .env.example .env # fill in your key + artifact triple
|
|
17
|
+
npm run dev # → http://localhost:8000
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If anything's missing the app shows a setup screen naming the exact env vars still needed.
|
|
21
|
+
|
|
22
|
+
> Requires `@matterfact/embed` **>= 0.5.0** — earlier versions' loader auto-opens a stale
|
|
23
|
+
> sign-in popup that errors "Missing authorization parameters" on Entra deployments.
|
|
24
|
+
|
|
25
|
+
## Configure — everything lives in `.env`
|
|
26
|
+
|
|
27
|
+
Vite exposes only `VITE_`-prefixed vars to the app.
|
|
28
|
+
|
|
29
|
+
| Variable | What |
|
|
30
|
+
| --------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
31
|
+
| `VITE_MF_ENV` | Deployment: `ecm` \| `prod` \| `staging` \| `local`. Picks the widget origin. |
|
|
32
|
+
| `VITE_MF_WIDGET_ORIGIN` | Optional. Overrides the origin (required for `staging`). |
|
|
33
|
+
| `VITE_MF_PUBLISHABLE_KEY` | The agent's publishable key (`pk_live_…` / `pk_test_…`). |
|
|
34
|
+
| `VITE_MF_ARTIFACT_SLUG` / `_OWNER` / `_TOKEN` | The artifact — from its **Share → Embed** dialog. |
|
|
35
|
+
| `VITE_MF_SURFACE` | Free-form usage/history label. |
|
|
36
|
+
| `VITE_MF_THEME` | `light` \| `dark` \| `auto`. |
|
|
37
|
+
|
|
38
|
+
### Env → origin
|
|
39
|
+
|
|
40
|
+
| `VITE_MF_ENV` | Widget origin |
|
|
41
|
+
| ------------- | ------------------------------------------------------------------------- |
|
|
42
|
+
| `ecm` | `https://emc.matterfact.com` |
|
|
43
|
+
| `prod` | `https://app.matterfact.com` |
|
|
44
|
+
| `staging` | _(set `VITE_MF_WIDGET_ORIGIN`)_ |
|
|
45
|
+
| `local` | `http://localhost:3000` _(wherever your dev server serves `/embed/chat`)_ |
|
|
46
|
+
|
|
47
|
+
## The one gotcha: origin registration
|
|
48
|
+
|
|
49
|
+
The publishable key is **origin-pinned** (scheme + host + port, exact match). This app runs on
|
|
50
|
+
`http://localhost:8000` (fixed via `strictPort`). That exact origin must be registered as an
|
|
51
|
+
allowed origin on the key in the target deployment (e.g. minted against emc-api for `ecm`). If
|
|
52
|
+
it isn't, the agent iframe renders nothing — the key isn't wrong, the origin just isn't allowed.
|
|
53
|
+
|
|
54
|
+
## Signing in
|
|
55
|
+
|
|
56
|
+
The widget runs its own sign-in in a popup — a cross-site iframe has no ambient session, so
|
|
57
|
+
this is the honest production behaviour. Silent host-auth (`getAuthToken`) is out of scope here.
|
|
58
|
+
|
|
59
|
+
## Inspect what the agent sees
|
|
60
|
+
|
|
61
|
+
Add `?mfdev=1` to the URL — a panel shows exactly what the widget sends each turn.
|
|
62
|
+
|
|
63
|
+
## The two lines that matter
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { MatterfactAgent, MatterfactArtifact } from '@matterfact/embed/react';
|
|
67
|
+
|
|
68
|
+
<MatterfactAgent inline publishableKey="pk_live_…" widgetOrigin="https://emc.matterfact.com" />
|
|
69
|
+
<MatterfactArtifact slug="…" owner="you@firm.com" token="…" widgetOrigin="https://emc.matterfact.com" />
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
See the package [README](../../README.md) for the full API.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Meridian Capital — matterfact embed demo</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "matterfact-embed-demo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "A tiny React app showing the matterfact embed agent + embed artifact, configured via .env.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "vite",
|
|
10
|
+
"build": "tsc --noEmit && vite build",
|
|
11
|
+
"preview": "vite preview --port 8000 --strictPort"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@matterfact/embed": "^0.6.0",
|
|
15
|
+
"react": "^18.3.1",
|
|
16
|
+
"react-dom": "^18.3.1"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/react": "^18.3.5",
|
|
20
|
+
"@types/react-dom": "^18.3.0",
|
|
21
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
22
|
+
"typescript": "^5.5.4",
|
|
23
|
+
"vite": "^5.4.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { MatterfactAgent, MatterfactArtifact } from '@matterfact/embed/react';
|
|
3
|
+
import { config, missingConfig } from './config';
|
|
4
|
+
|
|
5
|
+
const HOLDINGS = [
|
|
6
|
+
{ t: 'NVDA', w: '9.4%', d: '+2.8%', y: '+41%', th: 'AI datacenter demand' },
|
|
7
|
+
{ t: 'MSFT', w: '8.1%', d: '+0.9%', y: '+22%', th: 'Copilot attach' },
|
|
8
|
+
{
|
|
9
|
+
t: 'UNH',
|
|
10
|
+
w: '6.7%',
|
|
11
|
+
d: '−1.2%',
|
|
12
|
+
y: '−8%',
|
|
13
|
+
th: 'MLR pressure',
|
|
14
|
+
dn: true,
|
|
15
|
+
yn: true,
|
|
16
|
+
},
|
|
17
|
+
{ t: 'V', w: '5.9%', d: '+0.4%', y: '+11%', th: 'Cross-border recovery' },
|
|
18
|
+
{ t: 'LLY', w: '5.2%', d: '+1.7%', y: '+34%', th: 'GLP-1 ramp' },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function App() {
|
|
22
|
+
const [panelOpen, setPanelOpen] = useState(true);
|
|
23
|
+
// A bit of state that changes WITHOUT a URL navigation — the exact case pull-based
|
|
24
|
+
// context exists for. Selecting a row must reach the agent, but the path never changes.
|
|
25
|
+
const [selected, setSelected] = useState<string | null>(null);
|
|
26
|
+
const missing = missingConfig();
|
|
27
|
+
|
|
28
|
+
if (missing.length) return <Setup missing={missing} />;
|
|
29
|
+
|
|
30
|
+
// PULL-BASED PAGE CONTEXT. Returns what this page is RIGHT NOW, including the holding the
|
|
31
|
+
// user has selected. The widget calls this at the start of every turn, so a selection —
|
|
32
|
+
// which changes no URL — still rides on the next question. (The React component reads this
|
|
33
|
+
// through a ref, so a fresh closure over `selected` each render is exactly right.)
|
|
34
|
+
const getPageContext = () => ({
|
|
35
|
+
route: '/portfolios/growth-sleeve',
|
|
36
|
+
description:
|
|
37
|
+
'Meridian Capital — Growth Sleeve portfolio: 12 holdings, $48.2M, +18.7% YTD, ' +
|
|
38
|
+
'2.1% behind the S&P 500.' +
|
|
39
|
+
(selected ? ` The user has selected ${selected}.` : ''),
|
|
40
|
+
entities: HOLDINGS.map((h) => ({
|
|
41
|
+
kind: 'x:ticker',
|
|
42
|
+
id: h.t,
|
|
43
|
+
label: h.t,
|
|
44
|
+
data: { weight: h.w, ytd: h.y, thesis: h.th, selected: h.t === selected },
|
|
45
|
+
})),
|
|
46
|
+
data: { selectedHolding: selected },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="app" data-panel={panelOpen ? 'open' : 'closed'}>
|
|
51
|
+
<header>
|
|
52
|
+
<div className="brand">
|
|
53
|
+
<span className="mark">M</span> Meridian Capital{' '}
|
|
54
|
+
<small>· embed demo</small>
|
|
55
|
+
</div>
|
|
56
|
+
<nav>
|
|
57
|
+
<a href="#">Positions</a>
|
|
58
|
+
<a href="#" className="active">
|
|
59
|
+
Growth Sleeve
|
|
60
|
+
</a>
|
|
61
|
+
<a href="#">Research</a>
|
|
62
|
+
</nav>
|
|
63
|
+
<span className="spacer" />
|
|
64
|
+
<span className="env-chip">
|
|
65
|
+
{config.env} · {config.widgetOrigin.replace(/^https?:\/\//, '')}
|
|
66
|
+
</span>
|
|
67
|
+
<button className="toggle" onClick={() => setPanelOpen((v) => !v)}>
|
|
68
|
+
<span className="dot" /> Assistant
|
|
69
|
+
</button>
|
|
70
|
+
</header>
|
|
71
|
+
|
|
72
|
+
<div className="layout">
|
|
73
|
+
<main>
|
|
74
|
+
<div className="eyebrow">Portfolio</div>
|
|
75
|
+
<h1>Growth Sleeve</h1>
|
|
76
|
+
<div className="sub">
|
|
77
|
+
12 holdings · rebalanced 3 days ago · benchmark S&P 500
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<div className="kpis">
|
|
81
|
+
<Kpi label="Market value" value="$48.2M" />
|
|
82
|
+
<Kpi label="Day change" value="+1.34%" tone="pos" />
|
|
83
|
+
<Kpi label="YTD" value="+18.7%" tone="pos" />
|
|
84
|
+
<Kpi label="vs benchmark" value="−2.1%" tone="neg" />
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div className="section-title">Embedded matterfact artifact</div>
|
|
88
|
+
<div className="artifact-frame">
|
|
89
|
+
<div className="artifact-badge">
|
|
90
|
+
<span className="mf">✦</span> matterfact artifact ·{' '}
|
|
91
|
+
<code>{config.artifact.slug}</code>
|
|
92
|
+
</div>
|
|
93
|
+
{/* The agent, mounted on the same page below, auto-detects this artifact
|
|
94
|
+
by origin and can answer questions about the data behind it. */}
|
|
95
|
+
<MatterfactArtifact
|
|
96
|
+
slug={config.artifact.slug}
|
|
97
|
+
owner={config.artifact.owner}
|
|
98
|
+
token={config.artifact.token}
|
|
99
|
+
widgetOrigin={config.widgetOrigin}
|
|
100
|
+
theme={config.theme}
|
|
101
|
+
style={{ width: '100%', height: '100%' }}
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
<p className="artifact-caption">
|
|
105
|
+
<strong>Artifact-aware:</strong> the assistant detects this embedded
|
|
106
|
+
artifact by its origin and can pull the real data behind it — try
|
|
107
|
+
asking it to summarise the artifact.
|
|
108
|
+
</p>
|
|
109
|
+
|
|
110
|
+
<div className="section-title">Holdings</div>
|
|
111
|
+
<table>
|
|
112
|
+
<thead>
|
|
113
|
+
<tr>
|
|
114
|
+
<th>Ticker</th>
|
|
115
|
+
<th>Weight</th>
|
|
116
|
+
<th>Day</th>
|
|
117
|
+
<th>YTD</th>
|
|
118
|
+
<th>Thesis</th>
|
|
119
|
+
</tr>
|
|
120
|
+
</thead>
|
|
121
|
+
<tbody>
|
|
122
|
+
{HOLDINGS.map((h) => (
|
|
123
|
+
<Row
|
|
124
|
+
key={h.t}
|
|
125
|
+
{...h}
|
|
126
|
+
selected={h.t === selected}
|
|
127
|
+
onSelect={() => setSelected(h.t === selected ? null : h.t)}
|
|
128
|
+
/>
|
|
129
|
+
))}
|
|
130
|
+
</tbody>
|
|
131
|
+
</table>
|
|
132
|
+
<p className="artifact-caption">
|
|
133
|
+
<strong>Pull-based context:</strong> click a holding to select it
|
|
134
|
+
(no page navigation), then ask the assistant{' '}
|
|
135
|
+
<em>"what's the thesis on this one?"</em>. The selection reaches the
|
|
136
|
+
agent via <code>getPageContext</code>, which the widget pulls fresh
|
|
137
|
+
at the start of every turn.
|
|
138
|
+
</p>
|
|
139
|
+
</main>
|
|
140
|
+
|
|
141
|
+
{/* THE PANEL — the host's box. The agent renders inline inside it, sized by CSS.
|
|
142
|
+
Showing/hiding this panel is the open/close control; no floating launcher. */}
|
|
143
|
+
<aside>
|
|
144
|
+
<div className="panel-head">
|
|
145
|
+
<span className="mf">✦</span> matterfact assistant
|
|
146
|
+
</div>
|
|
147
|
+
<div className="agent-slot">
|
|
148
|
+
<MatterfactAgent
|
|
149
|
+
inline
|
|
150
|
+
publishableKey={config.publishableKey}
|
|
151
|
+
widgetOrigin={config.widgetOrigin}
|
|
152
|
+
surface={config.surface}
|
|
153
|
+
theme={config.theme}
|
|
154
|
+
// Pulled fresh every turn — the whole point of this example.
|
|
155
|
+
getPageContext={getPageContext}
|
|
156
|
+
style={{ width: '100%', height: '100%' }}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
</aside>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function Kpi({
|
|
166
|
+
label,
|
|
167
|
+
value,
|
|
168
|
+
tone,
|
|
169
|
+
}: {
|
|
170
|
+
label: string;
|
|
171
|
+
value: string;
|
|
172
|
+
tone?: 'pos' | 'neg';
|
|
173
|
+
}) {
|
|
174
|
+
return (
|
|
175
|
+
<div className="kpi">
|
|
176
|
+
<div className="label">{label}</div>
|
|
177
|
+
<div className={`val ${tone ?? ''}`}>{value}</div>
|
|
178
|
+
</div>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function Row(props: {
|
|
183
|
+
t: string;
|
|
184
|
+
w: string;
|
|
185
|
+
d: string;
|
|
186
|
+
y: string;
|
|
187
|
+
th: string;
|
|
188
|
+
dn?: boolean;
|
|
189
|
+
yn?: boolean;
|
|
190
|
+
selected?: boolean;
|
|
191
|
+
onSelect?: () => void;
|
|
192
|
+
}) {
|
|
193
|
+
return (
|
|
194
|
+
<tr
|
|
195
|
+
onClick={props.onSelect}
|
|
196
|
+
aria-pressed={props.selected}
|
|
197
|
+
style={{
|
|
198
|
+
cursor: 'pointer',
|
|
199
|
+
background: props.selected ? 'var(--surface-2)' : undefined,
|
|
200
|
+
}}
|
|
201
|
+
>
|
|
202
|
+
<td className="tick">
|
|
203
|
+
{props.selected ? '▸ ' : ''}
|
|
204
|
+
{props.t}
|
|
205
|
+
</td>
|
|
206
|
+
<td>{props.w}</td>
|
|
207
|
+
<td className={props.dn ? 'neg' : 'pos'}>{props.d}</td>
|
|
208
|
+
<td className={props.yn ? 'neg' : 'pos'}>{props.y}</td>
|
|
209
|
+
<td>{props.th}</td>
|
|
210
|
+
</tr>
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function Setup({ missing }: { missing: string[] }) {
|
|
215
|
+
return (
|
|
216
|
+
<div className="setup">
|
|
217
|
+
<div className="box">
|
|
218
|
+
<h2>Finish the config first</h2>
|
|
219
|
+
<p>
|
|
220
|
+
Copy <code>.env.example</code> to <code>.env</code>, set the values
|
|
221
|
+
below, then restart
|
|
222
|
+
<code>npm run dev</code>.
|
|
223
|
+
</p>
|
|
224
|
+
<ul>
|
|
225
|
+
{missing.map((m) => (
|
|
226
|
+
<li key={m} className="bad">
|
|
227
|
+
✗ {m}
|
|
228
|
+
</li>
|
|
229
|
+
))}
|
|
230
|
+
</ul>
|
|
231
|
+
<p>
|
|
232
|
+
The publishable key must have <code>http://localhost:8000</code>{' '}
|
|
233
|
+
registered as an allowed origin on the target deployment, or the agent
|
|
234
|
+
renders nothing.
|
|
235
|
+
</p>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// All demo config comes from Vite env vars (VITE_*), so the components stay clean
|
|
2
|
+
// and nothing lives in the source. See .env.example.
|
|
3
|
+
|
|
4
|
+
type Theme = 'light' | 'dark' | 'auto';
|
|
5
|
+
|
|
6
|
+
// MF_WIDGET_ORIGIN always wins over this table.
|
|
7
|
+
const ORIGINS: Record<string, string> = {
|
|
8
|
+
ecm: 'https://emc.matterfact.com',
|
|
9
|
+
prod: 'https://app.matterfact.com',
|
|
10
|
+
staging: '', // no fixed host — set VITE_MF_WIDGET_ORIGIN
|
|
11
|
+
local: 'http://localhost:3000', // wherever your dev server serves /embed/chat
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const env = import.meta.env;
|
|
15
|
+
const envName = (env.VITE_MF_ENV ?? 'ecm').toLowerCase();
|
|
16
|
+
|
|
17
|
+
export const config = {
|
|
18
|
+
env: envName,
|
|
19
|
+
widgetOrigin: (env.VITE_MF_WIDGET_ORIGIN || ORIGINS[envName] || '').replace(
|
|
20
|
+
/\/+$/,
|
|
21
|
+
'',
|
|
22
|
+
),
|
|
23
|
+
publishableKey: env.VITE_MF_PUBLISHABLE_KEY ?? '',
|
|
24
|
+
artifact: {
|
|
25
|
+
slug: env.VITE_MF_ARTIFACT_SLUG ?? '',
|
|
26
|
+
owner: env.VITE_MF_ARTIFACT_OWNER ?? '',
|
|
27
|
+
token: env.VITE_MF_ARTIFACT_TOKEN ?? '',
|
|
28
|
+
},
|
|
29
|
+
surface: env.VITE_MF_SURFACE ?? 'embed-demo',
|
|
30
|
+
theme: (env.VITE_MF_THEME ?? 'light') as Theme,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Which required values are still missing — drives the setup screen. */
|
|
34
|
+
export function missingConfig(): string[] {
|
|
35
|
+
const miss: string[] = [];
|
|
36
|
+
if (!config.widgetOrigin)
|
|
37
|
+
miss.push('VITE_MF_ENV / VITE_MF_WIDGET_ORIGIN (widget origin)');
|
|
38
|
+
if (!config.publishableKey) miss.push('VITE_MF_PUBLISHABLE_KEY (agent key)');
|
|
39
|
+
if (!config.artifact.slug) miss.push('VITE_MF_ARTIFACT_SLUG');
|
|
40
|
+
if (!config.artifact.owner) miss.push('VITE_MF_ARTIFACT_OWNER');
|
|
41
|
+
if (!config.artifact.token) miss.push('VITE_MF_ARTIFACT_TOKEN');
|
|
42
|
+
return miss;
|
|
43
|
+
}
|