@matterfact/embed 0.3.0 → 0.5.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 +45 -5
- package/dist/{chunk-33LPVL2R.js → chunk-GZZYR6YF.js} +3 -3
- package/dist/{chunk-33LPVL2R.js.map → chunk-GZZYR6YF.js.map} +3 -3
- package/dist/chunk-JN3ZMONO.js +2 -0
- package/dist/chunk-JN3ZMONO.js.map +7 -0
- package/dist/{chunk-CHPADW72.js → chunk-JSH3BRYP.js} +80 -47
- package/dist/chunk-JSH3BRYP.js.map +1 -0
- package/dist/{chunk-SQJX27LT.js → chunk-NZIN3LK7.js} +79 -46
- package/dist/chunk-NZIN3LK7.js.map +1 -0
- package/dist/chunk-Z53V2P4B.js +2 -0
- package/dist/context-AQY2LJWK.js +3 -0
- package/dist/{context-BFNFFHC2.js.map → context-AQY2LJWK.js.map} +1 -1
- package/dist/{context-MORDASRH.js → context-UBTZEEWS.js} +6 -6
- package/dist/embed.js +1 -1
- package/dist/embed.js.map +3 -3
- package/dist/index.cjs +106 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -3
- package/dist/index.d.ts +23 -3
- package/dist/index.js +23 -17
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +109 -66
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +14 -1
- package/dist/react.d.ts +14 -1
- package/dist/react.js +25 -19
- package/dist/react.js.map +1 -1
- package/dist/{snapshot-IWVXLE4G.js → snapshot-HYMRZTZC.js} +6 -2
- package/dist/snapshot-HYMRZTZC.js.map +1 -0
- package/dist/{snapshot-QBV7DTJP.js → snapshot-XFCV2MYR.js} +7 -4
- package/dist/snapshot-XFCV2MYR.js.map +1 -0
- 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 +189 -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 +237 -0
- package/package.json +3 -2
- package/dist/chunk-CHPADW72.js.map +0 -1
- package/dist/chunk-F7DLOR7F.js +0 -2
- package/dist/chunk-F7DLOR7F.js.map +0 -7
- package/dist/chunk-FBIJZJGL.js +0 -2
- package/dist/chunk-SQJX27LT.js.map +0 -1
- package/dist/context-BFNFFHC2.js +0 -3
- package/dist/snapshot-IWVXLE4G.js.map +0 -1
- package/dist/snapshot-QBV7DTJP.js.map +0 -1
- /package/dist/{chunk-FBIJZJGL.js.map → chunk-Z53V2P4B.js.map} +0 -0
- /package/dist/{context-MORDASRH.js.map → context-UBTZEEWS.js.map} +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { MatterfactAgent, MatterfactArtifact } from '@matterfact/embed/react';
|
|
3
|
+
import { config, missingConfig } from './config';
|
|
4
|
+
|
|
5
|
+
export function App() {
|
|
6
|
+
const [panelOpen, setPanelOpen] = useState(true);
|
|
7
|
+
const missing = missingConfig();
|
|
8
|
+
|
|
9
|
+
if (missing.length) return <Setup missing={missing} />;
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="app" data-panel={panelOpen ? 'open' : 'closed'}>
|
|
13
|
+
<header>
|
|
14
|
+
<div className="brand">
|
|
15
|
+
<span className="mark">M</span> Meridian Capital{' '}
|
|
16
|
+
<small>· embed demo</small>
|
|
17
|
+
</div>
|
|
18
|
+
<nav>
|
|
19
|
+
<a href="#">Positions</a>
|
|
20
|
+
<a href="#" className="active">
|
|
21
|
+
Growth Sleeve
|
|
22
|
+
</a>
|
|
23
|
+
<a href="#">Research</a>
|
|
24
|
+
</nav>
|
|
25
|
+
<span className="spacer" />
|
|
26
|
+
<span className="env-chip">
|
|
27
|
+
{config.env} · {config.widgetOrigin.replace(/^https?:\/\//, '')}
|
|
28
|
+
</span>
|
|
29
|
+
<button className="toggle" onClick={() => setPanelOpen((v) => !v)}>
|
|
30
|
+
<span className="dot" /> Assistant
|
|
31
|
+
</button>
|
|
32
|
+
</header>
|
|
33
|
+
|
|
34
|
+
<div className="layout">
|
|
35
|
+
<main>
|
|
36
|
+
<div className="eyebrow">Portfolio</div>
|
|
37
|
+
<h1>Growth Sleeve</h1>
|
|
38
|
+
<div className="sub">
|
|
39
|
+
12 holdings · rebalanced 3 days ago · benchmark S&P 500
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div className="kpis">
|
|
43
|
+
<Kpi label="Market value" value="$48.2M" />
|
|
44
|
+
<Kpi label="Day change" value="+1.34%" tone="pos" />
|
|
45
|
+
<Kpi label="YTD" value="+18.7%" tone="pos" />
|
|
46
|
+
<Kpi label="vs benchmark" value="−2.1%" tone="neg" />
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div className="section-title">Embedded matterfact artifact</div>
|
|
50
|
+
<div className="artifact-frame">
|
|
51
|
+
<div className="artifact-badge">
|
|
52
|
+
<span className="mf">✦</span> matterfact artifact ·{' '}
|
|
53
|
+
<code>{config.artifact.slug}</code>
|
|
54
|
+
</div>
|
|
55
|
+
{/* The agent, mounted on the same page below, auto-detects this artifact
|
|
56
|
+
by origin and can answer questions about the data behind it. */}
|
|
57
|
+
<MatterfactArtifact
|
|
58
|
+
slug={config.artifact.slug}
|
|
59
|
+
owner={config.artifact.owner}
|
|
60
|
+
token={config.artifact.token}
|
|
61
|
+
widgetOrigin={config.widgetOrigin}
|
|
62
|
+
theme={config.theme}
|
|
63
|
+
style={{ width: '100%', height: '100%' }}
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
<p className="artifact-caption">
|
|
67
|
+
<strong>Artifact-aware:</strong> the assistant detects this embedded
|
|
68
|
+
artifact by its origin and can pull the real data behind it — try
|
|
69
|
+
asking it to summarise the artifact.
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
<div className="section-title">Holdings</div>
|
|
73
|
+
<table>
|
|
74
|
+
<thead>
|
|
75
|
+
<tr>
|
|
76
|
+
<th>Ticker</th>
|
|
77
|
+
<th>Weight</th>
|
|
78
|
+
<th>Day</th>
|
|
79
|
+
<th>YTD</th>
|
|
80
|
+
<th>Thesis</th>
|
|
81
|
+
</tr>
|
|
82
|
+
</thead>
|
|
83
|
+
<tbody>
|
|
84
|
+
<Row
|
|
85
|
+
t="NVDA"
|
|
86
|
+
w="9.4%"
|
|
87
|
+
d="+2.8%"
|
|
88
|
+
y="+41%"
|
|
89
|
+
th="AI datacenter demand"
|
|
90
|
+
/>
|
|
91
|
+
<Row t="MSFT" w="8.1%" d="+0.9%" y="+22%" th="Copilot attach" />
|
|
92
|
+
<Row t="UNH" w="6.7%" d="−1.2%" y="−8%" th="MLR pressure" dn yn />
|
|
93
|
+
<Row
|
|
94
|
+
t="V"
|
|
95
|
+
w="5.9%"
|
|
96
|
+
d="+0.4%"
|
|
97
|
+
y="+11%"
|
|
98
|
+
th="Cross-border recovery"
|
|
99
|
+
/>
|
|
100
|
+
<Row t="LLY" w="5.2%" d="+1.7%" y="+34%" th="GLP-1 ramp" />
|
|
101
|
+
</tbody>
|
|
102
|
+
</table>
|
|
103
|
+
</main>
|
|
104
|
+
|
|
105
|
+
{/* THE PANEL — the host's box. The agent renders inline inside it, sized by CSS.
|
|
106
|
+
Showing/hiding this panel is the open/close control; no floating launcher. */}
|
|
107
|
+
<aside>
|
|
108
|
+
<div className="panel-head">
|
|
109
|
+
<span className="mf">✦</span> matterfact assistant
|
|
110
|
+
</div>
|
|
111
|
+
<div className="agent-slot">
|
|
112
|
+
<MatterfactAgent
|
|
113
|
+
inline
|
|
114
|
+
publishableKey={config.publishableKey}
|
|
115
|
+
widgetOrigin={config.widgetOrigin}
|
|
116
|
+
surface={config.surface}
|
|
117
|
+
theme={config.theme}
|
|
118
|
+
style={{ width: '100%', height: '100%' }}
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
</aside>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function Kpi({
|
|
128
|
+
label,
|
|
129
|
+
value,
|
|
130
|
+
tone,
|
|
131
|
+
}: {
|
|
132
|
+
label: string;
|
|
133
|
+
value: string;
|
|
134
|
+
tone?: 'pos' | 'neg';
|
|
135
|
+
}) {
|
|
136
|
+
return (
|
|
137
|
+
<div className="kpi">
|
|
138
|
+
<div className="label">{label}</div>
|
|
139
|
+
<div className={`val ${tone ?? ''}`}>{value}</div>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function Row(props: {
|
|
145
|
+
t: string;
|
|
146
|
+
w: string;
|
|
147
|
+
d: string;
|
|
148
|
+
y: string;
|
|
149
|
+
th: string;
|
|
150
|
+
dn?: boolean;
|
|
151
|
+
yn?: boolean;
|
|
152
|
+
}) {
|
|
153
|
+
return (
|
|
154
|
+
<tr>
|
|
155
|
+
<td className="tick">{props.t}</td>
|
|
156
|
+
<td>{props.w}</td>
|
|
157
|
+
<td className={props.dn ? 'neg' : 'pos'}>{props.d}</td>
|
|
158
|
+
<td className={props.yn ? 'neg' : 'pos'}>{props.y}</td>
|
|
159
|
+
<td>{props.th}</td>
|
|
160
|
+
</tr>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function Setup({ missing }: { missing: string[] }) {
|
|
165
|
+
return (
|
|
166
|
+
<div className="setup">
|
|
167
|
+
<div className="box">
|
|
168
|
+
<h2>Finish the config first</h2>
|
|
169
|
+
<p>
|
|
170
|
+
Copy <code>.env.example</code> to <code>.env</code>, set the values
|
|
171
|
+
below, then restart
|
|
172
|
+
<code>npm run dev</code>.
|
|
173
|
+
</p>
|
|
174
|
+
<ul>
|
|
175
|
+
{missing.map((m) => (
|
|
176
|
+
<li key={m} className="bad">
|
|
177
|
+
✗ {m}
|
|
178
|
+
</li>
|
|
179
|
+
))}
|
|
180
|
+
</ul>
|
|
181
|
+
<p>
|
|
182
|
+
The publishable key must have <code>http://localhost:8000</code>{' '}
|
|
183
|
+
registered as an allowed origin on the target deployment, or the agent
|
|
184
|
+
renders nothing.
|
|
185
|
+
</p>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
);
|
|
189
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--bg: #f4f6f9;
|
|
3
|
+
--surface: #fff;
|
|
4
|
+
--surface-2: #f0f3f7;
|
|
5
|
+
--line: #dfe4ec;
|
|
6
|
+
--ink: #131722;
|
|
7
|
+
--muted: #616b7c;
|
|
8
|
+
--accent: #2f5fe0;
|
|
9
|
+
--pos: #128a5b;
|
|
10
|
+
--neg: #c2453d;
|
|
11
|
+
--panel-w: 420px;
|
|
12
|
+
--sans:
|
|
13
|
+
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
|
|
14
|
+
sans-serif;
|
|
15
|
+
--mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
16
|
+
}
|
|
17
|
+
@media (prefers-color-scheme: dark) {
|
|
18
|
+
:root {
|
|
19
|
+
--bg: #0f1419;
|
|
20
|
+
--surface: #161c24;
|
|
21
|
+
--surface-2: #1d2530;
|
|
22
|
+
--line: #28303c;
|
|
23
|
+
--ink: #e7ecf2;
|
|
24
|
+
--muted: #8b97a6;
|
|
25
|
+
--accent: #6b93ff;
|
|
26
|
+
--pos: #4ea88a;
|
|
27
|
+
--neg: #d1706a;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
* {
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
}
|
|
33
|
+
html,
|
|
34
|
+
body,
|
|
35
|
+
#root {
|
|
36
|
+
height: 100%;
|
|
37
|
+
margin: 0;
|
|
38
|
+
}
|
|
39
|
+
body {
|
|
40
|
+
background: var(--bg);
|
|
41
|
+
color: var(--ink);
|
|
42
|
+
font-family: var(--sans);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.app {
|
|
46
|
+
display: grid;
|
|
47
|
+
grid-template-rows: auto 1fr;
|
|
48
|
+
height: 100vh;
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
header {
|
|
53
|
+
display: flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: 16px;
|
|
56
|
+
padding: 0 20px;
|
|
57
|
+
height: 56px;
|
|
58
|
+
border-bottom: 1px solid var(--line);
|
|
59
|
+
background: var(--surface);
|
|
60
|
+
}
|
|
61
|
+
.brand {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 10px;
|
|
65
|
+
font-weight: 700;
|
|
66
|
+
letter-spacing: -0.01em;
|
|
67
|
+
}
|
|
68
|
+
.brand .mark {
|
|
69
|
+
width: 26px;
|
|
70
|
+
height: 26px;
|
|
71
|
+
border-radius: 7px;
|
|
72
|
+
background: linear-gradient(135deg, var(--accent), #1b3fa8);
|
|
73
|
+
display: grid;
|
|
74
|
+
place-items: center;
|
|
75
|
+
color: #fff;
|
|
76
|
+
font-weight: 800;
|
|
77
|
+
font-size: 15px;
|
|
78
|
+
}
|
|
79
|
+
.brand small {
|
|
80
|
+
color: var(--muted);
|
|
81
|
+
font-weight: 500;
|
|
82
|
+
margin-left: 2px;
|
|
83
|
+
}
|
|
84
|
+
nav {
|
|
85
|
+
display: flex;
|
|
86
|
+
gap: 4px;
|
|
87
|
+
margin-left: 12px;
|
|
88
|
+
}
|
|
89
|
+
nav a {
|
|
90
|
+
color: var(--muted);
|
|
91
|
+
text-decoration: none;
|
|
92
|
+
font-size: 13.5px;
|
|
93
|
+
font-weight: 500;
|
|
94
|
+
padding: 6px 11px;
|
|
95
|
+
border-radius: 7px;
|
|
96
|
+
}
|
|
97
|
+
nav a.active {
|
|
98
|
+
color: var(--ink);
|
|
99
|
+
background: var(--surface-2);
|
|
100
|
+
}
|
|
101
|
+
.spacer {
|
|
102
|
+
flex: 1;
|
|
103
|
+
}
|
|
104
|
+
.env-chip {
|
|
105
|
+
font-family: var(--mono);
|
|
106
|
+
font-size: 11.5px;
|
|
107
|
+
color: var(--muted);
|
|
108
|
+
border: 1px solid var(--line);
|
|
109
|
+
border-radius: 999px;
|
|
110
|
+
padding: 4px 10px;
|
|
111
|
+
}
|
|
112
|
+
.toggle {
|
|
113
|
+
display: flex;
|
|
114
|
+
align-items: center;
|
|
115
|
+
gap: 8px;
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
background: var(--surface-2);
|
|
118
|
+
color: var(--ink);
|
|
119
|
+
border: 1px solid var(--line);
|
|
120
|
+
border-radius: 8px;
|
|
121
|
+
padding: 7px 12px;
|
|
122
|
+
font: inherit;
|
|
123
|
+
font-size: 13px;
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
}
|
|
126
|
+
.toggle:hover {
|
|
127
|
+
border-color: var(--accent);
|
|
128
|
+
}
|
|
129
|
+
.toggle .dot {
|
|
130
|
+
width: 7px;
|
|
131
|
+
height: 7px;
|
|
132
|
+
border-radius: 50%;
|
|
133
|
+
background: var(--pos);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.layout {
|
|
137
|
+
display: grid;
|
|
138
|
+
grid-template-columns: 1fr var(--panel-w);
|
|
139
|
+
min-height: 0;
|
|
140
|
+
}
|
|
141
|
+
.app[data-panel='closed'] .layout {
|
|
142
|
+
grid-template-columns: 1fr 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
main {
|
|
146
|
+
overflow-y: auto;
|
|
147
|
+
padding: 28px 32px 40px;
|
|
148
|
+
min-width: 0;
|
|
149
|
+
}
|
|
150
|
+
.eyebrow {
|
|
151
|
+
font-family: var(--mono);
|
|
152
|
+
font-size: 11.5px;
|
|
153
|
+
letter-spacing: 0.08em;
|
|
154
|
+
text-transform: uppercase;
|
|
155
|
+
color: var(--muted);
|
|
156
|
+
}
|
|
157
|
+
h1 {
|
|
158
|
+
font-size: 26px;
|
|
159
|
+
letter-spacing: -0.02em;
|
|
160
|
+
margin: 6px 0 2px;
|
|
161
|
+
}
|
|
162
|
+
.sub {
|
|
163
|
+
color: var(--muted);
|
|
164
|
+
font-size: 14px;
|
|
165
|
+
margin-bottom: 26px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.kpis {
|
|
169
|
+
display: grid;
|
|
170
|
+
grid-template-columns: repeat(4, 1fr);
|
|
171
|
+
gap: 12px;
|
|
172
|
+
margin-bottom: 26px;
|
|
173
|
+
}
|
|
174
|
+
.kpi {
|
|
175
|
+
background: var(--surface);
|
|
176
|
+
border: 1px solid var(--line);
|
|
177
|
+
border-radius: 12px;
|
|
178
|
+
padding: 14px 16px;
|
|
179
|
+
}
|
|
180
|
+
.kpi .label {
|
|
181
|
+
font-size: 11.5px;
|
|
182
|
+
color: var(--muted);
|
|
183
|
+
text-transform: uppercase;
|
|
184
|
+
letter-spacing: 0.05em;
|
|
185
|
+
}
|
|
186
|
+
.kpi .val {
|
|
187
|
+
font-size: 22px;
|
|
188
|
+
font-weight: 700;
|
|
189
|
+
margin-top: 6px;
|
|
190
|
+
font-variant-numeric: tabular-nums;
|
|
191
|
+
}
|
|
192
|
+
.kpi .val.pos {
|
|
193
|
+
color: var(--pos);
|
|
194
|
+
}
|
|
195
|
+
.kpi .val.neg {
|
|
196
|
+
color: var(--neg);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.section-title {
|
|
200
|
+
font-size: 13px;
|
|
201
|
+
font-weight: 700;
|
|
202
|
+
color: var(--muted);
|
|
203
|
+
text-transform: uppercase;
|
|
204
|
+
letter-spacing: 0.05em;
|
|
205
|
+
margin: 4px 0 12px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.artifact-frame {
|
|
209
|
+
position: relative;
|
|
210
|
+
width: 100%;
|
|
211
|
+
height: 360px;
|
|
212
|
+
border: 1px solid var(--line);
|
|
213
|
+
border-radius: 12px;
|
|
214
|
+
background: var(--surface-2);
|
|
215
|
+
margin-bottom: 8px;
|
|
216
|
+
overflow: hidden;
|
|
217
|
+
}
|
|
218
|
+
.artifact-badge {
|
|
219
|
+
position: absolute;
|
|
220
|
+
top: 12px;
|
|
221
|
+
left: 12px;
|
|
222
|
+
z-index: 2;
|
|
223
|
+
display: flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
gap: 7px;
|
|
226
|
+
background: color-mix(in srgb, var(--surface) 82%, transparent);
|
|
227
|
+
backdrop-filter: blur(4px);
|
|
228
|
+
border: 1px solid var(--line);
|
|
229
|
+
border-radius: 999px;
|
|
230
|
+
padding: 5px 11px;
|
|
231
|
+
font-size: 11.5px;
|
|
232
|
+
color: var(--muted);
|
|
233
|
+
}
|
|
234
|
+
.artifact-badge .mf {
|
|
235
|
+
color: var(--accent);
|
|
236
|
+
}
|
|
237
|
+
.artifact-badge code {
|
|
238
|
+
color: var(--ink);
|
|
239
|
+
font-family: var(--mono);
|
|
240
|
+
font-size: 11px;
|
|
241
|
+
}
|
|
242
|
+
.artifact-caption {
|
|
243
|
+
font-size: 12.5px;
|
|
244
|
+
color: var(--muted);
|
|
245
|
+
margin: 0 0 26px;
|
|
246
|
+
}
|
|
247
|
+
.artifact-caption strong {
|
|
248
|
+
color: var(--ink);
|
|
249
|
+
font-weight: 600;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
table {
|
|
253
|
+
width: 100%;
|
|
254
|
+
border-collapse: collapse;
|
|
255
|
+
font-size: 13.5px;
|
|
256
|
+
}
|
|
257
|
+
th {
|
|
258
|
+
text-align: left;
|
|
259
|
+
color: var(--muted);
|
|
260
|
+
font-weight: 600;
|
|
261
|
+
font-size: 11.5px;
|
|
262
|
+
text-transform: uppercase;
|
|
263
|
+
letter-spacing: 0.04em;
|
|
264
|
+
padding: 8px 10px;
|
|
265
|
+
border-bottom: 1px solid var(--line);
|
|
266
|
+
}
|
|
267
|
+
td {
|
|
268
|
+
padding: 11px 10px;
|
|
269
|
+
border-bottom: 1px solid var(--line);
|
|
270
|
+
font-variant-numeric: tabular-nums;
|
|
271
|
+
}
|
|
272
|
+
td.tick {
|
|
273
|
+
font-weight: 700;
|
|
274
|
+
}
|
|
275
|
+
td.pos {
|
|
276
|
+
color: var(--pos);
|
|
277
|
+
}
|
|
278
|
+
td.neg {
|
|
279
|
+
color: var(--neg);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
aside {
|
|
283
|
+
border-left: 1px solid var(--line);
|
|
284
|
+
background: var(--surface);
|
|
285
|
+
display: flex;
|
|
286
|
+
flex-direction: column;
|
|
287
|
+
min-width: 0;
|
|
288
|
+
overflow: hidden;
|
|
289
|
+
}
|
|
290
|
+
.panel-head {
|
|
291
|
+
display: flex;
|
|
292
|
+
align-items: center;
|
|
293
|
+
gap: 8px;
|
|
294
|
+
height: 44px;
|
|
295
|
+
padding: 0 14px;
|
|
296
|
+
border-bottom: 1px solid var(--line);
|
|
297
|
+
font-size: 12.5px;
|
|
298
|
+
font-weight: 700;
|
|
299
|
+
color: var(--muted);
|
|
300
|
+
text-transform: uppercase;
|
|
301
|
+
letter-spacing: 0.05em;
|
|
302
|
+
}
|
|
303
|
+
.panel-head .mf {
|
|
304
|
+
color: var(--accent);
|
|
305
|
+
}
|
|
306
|
+
.agent-slot {
|
|
307
|
+
flex: 1;
|
|
308
|
+
min-height: 0;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.setup {
|
|
312
|
+
display: grid;
|
|
313
|
+
place-items: center;
|
|
314
|
+
height: 100vh;
|
|
315
|
+
padding: 24px;
|
|
316
|
+
}
|
|
317
|
+
.setup .box {
|
|
318
|
+
max-width: 580px;
|
|
319
|
+
background: var(--surface);
|
|
320
|
+
border: 1px solid var(--line);
|
|
321
|
+
border-radius: 14px;
|
|
322
|
+
padding: 28px 30px;
|
|
323
|
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12);
|
|
324
|
+
}
|
|
325
|
+
.setup h2 {
|
|
326
|
+
margin: 0 0 6px;
|
|
327
|
+
font-size: 18px;
|
|
328
|
+
}
|
|
329
|
+
.setup p {
|
|
330
|
+
color: var(--muted);
|
|
331
|
+
font-size: 14px;
|
|
332
|
+
line-height: 1.55;
|
|
333
|
+
margin: 10px 0;
|
|
334
|
+
}
|
|
335
|
+
.setup code {
|
|
336
|
+
font-family: var(--mono);
|
|
337
|
+
font-size: 12.5px;
|
|
338
|
+
background: var(--surface-2);
|
|
339
|
+
padding: 2px 6px;
|
|
340
|
+
border-radius: 5px;
|
|
341
|
+
color: var(--ink);
|
|
342
|
+
margin: 0 2px;
|
|
343
|
+
}
|
|
344
|
+
.setup ul {
|
|
345
|
+
margin: 8px 0 0;
|
|
346
|
+
padding-left: 18px;
|
|
347
|
+
}
|
|
348
|
+
.setup li {
|
|
349
|
+
font-size: 13.5px;
|
|
350
|
+
line-height: 1.7;
|
|
351
|
+
}
|
|
352
|
+
.setup li.bad {
|
|
353
|
+
color: var(--neg);
|
|
354
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noUnusedLocals": true,
|
|
10
|
+
"noUnusedParameters": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"types": ["vite/client"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["src", "vite.config.ts"]
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
|
|
4
|
+
// The dev server runs on localhost:8000 — that exact origin must be registered
|
|
5
|
+
// as an allowed origin on your publishable key. strictPort so it never silently
|
|
6
|
+
// drifts to a different (unregistered) port.
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [react()],
|
|
9
|
+
server: { host: 'localhost', port: 8000, strictPort: true },
|
|
10
|
+
preview: { host: 'localhost', port: 8000, strictPort: true },
|
|
11
|
+
});
|