sbb-mcp 0.4.3 → 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/LICENSE +50 -57
- package/README.md +25 -214
- package/dist/index.js +47 -19
- package/package.json +10 -33
- package/dist/auth.d.ts +0 -2
- package/dist/auth.js +0 -44
- package/dist/auth.js.map +0 -1
- package/dist/cache.d.ts +0 -14
- package/dist/cache.js +0 -62
- package/dist/cache.js.map +0 -1
- package/dist/client.d.ts +0 -17
- package/dist/client.js +0 -70
- package/dist/client.js.map +0 -1
- package/dist/formatters.d.ts +0 -35
- package/dist/formatters.js +0 -285
- package/dist/formatters.js.map +0 -1
- package/dist/http.d.ts +0 -2
- package/dist/http.js +0 -117
- package/dist/http.js.map +0 -1
- package/dist/i18n.d.ts +0 -22
- package/dist/i18n.js +0 -36
- package/dist/i18n.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js.map +0 -1
- package/dist/journey.d.ts +0 -5
- package/dist/journey.js +0 -67
- package/dist/journey.js.map +0 -1
- package/dist/look2book.d.ts +0 -98
- package/dist/look2book.js +0 -212
- package/dist/look2book.js.map +0 -1
- package/dist/prices.d.ts +0 -3
- package/dist/prices.js +0 -51
- package/dist/prices.js.map +0 -1
- package/dist/profile.d.ts +0 -16
- package/dist/profile.js +0 -84
- package/dist/profile.js.map +0 -1
- package/dist/rate-limit.d.ts +0 -5
- package/dist/rate-limit.js +0 -44
- package/dist/rate-limit.js.map +0 -1
- package/dist/shortlink.d.ts +0 -60
- package/dist/shortlink.js +0 -122
- package/dist/shortlink.js.map +0 -1
- package/dist/structured.d.ts +0 -125
- package/dist/structured.js +0 -134
- package/dist/structured.js.map +0 -1
- package/dist/swisstrip.d.ts +0 -41
- package/dist/swisstrip.js +0 -135
- package/dist/swisstrip.js.map +0 -1
- package/dist/tools.d.ts +0 -40
- package/dist/tools.js +0 -509
- package/dist/tools.js.map +0 -1
- package/dist/transport/index.d.ts +0 -10
- package/dist/transport/index.js +0 -13
- package/dist/transport/index.js.map +0 -1
- package/dist/transport/setup.d.ts +0 -1
- package/dist/transport/setup.js +0 -59
- package/dist/transport/setup.js.map +0 -1
- package/dist/transport/smapi-auth.d.ts +0 -14
- package/dist/transport/smapi-auth.js +0 -89
- package/dist/transport/smapi-auth.js.map +0 -1
- package/dist/transport/smapi-client.d.ts +0 -46
- package/dist/transport/smapi-client.js +0 -186
- package/dist/transport/smapi-client.js.map +0 -1
- package/dist/transport/smapi-journey.d.ts +0 -29
- package/dist/transport/smapi-journey.js +0 -91
- package/dist/transport/smapi-journey.js.map +0 -1
- package/dist/transport/smapi-mock.d.ts +0 -9
- package/dist/transport/smapi-mock.js +0 -151
- package/dist/transport/smapi-mock.js.map +0 -1
- package/dist/transport/smapi-prices.d.ts +0 -48
- package/dist/transport/smapi-prices.js +0 -144
- package/dist/transport/smapi-prices.js.map +0 -1
- package/dist/transport/smapi-types.d.ts +0 -181
- package/dist/transport/smapi-types.js +0 -2
- package/dist/transport/smapi-types.js.map +0 -1
- package/dist/types.d.ts +0 -139
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/widgets.d.ts +0 -60
- package/dist/widgets.js +0 -184
- package/dist/widgets.js.map +0 -1
- package/web/dist/widgets.css +0 -1
- package/web/dist/widgets.js +0 -1
package/dist/widgets.js
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Registers ChatGPT Apps SDK widget resources on an McpServer.
|
|
3
|
-
*
|
|
4
|
-
* Each tool that returns structured data links to a widget via
|
|
5
|
-
* `_meta["openai/outputTemplate"] = "ui://widget/<name>.html"`.
|
|
6
|
-
*
|
|
7
|
-
* The HTML shell for every widget embeds the same JS bundle (dist/widgets.js)
|
|
8
|
-
* produced by `packages/sbb-mcp/web`. Routing to the right widget component
|
|
9
|
-
* happens in the bundle via `data-widget` on the root element.
|
|
10
|
-
*/
|
|
11
|
-
import { readFileSync, existsSync } from 'node:fs';
|
|
12
|
-
import { dirname, resolve } from 'node:path';
|
|
13
|
-
import { fileURLToPath } from 'node:url';
|
|
14
|
-
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
15
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
/** Widgets ship under packages/sbb-mcp/web/dist, i.e. two levels above dist/*.js at runtime. */
|
|
17
|
-
const WEB_DIST = resolve(__dirname, '..', 'web', 'dist');
|
|
18
|
-
/** Registered widget IDs (mirrored in web/src/main.tsx). */
|
|
19
|
-
export const WIDGETS = {
|
|
20
|
-
STATIONS_LIST: 'stations-list',
|
|
21
|
-
CONNECTION_LIST: 'connection-list',
|
|
22
|
-
TRIP_DETAILS: 'trip-details',
|
|
23
|
-
PRICES_TABLE: 'prices-table',
|
|
24
|
-
TICKET_CARD: 'ticket-card',
|
|
25
|
-
};
|
|
26
|
-
export function widgetUri(id) {
|
|
27
|
-
return `ui://widget/${id}.html`;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* True when the connected MCP client renders our widget iframe (currently
|
|
31
|
-
* ChatGPT via the Apps SDK). Used by tool handlers to decide between a full
|
|
32
|
-
* markdown response (non-widget clients need it to display anything useful)
|
|
33
|
-
* and a terse one-line summary (widget clients already show the rich UI, and
|
|
34
|
-
* the detailed prose just gets paraphrased by the LLM into a duplicate
|
|
35
|
-
* narration next to the iframe — see https://…/issues/connections-shown-twice).
|
|
36
|
-
*
|
|
37
|
-
* Detection is based on `clientInfo.name` from the MCP `initialize` handshake.
|
|
38
|
-
* ChatGPT self-identifies as "ChatGPT" / "ChatGPT-Atlas" / similar — we match
|
|
39
|
-
* case-insensitively on "chatgpt" or "openai" so future naming tweaks from
|
|
40
|
-
* OpenAI don't silently revert us to the noisy path.
|
|
41
|
-
*
|
|
42
|
-
* Falls back to `false` (full markdown) when no clientInfo has been received
|
|
43
|
-
* yet or when the name doesn't match — the safe default for unknown clients
|
|
44
|
-
* that might not render widgets.
|
|
45
|
-
*/
|
|
46
|
-
export function isWidgetRenderingClient(server) {
|
|
47
|
-
const info = server.server.getClientVersion();
|
|
48
|
-
const name = info?.name ?? '';
|
|
49
|
-
return /chatgpt|openai/i.test(name);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Descriptor `_meta` (on the tool and resource registrations).
|
|
53
|
-
* This is what tells ChatGPT to render a UI widget for the tool.
|
|
54
|
-
*/
|
|
55
|
-
export function widgetToolMeta(id, invoking, invoked) {
|
|
56
|
-
return {
|
|
57
|
-
'openai/outputTemplate': widgetUri(id),
|
|
58
|
-
'openai/toolInvocation/invoking': invoking,
|
|
59
|
-
'openai/toolInvocation/invoked': invoked,
|
|
60
|
-
'openai/widgetAccessible': true,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Invocation `_meta` (on each CallTool response).
|
|
65
|
-
*
|
|
66
|
-
* IMPORTANT: matches OpenAI's Pizzaz reference server exactly — ONLY the
|
|
67
|
-
* invoking/invoked strings. Historically we also set `openai/outputTemplate`
|
|
68
|
-
* and `openai/widgetAccessible` here, but those belong on the tool descriptor
|
|
69
|
-
* + resource registration (see `widgetToolMeta`). Repeating them on the
|
|
70
|
-
* response appears to confuse ChatGPT's widget resolver: the iframe mounts
|
|
71
|
-
* but `window.openai.toolOutput` never populates — leaving every widget
|
|
72
|
-
* stuck on the "Loading…" fallback.
|
|
73
|
-
*
|
|
74
|
-
* Ref: https://github.com/openai/openai-apps-sdk-examples/blob/main/pizzaz_server_node/src/server.ts
|
|
75
|
-
*/
|
|
76
|
-
export function widgetResponseMeta(_id, invoking, invoked) {
|
|
77
|
-
return {
|
|
78
|
-
'openai/toolInvocation/invoking': invoking,
|
|
79
|
-
'openai/toolInvocation/invoked': invoked,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
let cachedBundles = null;
|
|
83
|
-
function loadBundles() {
|
|
84
|
-
if (cachedBundles)
|
|
85
|
-
return cachedBundles;
|
|
86
|
-
const jsPath = resolve(WEB_DIST, 'widgets.js');
|
|
87
|
-
const cssPath = resolve(WEB_DIST, 'widgets.css');
|
|
88
|
-
if (!existsSync(jsPath)) {
|
|
89
|
-
throw new Error(`Widget bundle not found at ${jsPath}. Run 'npm run build' in packages/sbb-mcp (which runs the web build first).`);
|
|
90
|
-
}
|
|
91
|
-
const js = readFileSync(jsPath, 'utf8');
|
|
92
|
-
const css = existsSync(cssPath) ? readFileSync(cssPath, 'utf8') : '';
|
|
93
|
-
cachedBundles = { js, css };
|
|
94
|
-
return cachedBundles;
|
|
95
|
-
}
|
|
96
|
-
function renderWidgetHtml(id) {
|
|
97
|
-
const { js, css } = loadBundles();
|
|
98
|
-
// Full HTML document shape matching OpenAI's Pizzaz reference server.
|
|
99
|
-
//
|
|
100
|
-
// We settled on a full `<!doctype html>` document (rather than a naked
|
|
101
|
-
// fragment) because ChatGPT's widget iframe resolver wraps text/html+
|
|
102
|
-
// skybridge resources as-is — no implicit <html>/<body> — and Preact's
|
|
103
|
-
// hydration was silently failing when the bundle executed before the
|
|
104
|
-
// document's body was parsed.
|
|
105
|
-
//
|
|
106
|
-
// Ref: https://github.com/openai/openai-apps-sdk-examples
|
|
107
|
-
return `<!doctype html>
|
|
108
|
-
<html>
|
|
109
|
-
<head>
|
|
110
|
-
<meta charset="utf-8">
|
|
111
|
-
<style>${css}</style>
|
|
112
|
-
</head>
|
|
113
|
-
<body>
|
|
114
|
-
<div id="sbb-widget-root" data-widget="${id}"></div>
|
|
115
|
-
<script>${js}</script>
|
|
116
|
-
</body>
|
|
117
|
-
</html>`;
|
|
118
|
-
}
|
|
119
|
-
const DEFINITIONS = [
|
|
120
|
-
{
|
|
121
|
-
id: WIDGETS.STATIONS_LIST,
|
|
122
|
-
title: 'Stations',
|
|
123
|
-
description: 'Card view of Swiss train station search results.',
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
id: WIDGETS.CONNECTION_LIST,
|
|
127
|
-
title: 'Train connections',
|
|
128
|
-
description: 'Card list of Swiss train connections with times, duration, and transfers.',
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
id: WIDGETS.TRIP_DETAILS,
|
|
132
|
-
title: 'Trip details',
|
|
133
|
-
description: 'Timeline view of a single train journey with stops, platforms, and occupancy.',
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
id: WIDGETS.PRICES_TABLE,
|
|
137
|
-
title: 'Ticket prices',
|
|
138
|
-
description: 'Table view of 1st- and 2nd-class ticket prices for selected trips.',
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
id: WIDGETS.TICKET_CARD,
|
|
142
|
-
title: 'Buy ticket',
|
|
143
|
-
description: 'SBB ticket purchase card with deep link that opens SBB.ch or the SBB mobile app.',
|
|
144
|
-
},
|
|
145
|
-
];
|
|
146
|
-
/** Registers all widget resources on the given MCP server. Call once per server instance. */
|
|
147
|
-
export function registerWidgets(server) {
|
|
148
|
-
for (const def of DEFINITIONS) {
|
|
149
|
-
const uri = widgetUri(def.id);
|
|
150
|
-
const meta = widgetToolMeta(def.id, 'Loading…', 'Done');
|
|
151
|
-
const config = {
|
|
152
|
-
title: def.title,
|
|
153
|
-
description: def.description,
|
|
154
|
-
mimeType: 'text/html+skybridge',
|
|
155
|
-
_meta: meta,
|
|
156
|
-
};
|
|
157
|
-
const read = async () => ({
|
|
158
|
-
contents: [
|
|
159
|
-
{
|
|
160
|
-
uri,
|
|
161
|
-
mimeType: 'text/html+skybridge',
|
|
162
|
-
text: renderWidgetHtml(def.id),
|
|
163
|
-
_meta: meta,
|
|
164
|
-
},
|
|
165
|
-
],
|
|
166
|
-
});
|
|
167
|
-
// Static registration — surfaces in resources/list + enables plain
|
|
168
|
-
// resources/read by URI. Matches Pizzaz's ListResourcesRequest shape.
|
|
169
|
-
server.registerResource(`widget-${def.id}`, uri, config, read);
|
|
170
|
-
// Template registration — surfaces in resources/templates/list, which
|
|
171
|
-
// is what ChatGPT's Apps SDK widget resolver actually queries to
|
|
172
|
-
// discover mountable widgets. Before this dual registration, ChatGPT
|
|
173
|
-
// saw an empty template list, failed with "Error loading app — Failed
|
|
174
|
-
// to fetch template", and never mounted the iframe.
|
|
175
|
-
//
|
|
176
|
-
// `list: undefined` matches the ResourceTemplate signature and tells
|
|
177
|
-
// the SDK there is no dynamic enumeration needed (our template URI
|
|
178
|
-
// has no {variables} — it's a plain URI, like Pizzaz).
|
|
179
|
-
//
|
|
180
|
-
// Ref: https://github.com/openai/openai-apps-sdk-examples/blob/main/pizzaz_server_node/src/server.ts
|
|
181
|
-
server.registerResource(`widget-${def.id}-template`, new ResourceTemplate(uri, { list: undefined }), config, read);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
//# sourceMappingURL=widgets.js.map
|
package/dist/widgets.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"widgets.js","sourceRoot":"","sources":["../src/widgets.ts"],"names":[],"mappings":"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"}
|
package/web/dist/widgets.css
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:host,.sbb-root{--bg: #ffffff;--bg-raised: #f7f7f8;--border: #e5e5e7;--text: #0f172a;--text-muted: #6b7280;--accent: #eb0000;--accent-fg: #ffffff;--accent-muted: #fff0f0;--success: #0a9447;--warn: #b45309;--radius: 12px;--radius-sm: 8px;--gap: 12px;--gap-sm: 8px;color:var(--text);background:var(--bg);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:14px;line-height:1.45;-webkit-font-smoothing:antialiased}.sbb-root[data-theme=dark]{--bg: #0f1115;--bg-raised: #1a1d23;--border: #2a2e37;--text: #e5e7eb;--text-muted: #9ca3af;--accent: #ff4444;--accent-fg: #ffffff;--accent-muted: #2a1515;--success: #10b981;--warn: #f59e0b}.sbb-root{padding:0;margin:0;max-width:560px}.sbb-header{display:flex;align-items:baseline;justify-content:space-between;gap:var(--gap-sm);margin:0 0 var(--gap) 0;padding:0}.sbb-header__title{font-size:15px;font-weight:600;margin:0}.sbb-header__meta{color:var(--text-muted);font-size:12px}.sbb-card{background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius);padding:var(--gap);display:flex;flex-direction:column;gap:var(--gap-sm)}.sbb-list{display:flex;flex-direction:column;gap:var(--gap-sm)}.sbb-connection{display:grid;grid-template-columns:1fr auto;gap:var(--gap-sm);align-items:center;padding:var(--gap-sm) var(--gap);border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--bg)}.sbb-connection__times{display:flex;align-items:baseline;gap:8px;font-variant-numeric:tabular-nums}.sbb-connection__time{font-weight:600;font-size:16px}.sbb-connection__time--link{color:var(--accent);text-decoration:none}.sbb-connection__time--link:hover{text-decoration:underline}.sbb-connection__arrow{color:var(--text-muted)}.sbb-connection__meta{color:var(--text-muted);font-size:12px;margin-top:2px;display:flex;flex-wrap:wrap;gap:6px}.sbb-connection__route{font-size:12px;color:var(--text-muted);display:flex;flex-wrap:wrap;gap:4px;align-items:center}.sbb-connection__actions{display:flex;gap:6px;justify-self:end}.sbb-badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:var(--bg-raised);border:1px solid var(--border);font-size:11px;color:var(--text);white-space:nowrap}.sbb-badge--accent{background:var(--accent-muted);border-color:transparent;color:var(--accent);font-weight:600}.sbb-button{appearance:none;border:1px solid var(--border);background:var(--bg);color:var(--text);padding:6px 10px;font-size:12px;font-weight:500;border-radius:var(--radius-sm);cursor:pointer;display:inline-flex;align-items:center;gap:4px;text-decoration:none;font-family:inherit}.sbb-button:hover:not(:disabled){background:var(--bg-raised)}.sbb-button:disabled{opacity:.5;cursor:not-allowed}.sbb-button--primary{background:var(--accent);color:var(--accent-fg);border-color:var(--accent);padding:8px 14px;font-size:13px}.sbb-button--primary:hover:not(:disabled){background:var(--accent);filter:brightness(.95)}.sbb-footer{margin-top:var(--gap);padding:var(--gap-sm) 0;display:flex;gap:var(--gap-sm);flex-wrap:wrap}.sbb-table{width:100%;border-collapse:collapse;font-size:13px}.sbb-table th,.sbb-table td{padding:6px 10px;text-align:left;border-bottom:1px solid var(--border)}.sbb-table th{font-weight:600;color:var(--text-muted);font-size:12px;text-transform:uppercase;letter-spacing:.03em}.sbb-table td{font-variant-numeric:tabular-nums}.sbb-table tr:last-child td{border-bottom:0}.sbb-ticket-card{padding:16px;background:linear-gradient(135deg,var(--accent-muted),var(--bg-raised));border:1px solid var(--border);border-radius:var(--radius);display:flex;flex-direction:column;gap:var(--gap)}.sbb-ticket-card__route{font-size:16px;font-weight:600}.sbb-ticket-card__times{color:var(--text-muted);font-size:13px}.sbb-weather{padding:8px var(--gap);background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text-muted);font-size:12px;margin-top:var(--gap-sm)}.sbb-timeline{display:flex;flex-direction:column;gap:4px;position:relative;padding-left:16px}.sbb-timeline:before{content:"";position:absolute;left:4px;top:6px;bottom:6px;width:2px;background:var(--border)}.sbb-timeline__stop{position:relative;display:flex;justify-content:space-between;padding:2px 0;font-size:13px}.sbb-timeline__stop:before{content:"";position:absolute;left:-16px;top:8px;width:10px;height:10px;border-radius:50%;background:var(--bg);border:2px solid var(--accent)}.sbb-timeline__stop--intermediate:before{background:var(--border);border-color:var(--border);width:6px;height:6px;left:-14px;top:10px}.sbb-timeline__stop--intermediate{color:var(--text-muted);font-size:12px}.sbb-timeline__time{font-variant-numeric:tabular-nums;color:var(--text-muted);margin-right:8px}.sbb-empty{color:var(--text-muted);padding:var(--gap);text-align:center;font-size:13px}.sbb-debug{margin-top:var(--gap);padding:8px;border:1px dashed var(--border);border-radius:var(--radius-sm);font-size:11px;color:var(--text-muted);text-align:left}.sbb-debug summary{cursor:pointer;font-weight:600}.sbb-debug code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:10px;word-break:break-all}.sbb-traveler-bar{margin:0 0 var(--gap-sm) 0;padding:8px var(--gap);background:var(--bg-raised);border:1px solid var(--border);border-radius:var(--radius-sm);font-size:12px}.sbb-traveler-bar__summary{display:flex;align-items:center;justify-content:space-between;gap:var(--gap-sm)}.sbb-traveler-bar__text{color:var(--text-muted)}.sbb-traveler-bar__text strong{color:var(--text);font-weight:600}.sbb-traveler-bar__form{margin-top:var(--gap-sm);padding-top:var(--gap-sm);border-top:1px solid var(--border);display:flex;flex-direction:column;gap:var(--gap-sm)}.sbb-traveler-bar__group{display:flex;align-items:center;gap:var(--gap-sm);flex-wrap:wrap}.sbb-traveler-bar__label{color:var(--text-muted);font-size:11px;text-transform:uppercase;letter-spacing:.04em;min-width:110px}.sbb-traveler-bar__options{display:flex;gap:4px;flex-wrap:wrap}.sbb-traveler-bar__hint{margin:4px 0 0;color:var(--text-muted);font-size:11px;font-style:italic}.sbb-chip{appearance:none;border:1px solid var(--border);background:var(--bg);color:var(--text);padding:4px 10px;font-size:12px;border-radius:999px;cursor:pointer;font-family:inherit}.sbb-chip:hover{background:var(--bg-raised)}.sbb-chip--active{background:var(--accent-muted);border-color:var(--accent);color:var(--accent);font-weight:600}.sbb-button--ghost{background:transparent;border-color:transparent;color:var(--accent);padding:4px 8px}.sbb-button--ghost:hover:not(:disabled){background:var(--accent-muted)}
|
package/web/dist/widgets.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(){"use strict";var H,b,le,L,ce,ae,de,V,B,A,ue,J,K,Q,E={},F=[],Ue=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,U=Array.isArray;function C(e,t){for(var n in t)e[n]=t[n];return e}function X(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function Me(e,t,n){var i,o,s,c={};for(s in t)s=="key"?i=t[s]:s=="ref"?o=t[s]:c[s]=t[s];if(arguments.length>2&&(c.children=arguments.length>3?H.call(arguments,2):n),typeof e=="function"&&e.defaultProps!=null)for(s in e.defaultProps)c[s]===void 0&&(c[s]=e.defaultProps[s]);return M(e,c,i,o,null)}function M(e,t,n,i,o){var s={type:e,props:t,key:n,ref:i,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:o??++le,__i:-1,__u:0};return o==null&&b.vnode!=null&&b.vnode(s),s}function O(e){return e.children}function R(e,t){this.props=e,this.context=t}function x(e,t){if(t==null)return e.__?x(e.__,e.__i+1):null;for(var n;t<e.__k.length;t++)if((n=e.__k[t])!=null&&n.__e!=null)return n.__e;return typeof e.type=="function"?x(e):null}function Oe(e){if(e.__P&&e.__d){var t=e.__v,n=t.__e,i=[],o=[],s=C({},t);s.__v=t.__v+1,b.vnode&&b.vnode(s),Y(e.__P,s,t,e.__n,e.__P.namespaceURI,32&t.__u?[n]:null,i,n??x(t),!!(32&t.__u),o),s.__v=t.__v,s.__.__k[s.__i]=s,ye(i,s,o),t.__e=t.__=null,s.__e!=n&&he(s)}}function he(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(t){if(t!=null&&t.__e!=null)return e.__e=e.__c.base=t.__e}),he(e)}function pe(e){(!e.__d&&(e.__d=!0)&&L.push(e)&&!W.__r++||ce!=b.debounceRendering)&&((ce=b.debounceRendering)||ae)(W)}function W(){try{for(var e,t=1;L.length;)L.length>t&&L.sort(de),e=L.shift(),t=L.length,Oe(e)}finally{L.length=W.__r=0}}function be(e,t,n,i,o,s,c,a,d,l,h){var _,p,u,g,w,k,f,m=i&&i.__k||F,$=t.length;for(d=Re(n,t,m,d,$),_=0;_<$;_++)(u=n.__k[_])!=null&&(p=u.__i!=-1&&m[u.__i]||E,u.__i=_,k=Y(e,u,p,o,s,c,a,d,l,h),g=u.__e,u.ref&&p.ref!=u.ref&&(p.ref&&te(p.ref,null,u),h.push(u.ref,u.__c||g,u)),w==null&&g!=null&&(w=g),(f=!!(4&u.__u))||p.__k===u.__k?(d=fe(u,d,e,f),f&&p.__e&&(p.__e=null)):typeof u.type=="function"&&k!==void 0?d=k:g&&(d=g.nextSibling),u.__u&=-7);return n.__e=w,d}function Re(e,t,n,i,o){var s,c,a,d,l,h=n.length,_=h,p=0;for(e.__k=new Array(o),s=0;s<o;s++)(c=t[s])!=null&&typeof c!="boolean"&&typeof c!="function"?(typeof c=="string"||typeof c=="number"||typeof c=="bigint"||c.constructor==String?c=e.__k[s]=M(null,c,null,null,null):U(c)?c=e.__k[s]=M(O,{children:c},null,null,null):c.constructor===void 0&&c.__b>0?c=e.__k[s]=M(c.type,c.props,c.key,c.ref?c.ref:null,c.__v):e.__k[s]=c,d=s+p,c.__=e,c.__b=e.__b+1,a=null,(l=c.__i=We(c,n,d,_))!=-1&&(_--,(a=n[l])&&(a.__u|=2)),a==null||a.__v==null?(l==-1&&(o>h?p--:o<h&&p++),typeof c.type!="function"&&(c.__u|=4)):l!=d&&(l==d-1?p--:l==d+1?p++:(l>d?p--:p++,c.__u|=4))):e.__k[s]=null;if(_)for(s=0;s<h;s++)(a=n[s])!=null&&!(2&a.__u)&&(a.__e==i&&(i=x(a)),ke(a,a));return i}function fe(e,t,n,i){var o,s;if(typeof e.type=="function"){for(o=e.__k,s=0;o&&s<o.length;s++)o[s]&&(o[s].__=e,t=fe(o[s],t,n,i));return t}e.__e!=t&&(i&&(t&&e.type&&!t.parentNode&&(t=x(e)),n.insertBefore(e.__e,t||null)),t=e.__e);do t=t&&t.nextSibling;while(t!=null&&t.nodeType==8);return t}function We(e,t,n,i){var o,s,c,a=e.key,d=e.type,l=t[n],h=l!=null&&(2&l.__u)==0;if(l===null&&a==null||h&&a==l.key&&d==l.type)return n;if(i>(h?1:0)){for(o=n-1,s=n+1;o>=0||s<t.length;)if((l=t[c=o>=0?o--:s++])!=null&&!(2&l.__u)&&a==l.key&&d==l.type)return c}return-1}function me(e,t,n){t[0]=="-"?e.setProperty(t,n??""):e[t]=n==null?"":typeof n!="number"||Ue.test(t)?n:n+"px"}function Z(e,t,n,i,o){var s,c;e:if(t=="style")if(typeof n=="string")e.style.cssText=n;else{if(typeof i=="string"&&(e.style.cssText=i=""),i)for(t in i)n&&t in n||me(e.style,t,"");if(n)for(t in n)i&&n[t]==i[t]||me(e.style,t,n[t])}else if(t[0]=="o"&&t[1]=="n")s=t!=(t=t.replace(ue,"$1")),c=t.toLowerCase(),t=c in e||t=="onFocusOut"||t=="onFocusIn"?c.slice(2):t.slice(2),e.l||(e.l={}),e.l[t+s]=n,n?i?n[A]=i[A]:(n[A]=J,e.addEventListener(t,s?Q:K,s)):e.removeEventListener(t,s?Q:K,s);else{if(o=="http://www.w3.org/2000/svg")t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(t!="width"&&t!="height"&&t!="href"&&t!="list"&&t!="form"&&t!="tabIndex"&&t!="download"&&t!="rowSpan"&&t!="colSpan"&&t!="role"&&t!="popover"&&t in e)try{e[t]=n??"";break e}catch{}typeof n=="function"||(n==null||n===!1&&t[4]!="-"?e.removeAttribute(t):e.setAttribute(t,t=="popover"&&n==1?"":n))}}function ve(e){return function(t){if(this.l){var n=this.l[t.type+e];if(t[B]==null)t[B]=J++;else if(t[B]<n[A])return;return n(b.event?b.event(t):t)}}}function Y(e,t,n,i,o,s,c,a,d,l){var h,_,p,u,g,w,k,f,m,$,P,D,Fe,z,_e,T=t.type;if(t.constructor!==void 0)return null;128&n.__u&&(d=!!(32&n.__u),s=[a=t.__e=n.__e]),(h=b.__b)&&h(t);e:if(typeof T=="function")try{if(f=t.props,m=T.prototype&&T.prototype.render,$=(h=T.contextType)&&i[h.__c],P=h?$?$.props.value:h.__:i,n.__c?k=(_=t.__c=n.__c).__=_.__E:(m?t.__c=_=new T(f,P):(t.__c=_=new R(f,P),_.constructor=T,_.render=je),$&&$.sub(_),_.state||(_.state={}),_.__n=i,p=_.__d=!0,_.__h=[],_._sb=[]),m&&_.__s==null&&(_.__s=_.state),m&&T.getDerivedStateFromProps!=null&&(_.__s==_.state&&(_.__s=C({},_.__s)),C(_.__s,T.getDerivedStateFromProps(f,_.__s))),u=_.props,g=_.state,_.__v=t,p)m&&T.getDerivedStateFromProps==null&&_.componentWillMount!=null&&_.componentWillMount(),m&&_.componentDidMount!=null&&_.__h.push(_.componentDidMount);else{if(m&&T.getDerivedStateFromProps==null&&f!==u&&_.componentWillReceiveProps!=null&&_.componentWillReceiveProps(f,P),t.__v==n.__v||!_.__e&&_.shouldComponentUpdate!=null&&_.shouldComponentUpdate(f,_.__s,P)===!1){t.__v!=n.__v&&(_.props=f,_.state=_.__s,_.__d=!1),t.__e=n.__e,t.__k=n.__k,t.__k.some(function(I){I&&(I.__=t)}),F.push.apply(_.__h,_._sb),_._sb=[],_.__h.length&&c.push(_);break e}_.componentWillUpdate!=null&&_.componentWillUpdate(f,_.__s,P),m&&_.componentDidUpdate!=null&&_.__h.push(function(){_.componentDidUpdate(u,g,w)})}if(_.context=P,_.props=f,_.__P=e,_.__e=!1,D=b.__r,Fe=0,m)_.state=_.__s,_.__d=!1,D&&D(t),h=_.render(_.props,_.state,_.context),F.push.apply(_.__h,_._sb),_._sb=[];else do _.__d=!1,D&&D(t),h=_.render(_.props,_.state,_.context),_.state=_.__s;while(_.__d&&++Fe<25);_.state=_.__s,_.getChildContext!=null&&(i=C(C({},i),_.getChildContext())),m&&!p&&_.getSnapshotBeforeUpdate!=null&&(w=_.getSnapshotBeforeUpdate(u,g)),z=h!=null&&h.type===O&&h.key==null?ge(h.props.children):h,a=be(e,U(z)?z:[z],t,n,i,o,s,c,a,d,l),_.base=t.__e,t.__u&=-161,_.__h.length&&c.push(_),k&&(_.__E=_.__=null)}catch(I){if(t.__v=null,d||s!=null)if(I.then){for(t.__u|=d?160:128;a&&a.nodeType==8&&a.nextSibling;)a=a.nextSibling;s[s.indexOf(a)]=null,t.__e=a}else{for(_e=s.length;_e--;)X(s[_e]);ee(t)}else t.__e=n.__e,t.__k=n.__k,I.then||ee(t);b.__e(I,t,n)}else s==null&&t.__v==n.__v?(t.__k=n.__k,t.__e=n.__e):a=t.__e=Ze(n.__e,t,n,i,o,s,c,d,l);return(h=b.diffed)&&h(t),128&t.__u?void 0:a}function ee(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(ee))}function ye(e,t,n){for(var i=0;i<n.length;i++)te(n[i],n[++i],n[++i]);b.__c&&b.__c(t,e),e.some(function(o){try{e=o.__h,o.__h=[],e.some(function(s){s.call(o)})}catch(s){b.__e(s,o.__v)}})}function ge(e){return typeof e!="object"||e==null||e.__b>0?e:U(e)?e.map(ge):C({},e)}function Ze(e,t,n,i,o,s,c,a,d){var l,h,_,p,u,g,w,k=n.props||E,f=t.props,m=t.type;if(m=="svg"?o="http://www.w3.org/2000/svg":m=="math"?o="http://www.w3.org/1998/Math/MathML":o||(o="http://www.w3.org/1999/xhtml"),s!=null){for(l=0;l<s.length;l++)if((u=s[l])&&"setAttribute"in u==!!m&&(m?u.localName==m:u.nodeType==3)){e=u,s[l]=null;break}}if(e==null){if(m==null)return document.createTextNode(f);e=document.createElementNS(o,m,f.is&&f),a&&(b.__m&&b.__m(t,s),a=!1),s=null}if(m==null)k===f||a&&e.data==f||(e.data=f);else{if(s=s&&H.call(e.childNodes),!a&&s!=null)for(k={},l=0;l<e.attributes.length;l++)k[(u=e.attributes[l]).name]=u.value;for(l in k)u=k[l],l=="dangerouslySetInnerHTML"?_=u:l=="children"||l in f||l=="value"&&"defaultValue"in f||l=="checked"&&"defaultChecked"in f||Z(e,l,null,u,o);for(l in f)u=f[l],l=="children"?p=u:l=="dangerouslySetInnerHTML"?h=u:l=="value"?g=u:l=="checked"?w=u:a&&typeof u!="function"||k[l]===u||Z(e,l,u,k[l],o);if(h)a||_&&(h.__html==_.__html||h.__html==e.innerHTML)||(e.innerHTML=h.__html),t.__k=[];else if(_&&(e.innerHTML=""),be(t.type=="template"?e.content:e,U(p)?p:[p],t,n,i,m=="foreignObject"?"http://www.w3.org/1999/xhtml":o,s,c,s?s[0]:n.__k&&x(n,0),a,d),s!=null)for(l=s.length;l--;)X(s[l]);a||(l="value",m=="progress"&&g==null?e.removeAttribute("value"):g!=null&&(g!==e[l]||m=="progress"&&!g||m=="option"&&g!=k[l])&&Z(e,l,g,k[l],o),l="checked",w!=null&&w!=e[l]&&Z(e,l,w,k[l],o))}return e}function te(e,t,n){try{if(typeof e=="function"){var i=typeof e.__u=="function";i&&e.__u(),i&&t==null||(e.__u=e(t))}else e.current=t}catch(o){b.__e(o,n)}}function ke(e,t,n){var i,o;if(b.unmount&&b.unmount(e),(i=e.ref)&&(i.current&&i.current!=e.__e||te(i,null,t)),(i=e.__c)!=null){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(s){b.__e(s,t)}i.base=i.__P=null}if(i=e.__k)for(o=0;o<i.length;o++)i[o]&&ke(i[o],t,n||typeof e.type!="function");n||X(e.__e),e.__c=e.__=e.__e=void 0}function je(e,t,n){return this.constructor(e,n)}function qe(e,t,n){var i,o,s,c;t==document&&(t=document.documentElement),b.__&&b.__(e,t),o=(i=!1)?null:t.__k,s=[],c=[],Y(t,e=t.__k=Me(O,null,[e]),o||E,E,t.namespaceURI,o?null:t.firstChild?H.call(t.childNodes):null,s,o?o.__e:t.firstChild,i,c),ye(s,e,c)}H=F.slice,b={__e:function(e,t,n,i){for(var o,s,c;t=t.__;)if((o=t.__c)&&!o.__)try{if((s=o.constructor)&&s.getDerivedStateFromError!=null&&(o.setState(s.getDerivedStateFromError(e)),c=o.__d),o.componentDidCatch!=null&&(o.componentDidCatch(e,i||{}),c=o.__d),c)return o.__E=o}catch(a){e=a}throw e}},le=0,R.prototype.setState=function(e,t){var n;n=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=C({},this.state),typeof e=="function"&&(e=e(C({},n),this.props)),e&&C(n,e),e!=null&&this.__v&&(t&&this._sb.push(t),pe(this))},R.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),pe(this))},R.prototype.render=O,L=[],ae=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,de=function(e,t){return e.__v.__b-t.__v.__b},W.__r=0,V=Math.random().toString(8),B="__d"+V,A="__a"+V,ue=/(PointerCapture)$|Capture$/i,J=0,K=ve(!1),Q=ve(!0);var Ge=0;function r(e,t,n,i,o,s){t||(t={});var c,a,d=t;if("ref"in d)for(a in d={},t)a=="ref"?c=t[a]:d[a]=t[a];var l={type:e,props:d,key:n,ref:c,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--Ge,__i:-1,__u:0,__source:o,__self:s};if(typeof e=="function"&&(c=e.defaultProps))for(a in c)d[a]===void 0&&(d[a]=c[a]);return b.vnode&&b.vnode(l),l}function N(){return window.openai??{}}function ze({data:e}){return e.stations.length?r("div",{children:[r("header",{class:"sbb-header",children:[r("h2",{class:"sbb-header__title",children:['Stations matching "',e.query,'"']}),r("span",{class:"sbb-header__meta",children:[e.stations.length," result",e.stations.length===1?"":"s"]})]}),r("div",{class:"sbb-list",children:e.stations.map(t=>r("div",{class:"sbb-connection",children:r("div",{children:[r("div",{style:{fontWeight:600},children:t.name}),r("div",{class:"sbb-connection__meta",children:[r("span",{class:"sbb-badge",children:["ID ",t.id]}),typeof t.lat=="number"&&typeof t.lon=="number"&&r("span",{children:[t.lat.toFixed(3),", ",t.lon.toFixed(3)]})]})]})},t.id))})]}):r("div",{class:"sbb-empty",children:['No stations matched "',e.query,'".']})}var ne,v,re,we,ie=0,Te=[],y=b,Ce=y.__b,Se=y.__r,Le=y.diffed,Ne=y.__c,$e=y.unmount,Pe=y.__;function Ve(e,t){y.__h&&y.__h(v,e,ie||t),ie=0;var n=v.__H||(v.__H={__:[],__h:[]});return e>=n.__.length&&n.__.push({}),n.__[e]}function Je(e){return ie=1,Ke(Ie,e)}function Ke(e,t,n){var i=Ve(ne++,2);if(i.t=e,!i.__c&&(i.__=[Ie(void 0,t),function(a){var d=i.__N?i.__N[0]:i.__[0],l=i.t(d,a);d!==l&&(i.__N=[l,i.__[1]],i.__c.setState({}))}],i.__c=v,!v.__f)){var o=function(a,d,l){if(!i.__c.__H)return!0;var h=i.__c.__H.__.filter(function(p){return p.__c});if(h.every(function(p){return!p.__N}))return!s||s.call(this,a,d,l);var _=i.__c.props!==a;return h.some(function(p){if(p.__N){var u=p.__[0];p.__=p.__N,p.__N=void 0,u!==p.__[0]&&(_=!0)}}),s&&s.call(this,a,d,l)||_};v.__f=!0;var s=v.shouldComponentUpdate,c=v.componentWillUpdate;v.componentWillUpdate=function(a,d,l){if(this.__e){var h=s;s=void 0,o(a,d,l),s=h}c&&c.call(this,a,d,l)},v.shouldComponentUpdate=o}return i.__N||i.__}function Qe(){for(var e;e=Te.shift();){var t=e.__H;if(e.__P&&t)try{t.__h.some(j),t.__h.some(se),t.__h=[]}catch(n){t.__h=[],y.__e(n,e.__v)}}}y.__b=function(e){v=null,Ce&&Ce(e)},y.__=function(e,t){e&&t.__k&&t.__k.__m&&(e.__m=t.__k.__m),Pe&&Pe(e,t)},y.__r=function(e){Se&&Se(e),ne=0;var t=(v=e.__c).__H;t&&(re===v?(t.__h=[],v.__h=[],t.__.some(function(n){n.__N&&(n.__=n.__N),n.u=n.__N=void 0})):(t.__h.some(j),t.__h.some(se),t.__h=[],ne=0)),re=v},y.diffed=function(e){Le&&Le(e);var t=e.__c;t&&t.__H&&(t.__H.__h.length&&(Te.push(t)!==1&&we===y.requestAnimationFrame||((we=y.requestAnimationFrame)||Xe)(Qe)),t.__H.__.some(function(n){n.u&&(n.__H=n.u),n.u=void 0})),re=v=null},y.__c=function(e,t){t.some(function(n){try{n.__h.some(j),n.__h=n.__h.filter(function(i){return!i.__||se(i)})}catch(i){t.some(function(o){o.__h&&(o.__h=[])}),t=[],y.__e(i,n.__v)}}),Ne&&Ne(e,t)},y.unmount=function(e){$e&&$e(e);var t,n=e.__c;n&&n.__H&&(n.__H.__.some(function(i){try{j(i)}catch(o){t=o}}),n.__H=void 0,t&&y.__e(t,n.__v))};var xe=typeof requestAnimationFrame=="function";function Xe(e){var t,n=function(){clearTimeout(i),xe&&cancelAnimationFrame(t),setTimeout(e)},i=setTimeout(n,35);xe&&(t=requestAnimationFrame(n))}function j(e){var t=v,n=e.__c;typeof n=="function"&&(e.__c=void 0,n()),v=t}function se(e){var t=v;e.__c=e.__(),v=t}function Ie(e,t){return typeof t=="function"?t(e):t}function S(e){try{return new Date(e).toLocaleTimeString("de-CH",{hour:"2-digit",minute:"2-digit",timeZone:"Europe/Zurich"})}catch{return e}}function oe(e){try{return new Date(e).toLocaleDateString("en-CH",{weekday:"short",day:"2-digit",month:"short",timeZone:"Europe/Zurich"})}catch{return e}}function q(e){if(!Number.isFinite(e)||e<0)return"—";const t=Math.floor(e/60),n=e%60;return t===0?`${n}m`:n===0?`${t}h`:`${t}h ${n}m`}function Ae(e,t="CHF"){return`${t} ${e.toFixed(2)}`}const Ye="https://mcp.swisstrip.app",et=1;function tt(e){return btoa(unescape(encodeURIComponent(e))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function nt(e){const t=new Date(e),n=t.toLocaleDateString("en-CA",{timeZone:"Europe/Zurich"}),i=t.toLocaleTimeString("en-GB",{timeZone:"Europe/Zurich",hour:"2-digit",minute:"2-digit"});return{date:n,time:i}}function rt(e){const{date:t,time:n}=nt(e.departureIso),i={v:et,tid:e.tripId,o:e.fromId,on:e.fromName,d:e.toId,dn:e.toName,dt:t,t:n,l:e.lang??"en"},o=tt(JSON.stringify(i));return`${Ye}/r/${o}`}const it={reductionCard:"HALF_FARE",travelerType:"ADULT"};function De(e){switch(e){case"HALF_FARE":return"Halbtax";case"GA":return"GA";case"NONE":return"No card"}}function st(e){return e==="CHILD"?"child":"adult"}function ot({prefs:e,onChange:t}){const[n,i]=Je(!1);return r("div",{class:"sbb-traveler-bar",children:[r("div",{class:"sbb-traveler-bar__summary",children:[r("span",{class:"sbb-traveler-bar__text",children:["Pricing for: ",r("strong",{children:["1 ",st(e.travelerType)]})," · ",r("strong",{children:De(e.reductionCard)})]}),r("button",{type:"button",class:"sbb-button sbb-button--ghost",onClick:()=>i(!n),children:n?"Done":"Change"})]}),n&&r("div",{class:"sbb-traveler-bar__form",children:[r("div",{class:"sbb-traveler-bar__group",children:[r("span",{class:"sbb-traveler-bar__label",children:"Reduction card"}),r("div",{class:"sbb-traveler-bar__options",children:["HALF_FARE","GA","NONE"].map(o=>r("button",{type:"button",class:`sbb-chip ${e.reductionCard===o?"sbb-chip--active":""}`,onClick:()=>t({...e,reductionCard:o}),children:De(o)},o))})]}),r("div",{class:"sbb-traveler-bar__group",children:[r("span",{class:"sbb-traveler-bar__label",children:"Traveler"}),r("div",{class:"sbb-traveler-bar__options",children:["ADULT","CHILD"].map(o=>r("button",{type:"button",class:`sbb-chip ${e.travelerType===o?"sbb-chip--active":""}`,onClick:()=>t({...e,travelerType:o}),children:o==="ADULT"?"Adult":"Child (6–16)"},o))})]}),r("p",{class:"sbb-traveler-bar__hint",children:'For multiple travelers or family tickets, ask in chat — e.g. "2 adults + 1 kid age 8".'})]})]})}function _t({legs:e}){const t=e.filter(n=>n.type==="train");return t.length===0?null:r("span",{class:"sbb-connection__route",children:t.map((n,i)=>r("span",{children:[i>0&&r("span",{style:{color:"var(--text-muted)",margin:"0 2px"},children:"›"}),r("span",{class:"sbb-badge",children:[n.line??"Train",n.platform?` · Pl. ${n.platform}`:""]})]},i))})}function lt(e,t,n,i){return rt({tripId:e.tripId,fromId:t.id,fromName:t.name,toId:n.id,toName:n.name,departureIso:e.departureTime,lang:i})}function ct({c:e,origin:t,destination:n,lang:i,onPrice:o}){const s=lt(e,t,n,i);return r("div",{class:"sbb-connection",children:[r("div",{children:[r("div",{class:"sbb-connection__times",children:[r("a",{class:"sbb-connection__time sbb-connection__time--link",href:s,target:"_blank",rel:"noopener noreferrer",title:"Open on SBB.ch",children:S(e.departureTime)}),r("span",{class:"sbb-connection__arrow",children:"→"}),r("span",{class:"sbb-connection__time",children:S(e.arrivalTime)})]}),r("div",{class:"sbb-connection__meta",children:[r("span",{class:"sbb-badge",children:q(e.durationMinutes)}),r("span",{class:"sbb-badge",children:[e.transfers," transfer",e.transfers===1?"":"s"]}),r(_t,{legs:e.legs})]})]}),r("div",{class:"sbb-connection__actions",children:[r("button",{type:"button",class:"sbb-button",onClick:o,children:"Price"}),r("a",{class:"sbb-button sbb-button--primary",href:s,target:"_blank",rel:"noopener noreferrer",children:"Book"})]})]})}function at({data:e,theme:t}){const n=N(),i=n.widgetState?.travelerPrefs??it,o=l=>{n.setWidgetState?.({...n.widgetState??{},travelerPrefs:l})},s=()=>({traveler_type:i.travelerType,reduction_card:i.reductionCard}),c=l=>{n.callTool?.("get_prices",{trip_ids:[l],...s()})},a=l=>{n.callTool?.("get_more_connections",{collection_id:e.collectionId,direction:l})},d=()=>{const l=e.connections.slice(0,5).map(h=>h.tripId);l.length&&n.callTool?.("get_prices",{trip_ids:l,...s()})};return e.connections.length?r("div",{children:[r("header",{class:"sbb-header",children:[r("h2",{class:"sbb-header__title",children:[e.origin.name," → ",e.destination.name]}),r("span",{class:"sbb-header__meta",children:oe(e.date)})]}),r(ot,{prefs:i,onChange:o}),r("div",{class:"sbb-list",children:e.connections.map(l=>r(ct,{c:l,origin:e.origin,destination:e.destination,lang:e.lang,onPrice:()=>c(l.tripId)},l.tripId))}),e.weather?.summary&&r("div",{class:"sbb-weather",children:["☁ ",e.destination.name,": ",e.weather.summary]}),r("div",{class:"sbb-footer",children:[r("button",{type:"button",class:"sbb-button",onClick:()=>a("previous"),children:"← Earlier"}),r("button",{type:"button",class:"sbb-button",onClick:()=>a("next"),children:"Later →"}),r("button",{type:"button",class:"sbb-button sbb-button--primary",onClick:d,children:"See all prices"})]})]}):r("div",{class:"sbb-empty",children:"No connections found."})}function dt({leg:e,index:t}){return e.type==="walk"?r("div",{class:"sbb-card",style:{background:"var(--bg)"},children:r("div",{style:{fontWeight:600},children:["Transfer · walk ",q(e.durationMinutes)]})}):r("div",{class:"sbb-card",children:[r("div",{style:{display:"flex",alignItems:"baseline",justifyContent:"space-between",gap:8},children:[r("div",{children:[r("span",{class:"sbb-badge sbb-badge--accent",children:["Leg ",t+1]}),r("span",{style:{marginLeft:8,fontWeight:600},children:[e.line??"Train",e.operator?r("span",{class:"sbb-header__meta",children:[" · ",e.operator]}):null]})]}),r("span",{class:"sbb-header__meta",children:q(e.durationMinutes)})]}),r("div",{class:"sbb-timeline",children:[e.from&&r("div",{class:"sbb-timeline__stop",children:[r("span",{children:[r("strong",{children:e.from.name}),e.from.platform?r("span",{class:"sbb-header__meta",children:[" · Pl. ",e.from.platform]}):null]}),r("span",{class:"sbb-timeline__time",children:S(e.from.time)})]}),e.intermediateStops?.map((n,i)=>r("div",{class:"sbb-timeline__stop sbb-timeline__stop--intermediate",children:[r("span",{children:n.name}),r("span",{class:"sbb-timeline__time",children:n.arrivalTime?S(n.arrivalTime):""})]},i)),e.to&&r("div",{class:"sbb-timeline__stop",children:[r("span",{children:[r("strong",{children:e.to.name}),e.to.platform?r("span",{class:"sbb-header__meta",children:[" · Pl. ",e.to.platform]}):null]}),r("span",{class:"sbb-timeline__time",children:S(e.to.time)})]})]}),e.occupancy&&r("div",{class:"sbb-connection__meta",children:[e.occupancy.firstClass&&r("span",{class:"sbb-badge",children:["1st: ",e.occupancy.firstClass]}),e.occupancy.secondClass&&r("span",{class:"sbb-badge",children:["2nd: ",e.occupancy.secondClass]})]})]})}function ut({data:e}){const t=N();return r("div",{children:[r("header",{class:"sbb-header",children:[r("h2",{class:"sbb-header__title",children:[e.origin.name," → ",e.destination.name]}),r("span",{class:"sbb-header__meta",children:oe(e.departureTime)})]}),r("div",{class:"sbb-card",style:{marginBottom:"var(--gap)"},children:r("div",{style:{display:"flex",justifyContent:"space-between"},children:[r("div",{class:"sbb-connection__times",children:[r("span",{class:"sbb-connection__time",children:S(e.departureTime)}),r("span",{class:"sbb-connection__arrow",children:"→"}),r("span",{class:"sbb-connection__time",children:S(e.arrivalTime)})]}),r("div",{class:"sbb-connection__meta",children:[r("span",{class:"sbb-badge",children:q(e.durationMinutes)}),r("span",{class:"sbb-badge",children:[e.transfers," transfer",e.transfers===1?"":"s"]}),r("span",{class:"sbb-badge",children:e.status})]})]})}),r("div",{class:"sbb-list",children:e.legs.map((n,i)=>r(dt,{leg:n,index:i},i))}),r("div",{class:"sbb-footer",children:r("button",{type:"button",class:"sbb-button sbb-button--primary",onClick:()=>t.callTool?.("get_prices",{trip_ids:[e.tripId]}),children:"See price"})})]})}function ht({data:e}){return e.prices.length?r("div",{children:[r("header",{class:"sbb-header",children:[r("h2",{class:"sbb-header__title",children:"Ticket prices"}),r("span",{class:"sbb-header__meta",children:[e.prices.length," trip",e.prices.length===1?"":"s"]})]}),r("div",{class:"sbb-card",children:r("table",{class:"sbb-table",children:[r("thead",{children:r("tr",{children:[r("th",{children:"Trip"}),r("th",{children:"2nd class"}),r("th",{children:"1st class"})]})}),r("tbody",{children:e.prices.map(t=>r("tr",{children:[r("td",{style:{fontFamily:"ui-monospace, monospace",fontSize:11},children:[t.tripId.slice(0,12),"…"]}),r("td",{children:t.secondClass?Ae(t.secondClass.amount,t.secondClass.currency):"—"}),r("td",{children:t.firstClass?Ae(t.firstClass.amount,t.firstClass.currency):"—"})]},t.tripId))})]})}),r("div",{class:"sbb-header__meta",style:{marginTop:8},children:"Prices are estimates. Ask for the ticket link to see the final price on SBB.ch."})]}):r("div",{class:"sbb-empty",children:"No price information available."})}function He(e){return`https://swisstrip.app/go?url=${encodeURIComponent(e)}`}function pt({data:e}){const t=e.affiliateLink??e.primaryLink;return r("div",{class:"sbb-ticket-card",children:[r("header",{class:"sbb-header",children:[r("h2",{class:"sbb-header__title",children:"Buy ticket on SBB"}),r("span",{class:"sbb-header__meta",children:oe(e.departureTime)})]}),r("div",{children:[r("div",{class:"sbb-ticket-card__route",children:[e.origin.name," → ",e.destination.name]}),r("div",{class:"sbb-ticket-card__times",children:[S(e.departureTime)," – ",S(e.arrivalTime)]})]}),r("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:[r("a",{class:"sbb-button sbb-button--primary",href:He(t),target:"_blank",rel:"noopener noreferrer",children:"Buy on SBB.ch →"}),e.affiliateLink&&e.affiliateLink!==e.primaryLink&&r("a",{class:"sbb-button",href:He(e.primaryLink),target:"_blank",rel:"noopener noreferrer",children:"Direct SBB link"})]}),r("div",{class:"sbb-header__meta",style:{fontSize:11},children:"Opens SBB.ch with this connection pre-filled. On mobile, the SBB app opens directly with your Halbtax/GA applied."})]})}function bt(){const t=document.getElementById("sbb-widget-root")?.getAttribute("data-widget");if(t)return t;const i=N().toolName;return i?{search_stations:"stations-list",search_connections:"connection-list",get_more_connections:"connection-list",get_trip_details:"trip-details",get_prices:"prices-table",get_ticket_link:"ticket-card"}[i]??null:null}let G=0;function Be({tick:e}){const t=N(),n=typeof window<"u"&&window.openai?Object.keys(window.openai).join(", ")||"(none)":"(no window.openai)";return r("details",{class:"sbb-debug",children:[r("summary",{children:["Debug · tick ",e]}),r("div",{children:["window.openai keys: ",r("code",{children:n})]}),r("div",{children:["toolName: ",r("code",{children:String(t.toolName??"undefined")})]}),r("div",{children:["toolOutput: ",r("code",{children:t.toolOutput?"present":"missing"})]})]})}function ft(){const e=bt(),t=N(),n=t.toolOutput,i=t.theme==="dark"?"dark":"light";if(!e)return r("div",{class:"sbb-empty",children:["Widget not initialised.",r(Be,{tick:G})]});if(!n)return r("div",{class:"sbb-empty",children:["Loading…",r(Be,{tick:G})]});switch(e){case"stations-list":return r(ze,{data:n});case"connection-list":return r(at,{data:n,theme:i});case"trip-details":return r(ut,{data:n});case"prices-table":return r(ht,{data:n});case"ticket-card":return r(pt,{data:n})}}function mt(e){const t=N().theme==="dark"?"dark":"light";e.setAttribute("data-theme",t)}function Ee(){const e=document.getElementById("sbb-widget-root");if(!e){console.warn("[sbb-widgets] #sbb-widget-root not found");return}e.classList.add("sbb-root");const t=o=>{G++,console.debug("[sbb-widgets] rerender",{reason:o,tick:G,hasToolOutput:!!N().toolOutput}),mt(e),qe(r(ft,{}),e)};t("initial"),window.addEventListener("openai:set_globals",()=>t("set_globals"));let n=0;const i=window.setInterval(()=>{n++,N().toolOutput?(t("poll"),window.clearInterval(i)):n>=50&&window.clearInterval(i)},100)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",Ee):Ee()})();
|