@elicitkit/renderers 0.1.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/LICENSE +201 -0
- package/dist/apps.d.ts +10 -0
- package/dist/apps.d.ts.map +1 -0
- package/dist/apps.js +26 -0
- package/dist/apps.js.map +1 -0
- package/dist/contract.d.ts +84 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +9 -0
- package/dist/contract.js.map +1 -0
- package/dist/elicitation.d.ts +41 -0
- package/dist/elicitation.d.ts.map +1 -0
- package/dist/elicitation.js +345 -0
- package/dist/elicitation.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/panel.d.ts +36 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/panel.js +608 -0
- package/dist/panel.js.map +1 -0
- package/dist/tui.d.ts +10 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +89 -0
- package/dist/tui.js.map +1 -0
- package/dist/url.d.ts +20 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +40 -0
- package/dist/url.js.map +1 -0
- package/package.json +17 -0
package/dist/panel.js
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
import { jsonForScript } from "./contract.js";
|
|
2
|
+
/**
|
|
3
|
+
* The ONE canonical safe-color grammar — identical to the JSON Schema
|
|
4
|
+
* `safeColor` pattern, the conformance corpus, and SPEC §7.y. Accepts
|
|
5
|
+
* `#hex` (3/4/6/8), a CSS named/keyword color, and the common functional
|
|
6
|
+
* notations with a safe inner charset; it structurally cannot express
|
|
7
|
+
* `url(`, `var(`, `image-set(`, `element(`, gradients, quotes, `;`, or a
|
|
8
|
+
* nested `(` — i.e. no CSS value-injection / exfiltration / breakout. Held
|
|
9
|
+
* as a string so it round-trips through `jsonForScript` into the panel
|
|
10
|
+
* script with layer-proof escaping (regex literals in a template string
|
|
11
|
+
* mangle backslashes; a JSON string + `new RegExp` does not).
|
|
12
|
+
*/
|
|
13
|
+
export const SAFE_COLOR_PATTERN = "^(?:#[0-9a-fA-F]{3,8}|[a-zA-Z]+|(?:rgb|rgba|hsl|hsla|hwb|lab|lch|oklab|oklch|color)\\([0-9a-zA-Z.,%/ +\\-]*\\))$";
|
|
14
|
+
export const SAFE_COLOR_RE = new RegExp(SAFE_COLOR_PATTERN);
|
|
15
|
+
export function buildPanelHtml(set, token, tier, opts = {}) {
|
|
16
|
+
const asksJson = jsonForScript(set.asks);
|
|
17
|
+
const tokenJson = jsonForScript(token);
|
|
18
|
+
const tierJson = jsonForScript(tier);
|
|
19
|
+
const submitUrlJson = jsonForScript(opts.submitUrl ?? null);
|
|
20
|
+
const safeColorPatternJson = jsonForScript(SAFE_COLOR_PATTERN);
|
|
21
|
+
// Meta-CSP mirroring the proven-good header policy the hosted server sends
|
|
22
|
+
// (packages/http server.ts). The panel's own inline <style>/<script> is
|
|
23
|
+
// designed CSP-safe (no external/eval), so 'unsafe-inline' for style/script
|
|
24
|
+
// is sufficient and nothing else is needed. connect-src is the submit URL's
|
|
25
|
+
// ORIGIN when built with a submitUrl (the hosted/fetch round-trip path);
|
|
26
|
+
// otherwise 'none' (the data:/file/elicit_render copy-paste path makes no
|
|
27
|
+
// network calls). This is defence-in-depth for channels with no header CSP
|
|
28
|
+
// (mcp-ui rawHtml, data: URL, copy-paste); it must NOT regress the hosted
|
|
29
|
+
// /r/:token POST-back, hence connect-src = the submit origin there.
|
|
30
|
+
let connectSrc = "'none'";
|
|
31
|
+
if (opts.submitUrl) {
|
|
32
|
+
try {
|
|
33
|
+
connectSrc = new URL(opts.submitUrl).origin;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
connectSrc = "'none'";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const metaCsp = "default-src 'none'; base-uri 'none'; form-action 'none'; " +
|
|
40
|
+
"img-src data:; style-src 'unsafe-inline'; script-src 'unsafe-inline'; " +
|
|
41
|
+
`connect-src ${connectSrc}`;
|
|
42
|
+
const metaCspAttr = metaCsp.replace(/"/g, """);
|
|
43
|
+
const setMeta = set.meta ?? {};
|
|
44
|
+
const layoutJson = jsonForScript(setMeta.layout ?? "auto");
|
|
45
|
+
const themeJson = jsonForScript(setMeta.theme ?? "system");
|
|
46
|
+
const accentJson = jsonForScript(setMeta.accent ?? null);
|
|
47
|
+
return `<!DOCTYPE html>
|
|
48
|
+
<html lang="en">
|
|
49
|
+
<head>
|
|
50
|
+
<meta charset="utf-8" />
|
|
51
|
+
<meta http-equiv="Content-Security-Policy" content="${metaCspAttr}" />
|
|
52
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
53
|
+
<style>
|
|
54
|
+
:root {
|
|
55
|
+
color-scheme: light dark;
|
|
56
|
+
--bg: Canvas; --fg: CanvasText;
|
|
57
|
+
--line: color-mix(in srgb, var(--fg) 13%, transparent);
|
|
58
|
+
--soft: color-mix(in srgb, var(--fg) 4%, transparent);
|
|
59
|
+
--soft2: color-mix(in srgb, var(--fg) 7%, transparent);
|
|
60
|
+
--muted: color-mix(in srgb, var(--fg) 58%, transparent);
|
|
61
|
+
--accent: AccentColor;
|
|
62
|
+
--accent-soft: color-mix(in srgb, var(--accent) 13%, transparent);
|
|
63
|
+
--accent-line: color-mix(in srgb, var(--accent) 55%, var(--line));
|
|
64
|
+
--r: 14px; --r-sm: 10px;
|
|
65
|
+
--sp: 16px;
|
|
66
|
+
--shadow: 0 1px 2px color-mix(in srgb, CanvasText 8%, transparent),
|
|
67
|
+
0 8px 24px color-mix(in srgb, CanvasText 6%, transparent);
|
|
68
|
+
}
|
|
69
|
+
/* Skins (set.meta.theme). system = native light/dark; others fixed. */
|
|
70
|
+
[data-theme="midnight"] { --bg: #0b0e16; --fg: #e7e9ee; color-scheme: dark; }
|
|
71
|
+
[data-theme="paper"] { --bg: #faf7f0; --fg: #2a2722; color-scheme: light; }
|
|
72
|
+
[data-theme="high-contrast"] {
|
|
73
|
+
--bg: #000; --fg: #fff; --accent: #ffd400;
|
|
74
|
+
--line: color-mix(in srgb, var(--fg) 55%, transparent); color-scheme: dark; }
|
|
75
|
+
* { box-sizing: border-box; }
|
|
76
|
+
body { font: 15px/1.6 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
77
|
+
margin: 0; padding: 34px 18px 124px; background: var(--bg); color: var(--fg);
|
|
78
|
+
-webkit-font-smoothing: antialiased; }
|
|
79
|
+
.wrap { max-width: 860px; margin: 0 auto; }
|
|
80
|
+
header { display: flex; align-items: center; gap: 11px; margin: 0 0 26px; }
|
|
81
|
+
header .mark { font-size: 22px; color: var(--accent); }
|
|
82
|
+
header h1 { font-size: 18px; font-weight: 700; margin: 0; letter-spacing: -.015em; }
|
|
83
|
+
header .count { margin-left: auto; font-size: 12.5px; color: var(--muted);
|
|
84
|
+
padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px; }
|
|
85
|
+
#f { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp); }
|
|
86
|
+
@media (max-width: 640px) { #f { grid-template-columns: 1fr; } }
|
|
87
|
+
.ask { background: var(--soft); border: 1px solid var(--line);
|
|
88
|
+
border-radius: var(--r); padding: 20px; min-width: 0;
|
|
89
|
+
transition: border-color .14s, box-shadow .14s; }
|
|
90
|
+
.ask.full { grid-column: 1 / -1; }
|
|
91
|
+
.ask:focus-within { border-color: var(--accent-line); box-shadow: var(--shadow); }
|
|
92
|
+
.prompt { font-weight: 650; font-size: 15.5px; margin: 0 0 3px; letter-spacing: -.01em; }
|
|
93
|
+
.req { color: color-mix(in srgb, #e00 70%, CanvasText); margin-left: 4px; }
|
|
94
|
+
.help { color: var(--muted); margin: 0 0 14px; font-size: 13.5px; }
|
|
95
|
+
.field { margin-top: 12px; }
|
|
96
|
+
textarea, input[type=text], input[type=number], input[type=date],
|
|
97
|
+
input[type=datetime-local] { width: 100%; font: inherit; padding: 10px 12px;
|
|
98
|
+
border-radius: var(--r-sm); border: 1px solid var(--line);
|
|
99
|
+
background: var(--bg); color: var(--fg); transition: border-color .12s, box-shadow .12s; }
|
|
100
|
+
textarea:focus, input:focus { outline: none; border-color: var(--accent);
|
|
101
|
+
box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
102
|
+
textarea { resize: vertical; min-height: 80px; }
|
|
103
|
+
.ic { display: inline-flex; width: 18px; height: 18px; flex: none;
|
|
104
|
+
align-items: center; justify-content: center; }
|
|
105
|
+
.ic svg { width: 100%; height: 100%; }
|
|
106
|
+
|
|
107
|
+
/* option list (default) */
|
|
108
|
+
.opts { display: grid; gap: 8px; }
|
|
109
|
+
.opts.cards, .opts.grid2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
|
|
110
|
+
@media (max-width: 520px) { .opts.cards, .opts.grid2 { grid-template-columns: 1fr; } }
|
|
111
|
+
.opt { display: flex; gap: 11px; align-items: flex-start; padding: 11px 13px;
|
|
112
|
+
border: 1px solid var(--line); border-radius: var(--r-sm); cursor: pointer;
|
|
113
|
+
background: var(--bg); transition: background .12s, border-color .12s, transform .06s; }
|
|
114
|
+
.opt:hover { background: var(--soft2); }
|
|
115
|
+
.opt:active { transform: translateY(1px); }
|
|
116
|
+
.opt:has(:checked) { border-color: var(--accent); background: var(--accent-soft); }
|
|
117
|
+
.opt:has(:focus-visible) { box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
118
|
+
.opt input { margin: 2px 0 0; accent-color: var(--accent); flex: none; }
|
|
119
|
+
.opt .lbl { font-weight: 560; }
|
|
120
|
+
.opt small { display: block; color: var(--muted); margin-top: 3px; line-height: 1.45; }
|
|
121
|
+
.opt .ic { margin-top: 1px; color: var(--accent); }
|
|
122
|
+
/* card variant: bigger hit area, icon on top, hidden native input */
|
|
123
|
+
.opts.cards .opt, .opts.grid2 .opt { flex-direction: column; gap: 8px; padding: 16px; }
|
|
124
|
+
.opts.cards .opt input, .opts.grid2 .opt input,
|
|
125
|
+
.opts.seg .opt input { position: absolute; opacity: 0; pointer-events: none; }
|
|
126
|
+
.opts.cards .opt .ic, .opts.grid2 .opt .ic { width: 22px; height: 22px; }
|
|
127
|
+
/* segmented variant */
|
|
128
|
+
.opts.seg { display: inline-flex; gap: 0; border: 1px solid var(--line);
|
|
129
|
+
border-radius: 999px; overflow: hidden; background: var(--bg); }
|
|
130
|
+
.opts.seg .opt { border: 0; border-radius: 0; padding: 8px 16px; background: transparent;
|
|
131
|
+
flex-direction: row; align-items: center; gap: 7px; }
|
|
132
|
+
.opts.seg .opt + .opt { border-left: 1px solid var(--line); }
|
|
133
|
+
.opts.seg .opt:has(:checked) { background: var(--accent); color: AccentColorText; }
|
|
134
|
+
.opts.seg .opt small { display: none; }
|
|
135
|
+
|
|
136
|
+
.swatch { width: 18px; height: 18px; border-radius: 5px; flex: none;
|
|
137
|
+
border: 1px solid color-mix(in srgb, CanvasText 25%, transparent); }
|
|
138
|
+
.swatches { display: flex; flex-wrap: wrap; gap: 10px; }
|
|
139
|
+
.chip { width: 38px; height: 38px; border-radius: 9px; cursor: pointer;
|
|
140
|
+
border: 2px solid var(--line); transition: transform .08s, box-shadow .12s;
|
|
141
|
+
padding: 0; }
|
|
142
|
+
.chip:hover { transform: scale(1.08); }
|
|
143
|
+
.chip.on { border-color: CanvasText;
|
|
144
|
+
box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
145
|
+
|
|
146
|
+
.file { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 13px;
|
|
147
|
+
font-weight: 600; margin: 14px 0 6px; color: var(--muted); }
|
|
148
|
+
.hunk { border: 1px solid var(--line); border-radius: var(--r-sm); margin: 10px 0;
|
|
149
|
+
overflow: hidden; }
|
|
150
|
+
.hunk-hdr { display: flex; justify-content: space-between; align-items: center;
|
|
151
|
+
gap: 10px; padding: 8px 12px; background: var(--soft2);
|
|
152
|
+
font-family: ui-monospace, monospace; font-size: 12px; }
|
|
153
|
+
pre { margin: 0; padding: 9px 12px; white-space: pre-wrap; font-size: 12.5px;
|
|
154
|
+
line-height: 1.5; font-family: ui-monospace, SFMono-Regular, monospace; }
|
|
155
|
+
pre.before { background: color-mix(in srgb, #d00 13%, transparent);
|
|
156
|
+
border-top: 1px solid var(--line); }
|
|
157
|
+
pre.after { background: color-mix(in srgb, #090 14%, transparent); }
|
|
158
|
+
.seg { display: inline-flex; border-radius: 8px; overflow: hidden;
|
|
159
|
+
border: 1px solid var(--line); }
|
|
160
|
+
.seg button { font: inherit; font-size: 12.5px; padding: 5px 14px; cursor: pointer;
|
|
161
|
+
border: 0; background: var(--soft2); color: var(--fg); transition: .12s; }
|
|
162
|
+
.seg button + button { border-left: 1px solid var(--line); }
|
|
163
|
+
.seg button[aria-pressed=true] { background: var(--accent); color: AccentColorText;
|
|
164
|
+
font-weight: 600; }
|
|
165
|
+
.rrow { display: flex; justify-content: space-between; align-items: center;
|
|
166
|
+
gap: 10px; padding: 11px 13px; border: 1px solid var(--line);
|
|
167
|
+
border-radius: var(--r-sm); margin: 8px 0; background: var(--bg); }
|
|
168
|
+
.stars { display: inline-flex; gap: 4px; }
|
|
169
|
+
.stars button { font: inherit; font-size: 24px; line-height: 1; padding: 2px 4px;
|
|
170
|
+
border: 0; background: transparent; color: var(--muted); cursor: pointer;
|
|
171
|
+
transition: transform .08s, color .12s; }
|
|
172
|
+
.stars button:hover { transform: scale(1.15); }
|
|
173
|
+
.stars button.on { color: var(--accent); }
|
|
174
|
+
.slider { display: flex; align-items: center; gap: 14px; }
|
|
175
|
+
.slider input[type=range] { flex: 1; accent-color: var(--accent); }
|
|
176
|
+
.slider output { font-variant-numeric: tabular-nums; font-weight: 650;
|
|
177
|
+
min-width: 3ch; text-align: right; }
|
|
178
|
+
.bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 5;
|
|
179
|
+
display: flex; gap: 10px; justify-content: center;
|
|
180
|
+
padding: 14px; background: color-mix(in srgb, Canvas 86%, transparent);
|
|
181
|
+
backdrop-filter: blur(10px); border-top: 1px solid var(--line); }
|
|
182
|
+
.bar button { font: inherit; font-weight: 650; padding: 11px 26px;
|
|
183
|
+
border-radius: var(--r-sm); cursor: pointer; border: 0;
|
|
184
|
+
background: var(--accent); color: AccentColorText; transition: .12s; }
|
|
185
|
+
.bar button:hover { filter: brightness(1.07); }
|
|
186
|
+
.bar button:active { transform: translateY(1px); }
|
|
187
|
+
.bar button.ghost { background: transparent; color: var(--muted);
|
|
188
|
+
border: 1px solid var(--line); }
|
|
189
|
+
.bar button.ghost:hover { color: CanvasText; }
|
|
190
|
+
#copyout { grid-column: 1 / -1; background: var(--soft); border: 1px solid var(--line);
|
|
191
|
+
border-radius: var(--r); padding: 16px; }
|
|
192
|
+
@media (prefers-reduced-motion: reduce) { * { transition: none !important;
|
|
193
|
+
animation: none !important; } }
|
|
194
|
+
</style>
|
|
195
|
+
</head>
|
|
196
|
+
<body>
|
|
197
|
+
<div class="wrap">
|
|
198
|
+
<header>
|
|
199
|
+
<span class="mark">‽</span>
|
|
200
|
+
<h1>Elicitkit needs your input</h1>
|
|
201
|
+
<span class="count" id="count"></span>
|
|
202
|
+
</header>
|
|
203
|
+
<form id="f">
|
|
204
|
+
<div id="copyout" hidden>
|
|
205
|
+
<p style="margin:0 0 8px;font-weight:600">Copy this and paste it back to the assistant:</p>
|
|
206
|
+
<textarea id="payload" readonly rows="8"
|
|
207
|
+
style="width:100%;font-family:ui-monospace,monospace;font-size:12px;
|
|
208
|
+
border-radius:10px;padding:10px"></textarea>
|
|
209
|
+
<div style="margin-top:8px"><button type="button" id="copybtn"
|
|
210
|
+
style="font:inherit;font-weight:600;padding:8px 16px;border-radius:10px;
|
|
211
|
+
border:0;background:AccentColor;color:AccentColorText;cursor:pointer">Copy answers</button></div>
|
|
212
|
+
</div>
|
|
213
|
+
</form>
|
|
214
|
+
</div>
|
|
215
|
+
<div class="bar">
|
|
216
|
+
<button type="button" id="submit">Submit answers</button>
|
|
217
|
+
<button type="button" id="decline" class="ghost">Decline all</button>
|
|
218
|
+
</div>
|
|
219
|
+
<script>
|
|
220
|
+
const ASKS = ${asksJson};
|
|
221
|
+
const TOKEN = ${tokenJson};
|
|
222
|
+
const PANEL_TIER = ${tierJson};
|
|
223
|
+
const SUBMIT_URL = ${submitUrlJson};
|
|
224
|
+
const LAYOUT = ${layoutJson};
|
|
225
|
+
const THEME = ${themeJson};
|
|
226
|
+
const ACCENT = ${accentJson};
|
|
227
|
+
|
|
228
|
+
// Defence-in-depth: a CSS color sink guard. The schema already constrains
|
|
229
|
+
// SelectOption.color / ask_color.palette / AskSet.meta.accent to this exact
|
|
230
|
+
// grammar, but renderers MUST also ignore non-conforming values (SPEC §7.x)
|
|
231
|
+
// so an unvalidated path can never reach a CSS url()/var()/image-set()
|
|
232
|
+
// value-injection. The pattern is the ONE canonical safe-color grammar
|
|
233
|
+
// (schema + sink guard + conformance + SPEC §7.y), injected as a JSON
|
|
234
|
+
// string (layer-proof escaping) and reconstructed here: #hex(3/4/6/8), CSS
|
|
235
|
+
// named/keyword colors, common functional notations with a safe inner
|
|
236
|
+
// charset — structurally cannot express url(/var(/image-set(/element(/
|
|
237
|
+
// gradients/quotes/;/nested (. Trimmed; empty/invalid ⇒ null ("no color",
|
|
238
|
+
// omit), never throws.
|
|
239
|
+
const SAFE_COLOR = new RegExp(${safeColorPatternJson});
|
|
240
|
+
function safeColor(v) {
|
|
241
|
+
if (typeof v !== "string") return null;
|
|
242
|
+
const t = v.trim();
|
|
243
|
+
return SAFE_COLOR.test(t) ? t : null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (THEME && THEME !== "system")
|
|
247
|
+
document.documentElement.setAttribute("data-theme", THEME);
|
|
248
|
+
const ACCENT_SAFE = safeColor(ACCENT);
|
|
249
|
+
if (ACCENT_SAFE)
|
|
250
|
+
document.documentElement.style.setProperty("--accent", ACCENT_SAFE);
|
|
251
|
+
const f = document.getElementById("f");
|
|
252
|
+
const copyout = document.getElementById("copyout");
|
|
253
|
+
const state = new Map();
|
|
254
|
+
|
|
255
|
+
// Curated, STATIC inline-SVG icons. Keyed by name; values are our own
|
|
256
|
+
// constants (safe for innerHTML). Anything not in here is treated as text.
|
|
257
|
+
const P = "stroke='currentColor' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round'";
|
|
258
|
+
const SVG = (b) => "<svg viewBox='0 0 24 24' " + P + ">" + b + "</svg>";
|
|
259
|
+
const ICONS = {
|
|
260
|
+
check: SVG("<path d='M20 6 9 17l-5-5'/>"),
|
|
261
|
+
x: SVG("<path d='M18 6 6 18M6 6l12 12'/>"),
|
|
262
|
+
warning: SVG("<path d='M12 9v4m0 4h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z'/>"),
|
|
263
|
+
info: SVG("<circle cx='12' cy='12' r='9'/><path d='M12 16v-4m0-4h.01'/>"),
|
|
264
|
+
rocket: SVG("<path d='M5 13c-1.5 1.5-2 5-2 5s3.5-.5 5-2m9-11a8 8 0 0 1-9 9l-3-3a8 8 0 0 1 9-9 6 6 0 0 1 3 3Z'/><circle cx='14.5' cy='9.5' r='1.5'/>"),
|
|
265
|
+
star: SVG("<path d='m12 3 2.7 5.5 6 .9-4.3 4.2 1 6L12 17l-5.4 2.6 1-6L3.3 9.4l6-.9Z'/>"),
|
|
266
|
+
bolt: SVG("<path d='M13 2 4 14h6l-1 8 9-12h-6l1-8Z'/>"),
|
|
267
|
+
clock: SVG("<circle cx='12' cy='12' r='9'/><path d='M12 7v5l3 2'/>"),
|
|
268
|
+
shield: SVG("<path d='M12 3 5 6v6c0 4 3 7 7 9 4-2 7-5 7-9V6l-7-3Z'/>"),
|
|
269
|
+
heart: SVG("<path d='M12 20s-7-4.3-9.3-8.2C1 9 2.5 5.5 6 5.5c2 0 3.2 1.2 6 4 2.8-2.8 4-4 6-4 3.5 0 5 3.5 3.3 6.3C19 15.7 12 20 12 20Z'/>"),
|
|
270
|
+
flag: SVG("<path d='M5 21V4m0 0 9 2-2 5 7 1-2 5-12-2'/>"),
|
|
271
|
+
leaf: SVG("<path d='M11 20A7 7 0 0 1 4 13C4 6 13 4 20 4c0 7-2 16-9 16Zm0 0c0-5 3-9 6-11'/>"),
|
|
272
|
+
sparkles: SVG("<path d='M12 3v6m0 6v6m-9-9h6m6 0h6M6 6l3 3m6 6 3 3m0-12-3 3M9 15l-3 3'/>"),
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
function icon(name) {
|
|
276
|
+
const s = document.createElement("span");
|
|
277
|
+
s.className = "ic";
|
|
278
|
+
if (typeof name === "string" && Object.prototype.hasOwnProperty.call(ICONS, name)) {
|
|
279
|
+
s.innerHTML = ICONS[name]; // our constant; never ask content
|
|
280
|
+
} else if (name) {
|
|
281
|
+
s.textContent = name; // emoji / arbitrary char — inert
|
|
282
|
+
}
|
|
283
|
+
return s;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function el(tag, props, kids) {
|
|
287
|
+
const n = document.createElement(tag);
|
|
288
|
+
if (props) for (const k in props) {
|
|
289
|
+
if (k === "text") n.textContent = props[k];
|
|
290
|
+
else if (k === "html") { /* never used with ask content */ n.innerHTML = props[k]; }
|
|
291
|
+
else n.setAttribute(k, props[k]);
|
|
292
|
+
}
|
|
293
|
+
for (const c of kids || []) n.append(c);
|
|
294
|
+
return n;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function spanFull(ask) {
|
|
298
|
+
const m = ask.meta && ask.meta.span;
|
|
299
|
+
if (m === "full") return true;
|
|
300
|
+
if (m === "half") return false;
|
|
301
|
+
if (LAYOUT === "single") return true;
|
|
302
|
+
if (LAYOUT === "grid") { /* fall through to heuristic */ }
|
|
303
|
+
switch (ask.type) {
|
|
304
|
+
case "ask_code_diff": case "ask_rank": return true;
|
|
305
|
+
case "ask_text": return !!(ask.spec && ask.spec.multiline);
|
|
306
|
+
case "ask_select": {
|
|
307
|
+
const n = (ask.spec && ask.spec.options || []).length;
|
|
308
|
+
const d = ask.spec && ask.spec.display;
|
|
309
|
+
return d === "cards" || d === "grid" || !!(ask.spec && ask.spec.multiple) || n > 4;
|
|
310
|
+
}
|
|
311
|
+
case "ask_color": return (ask.spec && (ask.spec.palette || []).length || 0) > 6;
|
|
312
|
+
default: return false; // text(single), confirm, number, rating, slider, date
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
document.getElementById("count").textContent =
|
|
317
|
+
ASKS.length + (ASKS.length === 1 ? " question" : " questions");
|
|
318
|
+
|
|
319
|
+
for (const ask of ASKS) {
|
|
320
|
+
const box = el("div", { class: "ask" + (spanFull(ask) ? " full" : "") });
|
|
321
|
+
const prompt = el("div", { class: "prompt", text: ask.prompt });
|
|
322
|
+
if (ask.required !== false) prompt.append(el("span", { class: "req", text: "*" }));
|
|
323
|
+
box.append(prompt);
|
|
324
|
+
if (ask.help) box.append(el("div", { class: "help", text: ask.help }));
|
|
325
|
+
const field = el("div", { class: "field" });
|
|
326
|
+
box.append(field);
|
|
327
|
+
|
|
328
|
+
if (ask.type === "ask_text") {
|
|
329
|
+
const big = ask.spec && ask.spec.multiline;
|
|
330
|
+
const inp = el(big ? "textarea" : "input", big ? { rows: "4" } : { type: "text" });
|
|
331
|
+
if (ask.spec && ask.spec.placeholder) inp.setAttribute("placeholder", ask.spec.placeholder);
|
|
332
|
+
field.append(inp);
|
|
333
|
+
state.set(ask.id, () => ({ status: inp.value ? "answered" : "declined", value: inp.value }));
|
|
334
|
+
|
|
335
|
+
} else if (ask.type === "ask_confirm") {
|
|
336
|
+
const aff = (ask.spec && ask.spec.affirm) || "Yes";
|
|
337
|
+
const den = (ask.spec && ask.spec.deny) || "No";
|
|
338
|
+
if (ask.spec && ask.spec.consequence)
|
|
339
|
+
box.insertBefore(el("div", { class: "help", text: ask.spec.consequence }),
|
|
340
|
+
field);
|
|
341
|
+
let v = null;
|
|
342
|
+
// consequence => big choice cards; otherwise a compact segmented control
|
|
343
|
+
if (ask.spec && ask.spec.consequence) {
|
|
344
|
+
const opts = el("div", { class: "opts cards" });
|
|
345
|
+
const mk = (label, val, ik) => {
|
|
346
|
+
const inp = el("input", { type: "radio", name: ask.id });
|
|
347
|
+
inp.onchange = () => { v = val; };
|
|
348
|
+
const lab = el("label", { class: "opt" }, [
|
|
349
|
+
inp, icon(ik), el("span", { class: "lbl", text: label }),
|
|
350
|
+
]);
|
|
351
|
+
return lab;
|
|
352
|
+
};
|
|
353
|
+
opts.append(mk(aff, true, "check"), mk(den, false, "x"));
|
|
354
|
+
field.append(opts);
|
|
355
|
+
} else {
|
|
356
|
+
const seg = el("div", { class: "seg" });
|
|
357
|
+
const yes = el("button", { type: "button", text: aff });
|
|
358
|
+
const no = el("button", { type: "button", text: den });
|
|
359
|
+
yes.onclick = () => { v = true; yes.setAttribute("aria-pressed", "true"); no.setAttribute("aria-pressed", "false"); };
|
|
360
|
+
no.onclick = () => { v = false; yes.setAttribute("aria-pressed", "false"); no.setAttribute("aria-pressed", "true"); };
|
|
361
|
+
seg.append(yes, no);
|
|
362
|
+
field.append(seg);
|
|
363
|
+
}
|
|
364
|
+
state.set(ask.id, () => ({ status: v === null ? "declined" : "answered", value: v }));
|
|
365
|
+
|
|
366
|
+
} else if (ask.type === "ask_select") {
|
|
367
|
+
const multiple = !!(ask.spec && ask.spec.multiple);
|
|
368
|
+
const display = (ask.spec && ask.spec.display) || "list";
|
|
369
|
+
const cls = display === "cards" ? "opts cards"
|
|
370
|
+
: display === "grid" ? "opts grid2"
|
|
371
|
+
: display === "segmented" ? "opts seg" : "opts";
|
|
372
|
+
const wrap = el("div", { class: cls, role: multiple ? "group" : "radiogroup" });
|
|
373
|
+
const picks = new Set();
|
|
374
|
+
for (const o of ask.spec.options) {
|
|
375
|
+
const input = el("input", { type: multiple ? "checkbox" : "radio", name: ask.id, value: o.id });
|
|
376
|
+
input.onchange = () => {
|
|
377
|
+
if (multiple) { input.checked ? picks.add(o.id) : picks.delete(o.id); }
|
|
378
|
+
else { picks.clear(); picks.add(o.id); }
|
|
379
|
+
};
|
|
380
|
+
const body = el("div", {}, [el("span", { class: "lbl", text: o.label })]);
|
|
381
|
+
if (o.description && display !== "segmented")
|
|
382
|
+
body.append(el("small", { text: o.description }));
|
|
383
|
+
const kids = [input];
|
|
384
|
+
const oColor = safeColor(o.color);
|
|
385
|
+
if (oColor) {
|
|
386
|
+
const sw = el("span", { class: "swatch" }); sw.style.background = oColor;
|
|
387
|
+
kids.push(sw);
|
|
388
|
+
} else if (o.icon) kids.push(icon(o.icon));
|
|
389
|
+
kids.push(body);
|
|
390
|
+
wrap.append(el("label", { class: "opt" }, kids));
|
|
391
|
+
}
|
|
392
|
+
field.append(wrap);
|
|
393
|
+
state.set(ask.id, () => {
|
|
394
|
+
const arr = [...picks];
|
|
395
|
+
return { status: arr.length ? "answered" : "declined",
|
|
396
|
+
value: multiple ? arr : arr[0] };
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
} else if (ask.type === "ask_code_diff") {
|
|
400
|
+
const decisions = new Map();
|
|
401
|
+
for (const file of ask.spec.files) {
|
|
402
|
+
field.append(el("div", { class: "file", text: file.path }));
|
|
403
|
+
for (const h of file.hunks) {
|
|
404
|
+
decisions.set(h.id, true);
|
|
405
|
+
const acc = el("button", { type: "button", "aria-pressed": "true", text: "Accept" });
|
|
406
|
+
const rej = el("button", { type: "button", "aria-pressed": "false", text: "Reject" });
|
|
407
|
+
acc.onclick = () => { decisions.set(h.id, true);
|
|
408
|
+
acc.setAttribute("aria-pressed", "true"); rej.setAttribute("aria-pressed", "false"); };
|
|
409
|
+
rej.onclick = () => { decisions.set(h.id, false);
|
|
410
|
+
acc.setAttribute("aria-pressed", "false"); rej.setAttribute("aria-pressed", "true"); };
|
|
411
|
+
const hunk = el("div", { class: "hunk" }, [
|
|
412
|
+
el("div", { class: "hunk-hdr" }, [
|
|
413
|
+
el("span", { text: h.header || h.id }),
|
|
414
|
+
el("span", { class: "seg" }, [acc, rej]),
|
|
415
|
+
]),
|
|
416
|
+
]);
|
|
417
|
+
if (h.before) hunk.append(el("pre", { class: "before", text: h.before }));
|
|
418
|
+
hunk.append(el("pre", { class: "after", text: h.after }));
|
|
419
|
+
field.append(hunk);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
state.set(ask.id, () => {
|
|
423
|
+
const accepted = [], rejected = [];
|
|
424
|
+
for (const [hid, ok] of decisions) (ok ? accepted : rejected).push(hid);
|
|
425
|
+
return { status: "answered", value: { accepted, rejected } };
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
} else if (ask.type === "ask_number") {
|
|
429
|
+
const s = ask.spec || {};
|
|
430
|
+
const inp = el("input", { type: "number" });
|
|
431
|
+
if (s.min != null) inp.setAttribute("min", s.min);
|
|
432
|
+
if (s.max != null) inp.setAttribute("max", s.max);
|
|
433
|
+
if (s.step != null) inp.setAttribute("step", s.step);
|
|
434
|
+
else if (s.integer) inp.setAttribute("step", "1");
|
|
435
|
+
if (s.unit) inp.setAttribute("placeholder", s.unit);
|
|
436
|
+
field.append(inp);
|
|
437
|
+
state.set(ask.id, () =>
|
|
438
|
+
inp.value === "" ? { status: "declined" }
|
|
439
|
+
: { status: "answered", value: Number(inp.value) });
|
|
440
|
+
|
|
441
|
+
} else if (ask.type === "ask_slider") {
|
|
442
|
+
const s = ask.spec;
|
|
443
|
+
const inp = el("input", { type: "range", min: s.min, max: s.max,
|
|
444
|
+
step: s.step != null ? s.step : "1", value: String(Math.round((s.min + s.max) / 2)) });
|
|
445
|
+
const out = el("output", { text: inp.value + (s.unit ? " " + s.unit : "") });
|
|
446
|
+
inp.oninput = () => { out.textContent = inp.value + (s.unit ? " " + s.unit : ""); };
|
|
447
|
+
field.append(el("div", { class: "slider" }, [inp, out]));
|
|
448
|
+
state.set(ask.id, () => ({ status: "answered", value: Number(inp.value) }));
|
|
449
|
+
|
|
450
|
+
} else if (ask.type === "ask_rating") {
|
|
451
|
+
const max = (ask.spec && ask.spec.max) || 5;
|
|
452
|
+
const numeric = ask.spec && ask.spec.icon === "number";
|
|
453
|
+
let val = 0;
|
|
454
|
+
const wrap = el("div", { class: "stars" });
|
|
455
|
+
const btns = [];
|
|
456
|
+
for (let i = 1; i <= max; i++) {
|
|
457
|
+
const b = el("button", { type: "button", "aria-label": "rate " + i,
|
|
458
|
+
text: numeric ? String(i) : "★" });
|
|
459
|
+
b.onclick = () => { val = i;
|
|
460
|
+
btns.forEach((x, j) => x.classList.toggle("on", j < i)); };
|
|
461
|
+
btns.push(b); wrap.append(b);
|
|
462
|
+
}
|
|
463
|
+
if (ask.spec && ask.spec.labels)
|
|
464
|
+
field.append(el("div", { class: "help",
|
|
465
|
+
text: (ask.spec.labels.min || "") + " → " + (ask.spec.labels.max || "") }));
|
|
466
|
+
field.append(wrap);
|
|
467
|
+
state.set(ask.id, () =>
|
|
468
|
+
val ? { status: "answered", value: val } : { status: "declined" });
|
|
469
|
+
|
|
470
|
+
} else if (ask.type === "ask_date") {
|
|
471
|
+
const inp = el("input", { type: ask.spec && ask.spec.time ? "datetime-local" : "date" });
|
|
472
|
+
if (ask.spec && ask.spec.min) inp.setAttribute("min", ask.spec.min);
|
|
473
|
+
if (ask.spec && ask.spec.max) inp.setAttribute("max", ask.spec.max);
|
|
474
|
+
field.append(inp);
|
|
475
|
+
state.set(ask.id, () =>
|
|
476
|
+
inp.value ? { status: "answered", value: inp.value } : { status: "declined" });
|
|
477
|
+
|
|
478
|
+
} else if (ask.type === "ask_rank") {
|
|
479
|
+
const order = ask.spec.items.map((it) => it.id);
|
|
480
|
+
const listEl = el("div", {});
|
|
481
|
+
const draw = () => {
|
|
482
|
+
listEl.textContent = "";
|
|
483
|
+
order.forEach((id, i) => {
|
|
484
|
+
const it = ask.spec.items.find((x) => x.id === id);
|
|
485
|
+
const up = el("button", { type: "button", text: "↑", "aria-label": "up" });
|
|
486
|
+
const dn = el("button", { type: "button", text: "↓", "aria-label": "down" });
|
|
487
|
+
up.disabled = i === 0; dn.disabled = i === order.length - 1;
|
|
488
|
+
up.onclick = () => { order.splice(i - 1, 0, order.splice(i, 1)[0]); draw(); };
|
|
489
|
+
dn.onclick = () => { order.splice(i + 1, 0, order.splice(i, 1)[0]); draw(); };
|
|
490
|
+
const body = el("div", {}, [el("span", { class: "lbl", text: (i + 1) + ". " + it.label })]);
|
|
491
|
+
if (it.description) body.append(el("small", { text: it.description }));
|
|
492
|
+
listEl.append(el("div", { class: "rrow" }, [body, el("span", { class: "seg" }, [up, dn])]));
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
draw();
|
|
496
|
+
field.append(listEl);
|
|
497
|
+
state.set(ask.id, () => ({ status: "answered", value: [...order] }));
|
|
498
|
+
|
|
499
|
+
} else if (ask.type === "ask_color") {
|
|
500
|
+
const s = ask.spec || {};
|
|
501
|
+
const palette = s.palette || [];
|
|
502
|
+
const allowCustom = palette.length === 0 || s.allowCustom === true;
|
|
503
|
+
let val = null;
|
|
504
|
+
const sw = el("div", { class: "swatches" });
|
|
505
|
+
const chips = [];
|
|
506
|
+
palette.forEach((rawC) => {
|
|
507
|
+
const c = safeColor(rawC);
|
|
508
|
+
if (!c) return; // non-conforming palette entry: skip the chip entirely
|
|
509
|
+
const b = el("button", { type: "button", "aria-label": c, title: c });
|
|
510
|
+
b.className = "chip"; b.style.background = c;
|
|
511
|
+
b.onclick = () => { val = c;
|
|
512
|
+
chips.forEach((x) => x.classList.remove("on")); b.classList.add("on");
|
|
513
|
+
if (custom) custom.value = /^#[0-9a-fA-F]{6}$/.test(c) ? c : custom.value; };
|
|
514
|
+
chips.push(b); sw.append(b);
|
|
515
|
+
});
|
|
516
|
+
field.append(sw);
|
|
517
|
+
let custom = null;
|
|
518
|
+
if (allowCustom) {
|
|
519
|
+
custom = el("input", { type: "color", value: palette[0] && /^#[0-9a-fA-F]{6}$/.test(palette[0]) ? palette[0] : "#888888" });
|
|
520
|
+
custom.style.cssText = "margin-top:10px;width:56px;height:34px;padding:2px;border-radius:8px;border:1px solid var(--line);background:Field;cursor:pointer";
|
|
521
|
+
custom.oninput = () => { val = custom.value;
|
|
522
|
+
chips.forEach((x) => x.classList.remove("on")); };
|
|
523
|
+
field.append(el("div", {}, [custom]));
|
|
524
|
+
}
|
|
525
|
+
state.set(ask.id, () =>
|
|
526
|
+
val ? { status: "answered", value: val } : { status: "declined" });
|
|
527
|
+
|
|
528
|
+
} else {
|
|
529
|
+
const inp = el("textarea", { rows: "3" });
|
|
530
|
+
field.append(inp);
|
|
531
|
+
state.set(ask.id, () => ({ status: inp.value ? "answered" : "declined", value: inp.value }));
|
|
532
|
+
}
|
|
533
|
+
f.insertBefore(box, copyout);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function collect(forceDecline) {
|
|
537
|
+
return ASKS.map((ask) => {
|
|
538
|
+
const r = forceDecline ? { status: "declined" } : state.get(ask.id)();
|
|
539
|
+
return { id: ask.id, type: ask.type, status: r.status,
|
|
540
|
+
value: r.status === "answered" ? r.value : undefined,
|
|
541
|
+
meta: { tier: PANEL_TIER, specVersion: ask.meta?.specVersion } };
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function submit(answers) {
|
|
546
|
+
const params = { token: TOKEN, answers };
|
|
547
|
+
// Automatic path — ONLY for the apps tier, where the panel is rawHtml
|
|
548
|
+
// embedded by a trusting MCP-Apps/mcp-ui host (its documented contract).
|
|
549
|
+
// The token + answers must never be broadcast with targetOrigin "*" from
|
|
550
|
+
// the url tier: that panel ships as a null-origin data: URL and the
|
|
551
|
+
// hosted /r/ route is framable, so "*" would leak the one-time token and
|
|
552
|
+
// the user's answers to any embedding page. url uses SUBMIT_URL / copy.
|
|
553
|
+
if (PANEL_TIER === "apps") {
|
|
554
|
+
try {
|
|
555
|
+
window.parent.postMessage(
|
|
556
|
+
{ type: "tool", payload: { toolName: "elicit_submit", params } },
|
|
557
|
+
"*",
|
|
558
|
+
);
|
|
559
|
+
} catch (e) { /* no controlling parent — fall through */ }
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Hosted path: a real POST back to the server (the hosted url tier).
|
|
563
|
+
if (SUBMIT_URL) {
|
|
564
|
+
fetch(SUBMIT_URL, {
|
|
565
|
+
method: "POST",
|
|
566
|
+
headers: { "content-type": "application/json" },
|
|
567
|
+
body: JSON.stringify(params),
|
|
568
|
+
}).then((res) => {
|
|
569
|
+
const bar = document.querySelector(".bar");
|
|
570
|
+
bar.innerHTML = res.ok
|
|
571
|
+
? "<p style='font-weight:600'>Submitted — you can close this.</p>"
|
|
572
|
+
: "<p style='font-weight:600'>Server rejected the answers — see assistant.</p>";
|
|
573
|
+
}).catch(() => showCopy(params));
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Always-works fallback (the defining 'url' channel): a copy-pasteable
|
|
578
|
+
// payload the user hands back to the assistant for elicit_submit.
|
|
579
|
+
if (PANEL_TIER === "url") showCopy(params);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function showCopy(params) {
|
|
583
|
+
const ta = document.getElementById("payload");
|
|
584
|
+
ta.value = JSON.stringify(params, null, 2);
|
|
585
|
+
copyout.hidden = false;
|
|
586
|
+
ta.focus(); ta.select();
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const copyBtn = document.getElementById("copybtn");
|
|
590
|
+
copyBtn.onclick = () => {
|
|
591
|
+
const ta = document.getElementById("payload");
|
|
592
|
+
ta.select();
|
|
593
|
+
(navigator.clipboard?.writeText(ta.value) ?? Promise.reject()).catch(() =>
|
|
594
|
+
document.execCommand("copy"));
|
|
595
|
+
copyBtn.textContent = "Copied";
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
document.getElementById("submit").onclick = () => submit(collect(false));
|
|
599
|
+
document.getElementById("decline").onclick = () => submit(collect(true));
|
|
600
|
+
// Lifecycle handshake is an apps-tier (mcp-ui host) concern only; carries
|
|
601
|
+
// no token, but keep the "*" broadcast scoped to the trusted-embed tier.
|
|
602
|
+
if (PANEL_TIER === "apps")
|
|
603
|
+
window.parent.postMessage({ type: "ui-lifecycle-iframe-ready" }, "*");
|
|
604
|
+
</script>
|
|
605
|
+
</body>
|
|
606
|
+
</html>`;
|
|
607
|
+
}
|
|
608
|
+
//# sourceMappingURL=panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"panel.js","sourceRoot":"","sources":["../src/panel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAC7B,kHAAkH,CAAC;AACrH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,MAAM,CAAC,kBAAkB,CAAC,CAAC;AAuB5D,MAAM,UAAU,cAAc,CAC5B,GAAW,EACX,KAAa,EACb,IAAU,EACV,OAAqB,EAAE;IAEvB,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,aAAa,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC;IAC5D,MAAM,oBAAoB,GAAG,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAE/D,2EAA2E;IAC3E,wEAAwE;IACxE,4EAA4E;IAC5E,4EAA4E;IAC5E,yEAAyE;IACzE,0EAA0E;IAC1E,2EAA2E;IAC3E,0EAA0E;IAC1E,oEAAoE;IACpE,IAAI,UAAU,GAAG,QAAQ,CAAC;IAC1B,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,UAAU,GAAG,QAAQ,CAAC;QACxB,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GACX,2DAA2D;QAC3D,wEAAwE;QACxE,eAAe,UAAU,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACpD,MAAM,OAAO,GAAI,GAAuE,CAAC,IAAI,IAAI,EAAE,CAAC;IACpG,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC;IAEzD,OAAO;;;;sDAI6C,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAyKlD,QAAQ;gBACP,SAAS;qBACJ,QAAQ;qBACR,aAAa;iBACjB,UAAU;gBACX,SAAS;iBACR,UAAU;;;;;;;;;;;;;gCAaK,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA+W5C,CAAC;AACT,CAAC"}
|
package/dist/tui.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AskSet } from "@elicitkit/core";
|
|
2
|
+
import type { RenderedPanel } from "./contract.js";
|
|
3
|
+
/**
|
|
4
|
+
* `tui` tier — the universal sink the spec guarantees for every type
|
|
5
|
+
* (SPEC §4/§7). When no UI surface exists the agent reads this back and
|
|
6
|
+
* collects answers in conversation, then calls `elicit_submit`. Never
|
|
7
|
+
* hard-fails; this is why every type must define a text degradation.
|
|
8
|
+
*/
|
|
9
|
+
export declare function renderTui(set: AskSet, token: string): RenderedPanel;
|
|
10
|
+
//# sourceMappingURL=tui.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAkBnE"}
|