@womp/mcp-ui 0.1.9 → 0.1.10
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 +28 -0
- package/dist/index.js +7 -7
- package/dist/print/index.html +7 -7
- package/package.json +1 -1
- package/src/print/main.ts +47 -1
- package/src/shared/bridge.ts +23 -0
package/package.json
CHANGED
package/src/print/main.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $, callTool, createCard, openLink, type GenerationResult } from "../shared/bridge";
|
|
1
|
+
import { $, callTool, createCard, openLink, pushModelContext, type GenerationResult } from "../shared/bridge";
|
|
2
2
|
import { expandHexInput, nearestPantone, normalizeHex, pantoneChips } from "./pantone";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -96,6 +96,8 @@ const MAX_LONGEST_CM = 60;
|
|
|
96
96
|
const PANEL_DEFAULT_TINT = "#276ee2";
|
|
97
97
|
/** The panel's own re-price debounce. */
|
|
98
98
|
const REQUOTE_DEBOUNCE_MS = 500;
|
|
99
|
+
/** State push: faster than a re-quote (it prices nothing) but still one per gesture, not per keystroke. */
|
|
100
|
+
const SYNC_DEBOUNCE_MS = 300;
|
|
99
101
|
const AXES: Axis[] = ["width", "height", "depth"];
|
|
100
102
|
|
|
101
103
|
const state = {
|
|
@@ -129,6 +131,7 @@ const state = {
|
|
|
129
131
|
/** Monotonic guard: a slow older quote must never overwrite a newer one. */
|
|
130
132
|
let quoteSeq = 0;
|
|
131
133
|
let debounceTimer: number | undefined;
|
|
134
|
+
let syncTimer: number | undefined;
|
|
132
135
|
|
|
133
136
|
// --- helpers ---------------------------------------------------------------
|
|
134
137
|
|
|
@@ -169,6 +172,7 @@ function setQuoting(on: boolean) {
|
|
|
169
172
|
function markDirty() {
|
|
170
173
|
state.cartCheckoutUrl = "";
|
|
171
174
|
state.preparedUrl = "";
|
|
175
|
+
scheduleSync();
|
|
172
176
|
}
|
|
173
177
|
|
|
174
178
|
/**
|
|
@@ -668,6 +672,48 @@ function scheduleRequote() {
|
|
|
668
672
|
debounceTimer = window.setTimeout(() => void requote(), REQUOTE_DEBOUNCE_MS);
|
|
669
673
|
}
|
|
670
674
|
|
|
675
|
+
/** "Tinted clear (resin-clear-tinted) · matte · #276ee2 (Pantone 2386 C) · 10.2 cm · x2" */
|
|
676
|
+
function describeSelection(): string {
|
|
677
|
+
const m = selectedMaterial();
|
|
678
|
+
const snapped = snappedColor();
|
|
679
|
+
const bits = [`${materialLabel(m)} (${state.material})`];
|
|
680
|
+
if (state.finish && m?.finishes?.length) bits.push(state.finish);
|
|
681
|
+
if (snapped) bits.push(`${snapped.hex} (Pantone ${snapped.code})`);
|
|
682
|
+
if (state.longestSideCm) bits.push(`${fmtDim(state.longestSideCm, state.unit)} ${state.unit} longest side`);
|
|
683
|
+
bits.push(`x${state.quantity}`);
|
|
684
|
+
return bits.join(" · ");
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Publish what the card is showing — the whole point of the card being live.
|
|
689
|
+
*
|
|
690
|
+
* Its re-quotes are app-initiated calls whose results come back here and go
|
|
691
|
+
* nowhere else, so a configuration tuned for two minutes is invisible to the
|
|
692
|
+
* conversation: ask for it in chat and the model orders the settings it opened
|
|
693
|
+
* with. Two channels, deliberately: `sync_print_config` parks the selection on
|
|
694
|
+
* the server, which is what `add_to_cart` actually reads, and
|
|
695
|
+
* `updateModelContext` tells the model so it can talk about the right print
|
|
696
|
+
* (host-gated, and a no-op where it is unsupported).
|
|
697
|
+
*
|
|
698
|
+
* Failures are swallowed. A state push that does not land costs an out-of-date
|
|
699
|
+
* order note, not a broken card, and the user has the button right there.
|
|
700
|
+
*/
|
|
701
|
+
function syncConfig() {
|
|
702
|
+
if (!state.source || state.staticPrintUrl || !state.material) return;
|
|
703
|
+
void callTool(card.app, "sync_print_config", baseArgs()).catch(() => {});
|
|
704
|
+
void pushModelContext(
|
|
705
|
+
card.app,
|
|
706
|
+
`The 3D print card in this conversation now shows: ${describeSelection()}. ` +
|
|
707
|
+
"These are the user's own selections on the card, later than anything said in chat. " +
|
|
708
|
+
"If they ask to order or add it to the cart, call add_to_cart with only the mesh source.",
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function scheduleSync() {
|
|
713
|
+
if (syncTimer !== undefined) clearTimeout(syncTimer);
|
|
714
|
+
syncTimer = window.setTimeout(syncConfig, SYNC_DEBOUNCE_MS);
|
|
715
|
+
}
|
|
716
|
+
|
|
671
717
|
async function addToCart() {
|
|
672
718
|
const cta = $("cta") as HTMLButtonElement;
|
|
673
719
|
cta.disabled = true;
|
package/src/shared/bridge.ts
CHANGED
|
@@ -202,6 +202,29 @@ export async function callTool(app: App, name: string, args: Record<string, unkn
|
|
|
202
202
|
return app.request({ method: "tools/call", params: { name, arguments: args } }, CallToolResultSchema);
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Push what the card is showing into the model's context for the next turn.
|
|
207
|
+
*
|
|
208
|
+
* A card's own tool calls are announced to nobody: the host proxies them and
|
|
209
|
+
* hands the result back to the iframe, so nothing the user changes here reaches
|
|
210
|
+
* the conversation. `ui/update-model-context` is the channel for that — the
|
|
211
|
+
* host holds the last update and gives it to the model on the next user
|
|
212
|
+
* message. It is host-gated, and Claude web was reported to drop the payload
|
|
213
|
+
* silently, so this is a bonus that makes the model DESCRIBE the right print,
|
|
214
|
+
* never the thing correctness rests on: the server holds the configuration too.
|
|
215
|
+
*
|
|
216
|
+
* No-ops (rather than throws) when the host does not advertise the capability.
|
|
217
|
+
*/
|
|
218
|
+
export async function pushModelContext(app: App, text: string) {
|
|
219
|
+
const supported = (app.getHostCapabilities() as { updateModelContext?: unknown } | undefined)?.updateModelContext;
|
|
220
|
+
if (!supported) return;
|
|
221
|
+
try {
|
|
222
|
+
await app.updateModelContext({ content: [{ type: "text", text }] });
|
|
223
|
+
} catch {
|
|
224
|
+
// A host that rejects the update is not a card the user can do nothing with.
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
205
228
|
/** Toggle fullscreen, trusting the host's answer over our request. */
|
|
206
229
|
export function toggleFullscreen(app: App) {
|
|
207
230
|
const isFull = document.documentElement.dataset.displayMode === "fullscreen";
|