sbb-mcp 0.4.1 → 0.4.3

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.
Files changed (54) hide show
  1. package/LICENSE +57 -57
  2. package/README.md +258 -314
  3. package/dist/cache.d.ts +2 -0
  4. package/dist/cache.js +4 -0
  5. package/dist/cache.js.map +1 -1
  6. package/dist/formatters.d.ts +13 -0
  7. package/dist/formatters.js +94 -19
  8. package/dist/formatters.js.map +1 -1
  9. package/dist/http.js +59 -3
  10. package/dist/http.js.map +1 -1
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/look2book.d.ts +98 -0
  14. package/dist/look2book.js +212 -0
  15. package/dist/look2book.js.map +1 -0
  16. package/dist/profile.js +15 -8
  17. package/dist/profile.js.map +1 -1
  18. package/dist/shortlink.d.ts +60 -0
  19. package/dist/shortlink.js +122 -0
  20. package/dist/shortlink.js.map +1 -0
  21. package/dist/structured.d.ts +7 -1
  22. package/dist/structured.js +2 -1
  23. package/dist/structured.js.map +1 -1
  24. package/dist/swisstrip.js +21 -5
  25. package/dist/swisstrip.js.map +1 -1
  26. package/dist/tools.d.ts +38 -0
  27. package/dist/tools.js +122 -77
  28. package/dist/tools.js.map +1 -1
  29. package/dist/transport/index.d.ts +1 -0
  30. package/dist/transport/index.js +4 -0
  31. package/dist/transport/index.js.map +1 -1
  32. package/dist/transport/setup.d.ts +1 -0
  33. package/dist/transport/setup.js +59 -0
  34. package/dist/transport/setup.js.map +1 -0
  35. package/dist/transport/smapi-auth.d.ts +14 -2
  36. package/dist/transport/smapi-auth.js +67 -17
  37. package/dist/transport/smapi-auth.js.map +1 -1
  38. package/dist/transport/smapi-client.d.ts +25 -1
  39. package/dist/transport/smapi-client.js +112 -9
  40. package/dist/transport/smapi-client.js.map +1 -1
  41. package/dist/transport/smapi-journey.d.ts +11 -5
  42. package/dist/transport/smapi-journey.js +16 -7
  43. package/dist/transport/smapi-journey.js.map +1 -1
  44. package/dist/transport/smapi-mock.js.map +1 -1
  45. package/dist/transport/smapi-prices.d.ts +43 -9
  46. package/dist/transport/smapi-prices.js +112 -41
  47. package/dist/transport/smapi-prices.js.map +1 -1
  48. package/dist/transport/smapi-types.d.ts +28 -0
  49. package/dist/widgets.d.ts +30 -3
  50. package/dist/widgets.js +78 -14
  51. package/dist/widgets.js.map +1 -1
  52. package/package.json +79 -73
  53. package/web/dist/widgets.css +1 -1
  54. package/web/dist/widgets.js +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"smapi-prices.js","sourceRoot":"","sources":["../../src/transport/smapi-prices.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"smapi-prices.js","sourceRoot":"","sources":["../../src/transport/smapi-prices.ts"],"names":[],"mappings":"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"}
@@ -144,10 +144,38 @@ export type SmapiOfferResult = {
144
144
  containers: SmapiOfferContainer[];
145
145
  affiliateDeepLink?: string;
146
146
  };
147
+ /**
148
+ * Error type per RFC 9457 (Problem Details for HTTP APIs), which obsoletes
149
+ * RFC 7807 with the `code` and `pointers[]` extensions. SBB's cookbook still
150
+ * cites 7807 but the Journey OpenAPI v1.3.17 produces 9457 — the formats are
151
+ * compatible: 9457 just adds optional fields. Parse leniently and surface
152
+ * pointers in logs to make support tickets actionable.
153
+ */
154
+ export type SmapiProblemPointer = {
155
+ code?: string;
156
+ type?: string;
157
+ title?: string;
158
+ status?: number;
159
+ detail?: string;
160
+ instance?: string;
161
+ /** JSON pointer into the request body for the problematic attribute. */
162
+ requestPointer?: string;
163
+ /** JSON pointer into the response body for the problematic attribute. */
164
+ responsePointer?: string;
165
+ /** Identifier of the originating server, useful when SBB chains backends. */
166
+ originator?: string;
167
+ };
147
168
  export type SmapiProblem = {
169
+ /** RFC 9457 short error code; optional in 3.1, mandatory in upcoming. */
170
+ code?: string;
148
171
  type: string;
149
172
  title?: string;
150
173
  status?: number;
151
174
  detail?: string;
175
+ /** Absolute URI identifying this specific occurrence (RFC 9457). */
176
+ instance?: string;
177
+ /** Per-attribute problem pointers (RFC 9457). */
178
+ pointers?: SmapiProblemPointer[];
179
+ /** SBB extension: end-user-translatable messages (Accept-Language honored). */
152
180
  displayMessages?: string[];
153
181
  };
package/dist/widgets.d.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * produced by `packages/sbb-mcp/web`. Routing to the right widget component
9
9
  * happens in the bundle via `data-widget` on the root element.
10
10
  */
11
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
+ import { type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
12
  /** Registered widget IDs (mirrored in web/src/main.tsx). */
13
13
  export declare const WIDGETS: {
14
14
  readonly STATIONS_LIST: "stations-list";
@@ -19,6 +19,24 @@ export declare const WIDGETS: {
19
19
  };
20
20
  export type WidgetId = (typeof WIDGETS)[keyof typeof WIDGETS];
21
21
  export declare function widgetUri(id: WidgetId): string;
22
+ /**
23
+ * True when the connected MCP client renders our widget iframe (currently
24
+ * ChatGPT via the Apps SDK). Used by tool handlers to decide between a full
25
+ * markdown response (non-widget clients need it to display anything useful)
26
+ * and a terse one-line summary (widget clients already show the rich UI, and
27
+ * the detailed prose just gets paraphrased by the LLM into a duplicate
28
+ * narration next to the iframe — see https://…/issues/connections-shown-twice).
29
+ *
30
+ * Detection is based on `clientInfo.name` from the MCP `initialize` handshake.
31
+ * ChatGPT self-identifies as "ChatGPT" / "ChatGPT-Atlas" / similar — we match
32
+ * case-insensitively on "chatgpt" or "openai" so future naming tweaks from
33
+ * OpenAI don't silently revert us to the noisy path.
34
+ *
35
+ * Falls back to `false` (full markdown) when no clientInfo has been received
36
+ * yet or when the name doesn't match — the safe default for unknown clients
37
+ * that might not render widgets.
38
+ */
39
+ export declare function isWidgetRenderingClient(server: McpServer): boolean;
22
40
  /**
23
41
  * Descriptor `_meta` (on the tool and resource registrations).
24
42
  * This is what tells ChatGPT to render a UI widget for the tool.
@@ -26,8 +44,17 @@ export declare function widgetUri(id: WidgetId): string;
26
44
  export declare function widgetToolMeta(id: WidgetId, invoking: string, invoked: string): Record<string, unknown>;
27
45
  /**
28
46
  * Invocation `_meta` (on each CallTool response).
29
- * Mirrors the tool-level template so ChatGPT associates the response with the widget.
47
+ *
48
+ * IMPORTANT: matches OpenAI's Pizzaz reference server exactly — ONLY the
49
+ * invoking/invoked strings. Historically we also set `openai/outputTemplate`
50
+ * and `openai/widgetAccessible` here, but those belong on the tool descriptor
51
+ * + resource registration (see `widgetToolMeta`). Repeating them on the
52
+ * response appears to confuse ChatGPT's widget resolver: the iframe mounts
53
+ * but `window.openai.toolOutput` never populates — leaving every widget
54
+ * stuck on the "Loading…" fallback.
55
+ *
56
+ * Ref: https://github.com/openai/openai-apps-sdk-examples/blob/main/pizzaz_server_node/src/server.ts
30
57
  */
31
- export declare function widgetResponseMeta(id: WidgetId): Record<string, unknown>;
58
+ export declare function widgetResponseMeta(_id: WidgetId, invoking: string, invoked: string): Record<string, unknown>;
32
59
  /** Registers all widget resources on the given MCP server. Call once per server instance. */
33
60
  export declare function registerWidgets(server: McpServer): void;
package/dist/widgets.js CHANGED
@@ -11,6 +11,7 @@
11
11
  import { readFileSync, existsSync } from 'node:fs';
12
12
  import { dirname, resolve } from 'node:path';
13
13
  import { fileURLToPath } from 'node:url';
14
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
14
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
15
16
  /** Widgets ship under packages/sbb-mcp/web/dist, i.e. two levels above dist/*.js at runtime. */
16
17
  const WEB_DIST = resolve(__dirname, '..', 'web', 'dist');
@@ -25,6 +26,28 @@ export const WIDGETS = {
25
26
  export function widgetUri(id) {
26
27
  return `ui://widget/${id}.html`;
27
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
+ }
28
51
  /**
29
52
  * Descriptor `_meta` (on the tool and resource registrations).
30
53
  * This is what tells ChatGPT to render a UI widget for the tool.
@@ -39,12 +62,21 @@ export function widgetToolMeta(id, invoking, invoked) {
39
62
  }
40
63
  /**
41
64
  * Invocation `_meta` (on each CallTool response).
42
- * Mirrors the tool-level template so ChatGPT associates the response with the widget.
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
43
75
  */
44
- export function widgetResponseMeta(id) {
76
+ export function widgetResponseMeta(_id, invoking, invoked) {
45
77
  return {
46
- 'openai/outputTemplate': widgetUri(id),
47
- 'openai/widgetAccessible': true,
78
+ 'openai/toolInvocation/invoking': invoking,
79
+ 'openai/toolInvocation/invoked': invoked,
48
80
  };
49
81
  }
50
82
  let cachedBundles = null;
@@ -63,11 +95,26 @@ function loadBundles() {
63
95
  }
64
96
  function renderWidgetHtml(id) {
65
97
  const { js, css } = loadBundles();
66
- return [
67
- `<div id="sbb-widget-root" data-widget="${id}"></div>`,
68
- css ? `<style>${css}</style>` : '',
69
- `<script>${js}</script>`,
70
- ].join('\n');
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>`;
71
118
  }
72
119
  const DEFINITIONS = [
73
120
  {
@@ -100,21 +147,38 @@ const DEFINITIONS = [
100
147
  export function registerWidgets(server) {
101
148
  for (const def of DEFINITIONS) {
102
149
  const uri = widgetUri(def.id);
103
- server.registerResource(`widget-${def.id}`, uri, {
150
+ const meta = widgetToolMeta(def.id, 'Loading…', 'Done');
151
+ const config = {
104
152
  title: def.title,
105
153
  description: def.description,
106
154
  mimeType: 'text/html+skybridge',
107
- _meta: widgetToolMeta(def.id, 'Loading…', 'Done'),
108
- }, async () => ({
155
+ _meta: meta,
156
+ };
157
+ const read = async () => ({
109
158
  contents: [
110
159
  {
111
160
  uri,
112
161
  mimeType: 'text/html+skybridge',
113
162
  text: renderWidgetHtml(def.id),
114
- _meta: widgetToolMeta(def.id, 'Loading…', 'Done'),
163
+ _meta: meta,
115
164
  },
116
165
  ],
117
- }));
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);
118
182
  }
119
183
  }
120
184
  //# sourceMappingURL=widgets.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"widgets.js","sourceRoot":"","sources":["../src/widgets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AAEzD,gGAAgG;AAChG,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;AAExD,4DAA4D;AAC5D,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,aAAa,EAAE,eAAe;IAC9B,eAAe,EAAE,iBAAiB;IAClC,YAAY,EAAE,cAAc;IAC5B,YAAY,EAAE,cAAc;IAC5B,WAAW,EAAE,aAAa;CAClB,CAAA;AAIV,MAAM,UAAU,SAAS,CAAC,EAAY;IACpC,OAAO,eAAe,EAAE,OAAO,CAAA;AACjC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAAY,EAAE,QAAgB,EAAE,OAAe;IAC5E,OAAO;QACL,uBAAuB,EAAE,SAAS,CAAC,EAAE,CAAC;QACtC,gCAAgC,EAAE,QAAQ;QAC1C,+BAA+B,EAAE,OAAO;QACxC,yBAAyB,EAAE,IAAI;KAChC,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,EAAY;IAC7C,OAAO;QACL,uBAAuB,EAAE,SAAS,CAAC,EAAE,CAAC;QACtC,yBAAyB,EAAE,IAAI;KAChC,CAAA;AACH,CAAC;AAED,IAAI,aAAa,GAAuC,IAAI,CAAA;AAE5D,SAAS,WAAW;IAClB,IAAI,aAAa;QAAE,OAAO,aAAa,CAAA;IAEvC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAA;IAEhD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,8BAA8B,MAAM,6EAA6E,CAClH,CAAA;IACH,CAAC;IAED,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACpE,aAAa,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,CAAA;IAC3B,OAAO,aAAa,CAAA;AACtB,CAAC;AAED,SAAS,gBAAgB,CAAC,EAAY;IACpC,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,WAAW,EAAE,CAAA;IACjC,OAAO;QACL,0CAA0C,EAAE,UAAU;QACtD,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,EAAE;QAClC,WAAW,EAAE,WAAW;KACzB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAQD,MAAM,WAAW,GAAgB;IAC/B;QACE,EAAE,EAAE,OAAO,CAAC,aAAa;QACzB,KAAK,EAAE,UAAU;QACjB,WAAW,EAAE,kDAAkD;KAChE;IACD;QACE,EAAE,EAAE,OAAO,CAAC,eAAe;QAC3B,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,2EAA2E;KACzF;IACD;QACE,EAAE,EAAE,OAAO,CAAC,YAAY;QACxB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,+EAA+E;KAC7F;IACD;QACE,EAAE,EAAE,OAAO,CAAC,YAAY;QACxB,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,oEAAoE;KAClF;IACD;QACE,EAAE,EAAE,OAAO,CAAC,WAAW;QACvB,KAAK,EAAE,YAAY;QACnB,WAAW,EAAE,kFAAkF;KAChG;CACF,CAAA;AAED,6FAA6F;AAC7F,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QAC7B,MAAM,CAAC,gBAAgB,CACrB,UAAU,GAAG,CAAC,EAAE,EAAE,EAClB,GAAG,EACH;YACE,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,QAAQ,EAAE,qBAAqB;YAC/B,KAAK,EAAE,cAAc,CACnB,GAAG,CAAC,EAAE,EACN,UAAU,EACV,MAAM,CACP;SACF,EACD,KAAK,IAAI,EAAE,CAAC,CAAC;YACX,QAAQ,EAAE;gBACR;oBACE,GAAG;oBACH,QAAQ,EAAE,qBAAqB;oBAC/B,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC9B,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC;iBAClD;aACF;SACF,CAAC,CACH,CAAA;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"widgets.js","sourceRoot":"","sources":["../src/widgets.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,73 +1,79 @@
1
- {
2
- "name": "sbb-mcp",
3
- "version": "0.4.1",
4
- "mcpName": "io.github.Fabsbags/sbb-mcp",
5
- "description": "MCP server for Swiss Federal Railways (SBB/CFF/FFS) — real-time train schedules, prices, and ticket purchase links for any AI assistant",
6
- "type": "module",
7
- "main": "dist/index.js",
8
- "exports": {
9
- ".": "./dist/index.js",
10
- "./transport": "./dist/transport/index.js"
11
- },
12
- "bin": {
13
- "sbb-mcp": "dist/index.js"
14
- },
15
- "files": [
16
- "dist",
17
- "web/dist",
18
- "README.md",
19
- "LICENSE"
20
- ],
21
- "scripts": {
22
- "build:widgets": "npm --prefix web install --no-audit --no-fund && npm --prefix web run build",
23
- "build:server": "tsc -p tsconfig.build.json",
24
- "build": "npm run build:widgets && npm run build:server",
25
- "dev": "tsc -w -p tsconfig.build.json",
26
- "start:http": "node dist/http.js",
27
- "test": "vitest run",
28
- "prepublishOnly": "npm run build"
29
- },
30
- "keywords": [
31
- "mcp",
32
- "sbb",
33
- "cff",
34
- "ffs",
35
- "swiss-railways",
36
- "swiss-federal-railways",
37
- "train",
38
- "travel",
39
- "switzerland",
40
- "timetable",
41
- "public-transport",
42
- "ai",
43
- "claude",
44
- "cursor",
45
- "model-context-protocol"
46
- ],
47
- "author": "SwissTrip <fabsforward2@gmail.com>",
48
- "license": "FSL-1.1-MIT",
49
- "repository": {
50
- "type": "git",
51
- "url": "https://github.com/Fabsbags/sbb-mcp"
52
- },
53
- "homepage": "https://github.com/Fabsbags/sbb-mcp#readme",
54
- "bugs": {
55
- "url": "https://github.com/Fabsbags/sbb-mcp/issues"
56
- },
57
- "engines": {
58
- "node": ">=18"
59
- },
60
- "dependencies": {
61
- "@modelcontextprotocol/sdk": "^1.12.1",
62
- "express": "^5.1.0",
63
- "sbb-i18n": "^0.1.0",
64
- "swiss-weather-mcp": "^0.1.1",
65
- "zod": "^3.24.4"
66
- },
67
- "devDependencies": {
68
- "@types/express": "^5.0.2",
69
- "@types/node": "^22.15.3",
70
- "typescript": "^5.8.3",
71
- "vitest": "^4.1.0"
72
- }
73
- }
1
+ {
2
+ "name": "sbb-mcp",
3
+ "version": "0.4.3",
4
+ "mcpName": "io.github.Fabsbags/sbb-mcp",
5
+ "description": "MCP server for Swiss Federal Railways (SBB/CFF/FFS) — real-time train schedules, prices, and ticket purchase links for any AI assistant",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "exports": {
9
+ ".": "./dist/index.js",
10
+ "./transport": "./dist/transport/index.js"
11
+ },
12
+ "bin": {
13
+ "sbb-mcp": "dist/index.js"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "web/dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "ensure:i18n": "node scripts/ensure-i18n.cjs",
23
+ "sync:transport": "node scripts/sync-transport.cjs",
24
+ "check:transport": "node scripts/sync-transport.cjs --check",
25
+ "build:widgets": "npm --prefix web install --no-audit --no-fund && npm --prefix web run build",
26
+ "build:server": "tsc -p tsconfig.build.json",
27
+ "build": "npm run ensure:i18n && npm run sync:transport && npm run build:widgets && npm run build:server",
28
+ "dev": "tsc -w -p tsconfig.build.json",
29
+ "start:http": "node dist/http.js",
30
+ "test": "vitest run",
31
+ "smoke:int": "node scripts/smoke-int.mjs",
32
+ "prepack": "npm run sync:transport && npm run build",
33
+ "prepublishOnly": "npm run build"
34
+ },
35
+ "keywords": [
36
+ "mcp",
37
+ "sbb",
38
+ "cff",
39
+ "ffs",
40
+ "swiss-railways",
41
+ "swiss-federal-railways",
42
+ "train",
43
+ "travel",
44
+ "switzerland",
45
+ "timetable",
46
+ "public-transport",
47
+ "ai",
48
+ "claude",
49
+ "cursor",
50
+ "model-context-protocol"
51
+ ],
52
+ "author": "SwissTrip <fabsforward2@gmail.com>",
53
+ "license": "FSL-1.1-MIT",
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "https://github.com/Fabsbags/swisstrip-web",
57
+ "directory": "packages/sbb-mcp"
58
+ },
59
+ "homepage": "https://github.com/Fabsbags/swisstrip-web/tree/main/packages/sbb-mcp#readme",
60
+ "bugs": {
61
+ "url": "https://github.com/Fabsbags/swisstrip-web/issues"
62
+ },
63
+ "engines": {
64
+ "node": ">=18"
65
+ },
66
+ "dependencies": {
67
+ "@modelcontextprotocol/sdk": "^1.12.1",
68
+ "express": "^5.1.0",
69
+ "sbb-i18n": "^0.1.1",
70
+ "swiss-weather-mcp": "^0.1.1",
71
+ "zod": "^3.24.4"
72
+ },
73
+ "devDependencies": {
74
+ "@types/express": "^5.0.2",
75
+ "@types/node": "^22.15.3",
76
+ "typescript": "^5.8.3",
77
+ "vitest": "^4.1.0"
78
+ }
79
+ }
@@ -1 +1 @@
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__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}
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)}
@@ -1 +1 @@
1
- (function(){"use strict";var I,u,ee,T,ne,te,re,z,N,B,ie,Z,q,G,$={},E=[],ve=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,A=Array.isArray;function w(e,n){for(var t in n)e[t]=n[t];return e}function V(e){e&&e.parentNode&&e.parentNode.removeChild(e)}function ye(e,n,t){var l,o,i,c={};for(i in n)i=="key"?l=n[i]:i=="ref"?o=n[i]:c[i]=n[i];if(arguments.length>2&&(c.children=arguments.length>3?I.call(arguments,2):t),typeof e=="function"&&e.defaultProps!=null)for(i in e.defaultProps)c[i]===void 0&&(c[i]=e.defaultProps[i]);return F(e,c,l,o,null)}function F(e,n,t,l,o){var i={type:e,props:n,key:t,ref:l,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:o??++ee,__i:-1,__u:0};return o==null&&u.vnode!=null&&u.vnode(i),i}function U(e){return e.children}function W(e,n){this.props=e,this.context=n}function P(e,n){if(n==null)return e.__?P(e.__,e.__i+1):null;for(var t;n<e.__k.length;n++)if((t=e.__k[n])!=null&&t.__e!=null)return t.__e;return typeof e.type=="function"?P(e):null}function ge(e){if(e.__P&&e.__d){var n=e.__v,t=n.__e,l=[],o=[],i=w({},n);i.__v=n.__v+1,u.vnode&&u.vnode(i),J(e.__P,i,n,e.__n,e.__P.namespaceURI,32&n.__u?[t]:null,l,t??P(n),!!(32&n.__u),o),i.__v=n.__v,i.__.__k[i.__i]=i,de(l,i,o),n.__e=n.__=null,i.__e!=t&&se(i)}}function se(e){if((e=e.__)!=null&&e.__c!=null)return e.__e=e.__c.base=null,e.__k.some(function(n){if(n!=null&&n.__e!=null)return e.__e=e.__c.base=n.__e}),se(e)}function le(e){(!e.__d&&(e.__d=!0)&&T.push(e)&&!H.__r++||ne!=u.debounceRendering)&&((ne=u.debounceRendering)||te)(H)}function H(){try{for(var e,n=1;T.length;)T.length>n&&T.sort(re),e=T.shift(),n=T.length,ge(e)}finally{T.length=H.__r=0}}function oe(e,n,t,l,o,i,c,a,h,_,p){var s,m,d,v,g,y,b,f=l&&l.__k||E,S=n.length;for(h=ke(t,n,f,h,S),s=0;s<S;s++)(d=t.__k[s])!=null&&(m=d.__i!=-1&&f[d.__i]||$,d.__i=s,y=J(e,d,m,o,i,c,a,h,_,p),v=d.__e,d.ref&&m.ref!=d.ref&&(m.ref&&Q(m.ref,null,d),p.push(d.ref,d.__c||v,d)),g==null&&v!=null&&(g=v),(b=!!(4&d.__u))||m.__k===d.__k?(h=ce(d,h,e,b),b&&m.__e&&(m.__e=null)):typeof d.type=="function"&&y!==void 0?h=y:v&&(h=v.nextSibling),d.__u&=-7);return t.__e=g,h}function ke(e,n,t,l,o){var i,c,a,h,_,p=t.length,s=p,m=0;for(e.__k=new Array(o),i=0;i<o;i++)(c=n[i])!=null&&typeof c!="boolean"&&typeof c!="function"?(typeof c=="string"||typeof c=="number"||typeof c=="bigint"||c.constructor==String?c=e.__k[i]=F(null,c,null,null,null):A(c)?c=e.__k[i]=F(U,{children:c},null,null,null):c.constructor===void 0&&c.__b>0?c=e.__k[i]=F(c.type,c.props,c.key,c.ref?c.ref:null,c.__v):e.__k[i]=c,h=i+m,c.__=e,c.__b=e.__b+1,a=null,(_=c.__i=we(c,t,h,s))!=-1&&(s--,(a=t[_])&&(a.__u|=2)),a==null||a.__v==null?(_==-1&&(o>p?m--:o<p&&m++),typeof c.type!="function"&&(c.__u|=4)):_!=h&&(_==h-1?m--:_==h+1?m++:(_>h?m--:m++,c.__u|=4))):e.__k[i]=null;if(s)for(i=0;i<p;i++)(a=t[i])!=null&&!(2&a.__u)&&(a.__e==l&&(l=P(a)),pe(a,a));return l}function ce(e,n,t,l){var o,i;if(typeof e.type=="function"){for(o=e.__k,i=0;o&&i<o.length;i++)o[i]&&(o[i].__=e,n=ce(o[i],n,t,l));return n}e.__e!=n&&(l&&(n&&e.type&&!n.parentNode&&(n=P(e)),t.insertBefore(e.__e,n||null)),n=e.__e);do n=n&&n.nextSibling;while(n!=null&&n.nodeType==8);return n}function we(e,n,t,l){var o,i,c,a=e.key,h=e.type,_=n[t],p=_!=null&&(2&_.__u)==0;if(_===null&&a==null||p&&a==_.key&&h==_.type)return t;if(l>(p?1:0)){for(o=t-1,i=t+1;o>=0||i<n.length;)if((_=n[c=o>=0?o--:i++])!=null&&!(2&_.__u)&&a==_.key&&h==_.type)return c}return-1}function _e(e,n,t){n[0]=="-"?e.setProperty(n,t??""):e[n]=t==null?"":typeof t!="number"||ve.test(n)?t:t+"px"}function R(e,n,t,l,o){var i,c;e:if(n=="style")if(typeof t=="string")e.style.cssText=t;else{if(typeof l=="string"&&(e.style.cssText=l=""),l)for(n in l)t&&n in t||_e(e.style,n,"");if(t)for(n in t)l&&t[n]==l[n]||_e(e.style,n,t[n])}else if(n[0]=="o"&&n[1]=="n")i=n!=(n=n.replace(ie,"$1")),c=n.toLowerCase(),n=c in e||n=="onFocusOut"||n=="onFocusIn"?c.slice(2):n.slice(2),e.l||(e.l={}),e.l[n+i]=t,t?l?t[B]=l[B]:(t[B]=Z,e.addEventListener(n,i?G:q,i)):e.removeEventListener(n,i?G:q,i);else{if(o=="http://www.w3.org/2000/svg")n=n.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if(n!="width"&&n!="height"&&n!="href"&&n!="list"&&n!="form"&&n!="tabIndex"&&n!="download"&&n!="rowSpan"&&n!="colSpan"&&n!="role"&&n!="popover"&&n in e)try{e[n]=t??"";break e}catch{}typeof t=="function"||(t==null||t===!1&&n[4]!="-"?e.removeAttribute(n):e.setAttribute(n,n=="popover"&&t==1?"":t))}}function ae(e){return function(n){if(this.l){var t=this.l[n.type+e];if(n[N]==null)n[N]=Z++;else if(n[N]<t[B])return;return t(u.event?u.event(n):n)}}}function J(e,n,t,l,o,i,c,a,h,_){var p,s,m,d,v,g,y,b,f,S,x,M,me,j,Y,k=n.type;if(n.constructor!==void 0)return null;128&t.__u&&(h=!!(32&t.__u),i=[a=n.__e=t.__e]),(p=u.__b)&&p(n);e:if(typeof k=="function")try{if(b=n.props,f=k.prototype&&k.prototype.render,S=(p=k.contextType)&&l[p.__c],x=p?S?S.props.value:p.__:l,t.__c?y=(s=n.__c=t.__c).__=s.__E:(f?n.__c=s=new k(b,x):(n.__c=s=new W(b,x),s.constructor=k,s.render=Te),S&&S.sub(s),s.state||(s.state={}),s.__n=l,m=s.__d=!0,s.__h=[],s._sb=[]),f&&s.__s==null&&(s.__s=s.state),f&&k.getDerivedStateFromProps!=null&&(s.__s==s.state&&(s.__s=w({},s.__s)),w(s.__s,k.getDerivedStateFromProps(b,s.__s))),d=s.props,v=s.state,s.__v=n,m)f&&k.getDerivedStateFromProps==null&&s.componentWillMount!=null&&s.componentWillMount(),f&&s.componentDidMount!=null&&s.__h.push(s.componentDidMount);else{if(f&&k.getDerivedStateFromProps==null&&b!==d&&s.componentWillReceiveProps!=null&&s.componentWillReceiveProps(b,x),n.__v==t.__v||!s.__e&&s.shouldComponentUpdate!=null&&s.shouldComponentUpdate(b,s.__s,x)===!1){n.__v!=t.__v&&(s.props=b,s.state=s.__s,s.__d=!1),n.__e=t.__e,n.__k=t.__k,n.__k.some(function(L){L&&(L.__=n)}),E.push.apply(s.__h,s._sb),s._sb=[],s.__h.length&&c.push(s);break e}s.componentWillUpdate!=null&&s.componentWillUpdate(b,s.__s,x),f&&s.componentDidUpdate!=null&&s.__h.push(function(){s.componentDidUpdate(d,v,g)})}if(s.context=x,s.props=b,s.__P=e,s.__e=!1,M=u.__r,me=0,f)s.state=s.__s,s.__d=!1,M&&M(n),p=s.render(s.props,s.state,s.context),E.push.apply(s.__h,s._sb),s._sb=[];else do s.__d=!1,M&&M(n),p=s.render(s.props,s.state,s.context),s.state=s.__s;while(s.__d&&++me<25);s.state=s.__s,s.getChildContext!=null&&(l=w(w({},l),s.getChildContext())),f&&!m&&s.getSnapshotBeforeUpdate!=null&&(g=s.getSnapshotBeforeUpdate(d,v)),j=p!=null&&p.type===U&&p.key==null?he(p.props.children):p,a=oe(e,A(j)?j:[j],n,t,l,o,i,c,a,h,_),s.base=n.__e,n.__u&=-161,s.__h.length&&c.push(s),y&&(s.__E=s.__=null)}catch(L){if(n.__v=null,h||i!=null)if(L.then){for(n.__u|=h?160:128;a&&a.nodeType==8&&a.nextSibling;)a=a.nextSibling;i[i.indexOf(a)]=null,n.__e=a}else{for(Y=i.length;Y--;)V(i[Y]);K(n)}else n.__e=t.__e,n.__k=t.__k,L.then||K(n);u.__e(L,n,t)}else i==null&&n.__v==t.__v?(n.__k=t.__k,n.__e=t.__e):a=n.__e=Ce(t.__e,n,t,l,o,i,c,h,_);return(p=u.diffed)&&p(n),128&n.__u?void 0:a}function K(e){e&&(e.__c&&(e.__c.__e=!0),e.__k&&e.__k.some(K))}function de(e,n,t){for(var l=0;l<t.length;l++)Q(t[l],t[++l],t[++l]);u.__c&&u.__c(n,e),e.some(function(o){try{e=o.__h,o.__h=[],e.some(function(i){i.call(o)})}catch(i){u.__e(i,o.__v)}})}function he(e){return typeof e!="object"||e==null||e.__b>0?e:A(e)?e.map(he):w({},e)}function Ce(e,n,t,l,o,i,c,a,h){var _,p,s,m,d,v,g,y=t.props||$,b=n.props,f=n.type;if(f=="svg"?o="http://www.w3.org/2000/svg":f=="math"?o="http://www.w3.org/1998/Math/MathML":o||(o="http://www.w3.org/1999/xhtml"),i!=null){for(_=0;_<i.length;_++)if((d=i[_])&&"setAttribute"in d==!!f&&(f?d.localName==f:d.nodeType==3)){e=d,i[_]=null;break}}if(e==null){if(f==null)return document.createTextNode(b);e=document.createElementNS(o,f,b.is&&b),a&&(u.__m&&u.__m(n,i),a=!1),i=null}if(f==null)y===b||a&&e.data==b||(e.data=b);else{if(i=i&&I.call(e.childNodes),!a&&i!=null)for(y={},_=0;_<e.attributes.length;_++)y[(d=e.attributes[_]).name]=d.value;for(_ in y)d=y[_],_=="dangerouslySetInnerHTML"?s=d:_=="children"||_ in b||_=="value"&&"defaultValue"in b||_=="checked"&&"defaultChecked"in b||R(e,_,null,d,o);for(_ in b)d=b[_],_=="children"?m=d:_=="dangerouslySetInnerHTML"?p=d:_=="value"?v=d:_=="checked"?g=d:a&&typeof d!="function"||y[_]===d||R(e,_,d,y[_],o);if(p)a||s&&(p.__html==s.__html||p.__html==e.innerHTML)||(e.innerHTML=p.__html),n.__k=[];else if(s&&(e.innerHTML=""),oe(n.type=="template"?e.content:e,A(m)?m:[m],n,t,l,f=="foreignObject"?"http://www.w3.org/1999/xhtml":o,i,c,i?i[0]:t.__k&&P(t,0),a,h),i!=null)for(_=i.length;_--;)V(i[_]);a||(_="value",f=="progress"&&v==null?e.removeAttribute("value"):v!=null&&(v!==e[_]||f=="progress"&&!v||f=="option"&&v!=y[_])&&R(e,_,v,y[_],o),_="checked",g!=null&&g!=e[_]&&R(e,_,g,y[_],o))}return e}function Q(e,n,t){try{if(typeof e=="function"){var l=typeof e.__u=="function";l&&e.__u(),l&&n==null||(e.__u=e(n))}else e.current=n}catch(o){u.__e(o,t)}}function pe(e,n,t){var l,o;if(u.unmount&&u.unmount(e),(l=e.ref)&&(l.current&&l.current!=e.__e||Q(l,null,n)),(l=e.__c)!=null){if(l.componentWillUnmount)try{l.componentWillUnmount()}catch(i){u.__e(i,n)}l.base=l.__P=null}if(l=e.__k)for(o=0;o<l.length;o++)l[o]&&pe(l[o],n,t||typeof e.type!="function");t||V(e.__e),e.__c=e.__=e.__e=void 0}function Te(e,n,t){return this.constructor(e,t)}function Se(e,n,t){var l,o,i,c;n==document&&(n=document.documentElement),u.__&&u.__(e,n),o=(l=!1)?null:n.__k,i=[],c=[],J(n,e=n.__k=ye(U,null,[e]),o||$,$,n.namespaceURI,o?null:n.firstChild?I.call(n.childNodes):null,i,o?o.__e:n.firstChild,l,c),de(i,e,c)}I=E.slice,u={__e:function(e,n,t,l){for(var o,i,c;n=n.__;)if((o=n.__c)&&!o.__)try{if((i=o.constructor)&&i.getDerivedStateFromError!=null&&(o.setState(i.getDerivedStateFromError(e)),c=o.__d),o.componentDidCatch!=null&&(o.componentDidCatch(e,l||{}),c=o.__d),c)return o.__E=o}catch(a){e=a}throw e}},ee=0,W.prototype.setState=function(e,n){var t;t=this.__s!=null&&this.__s!=this.state?this.__s:this.__s=w({},this.state),typeof e=="function"&&(e=e(w({},t),this.props)),e&&w(t,e),e!=null&&this.__v&&(n&&this._sb.push(n),le(this))},W.prototype.forceUpdate=function(e){this.__v&&(this.__e=!0,e&&this.__h.push(e),le(this))},W.prototype.render=U,T=[],te=typeof Promise=="function"?Promise.prototype.then.bind(Promise.resolve()):setTimeout,re=function(e,n){return e.__v.__b-n.__v.__b},H.__r=0,z=Math.random().toString(8),N="__d"+z,B="__a"+z,ie=/(PointerCapture)$|Capture$/i,Z=0,q=ae(!1),G=ae(!0);var xe=0;function r(e,n,t,l,o,i){n||(n={});var c,a,h=n;if("ref"in h)for(a in h={},n)a=="ref"?c=n[a]:h[a]=n[a];var _={type:e,props:h,key:t,ref:c,__k:null,__:null,__b:0,__e:null,__c:null,constructor:void 0,__v:--xe,__i:-1,__u:0,__source:o,__self:i};if(typeof e=="function"&&(c=e.defaultProps))for(a in c)h[a]===void 0&&(h[a]=c[a]);return u.vnode&&u.vnode(_),_}function D(){return window.openai??{}}function Pe({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(n=>r("div",{class:"sbb-connection",children:r("div",{children:[r("div",{style:{fontWeight:600},children:n.name}),r("div",{class:"sbb-connection__meta",children:[r("span",{class:"sbb-badge",children:["ID ",n.id]}),typeof n.lat=="number"&&typeof n.lon=="number"&&r("span",{children:[n.lat.toFixed(3),", ",n.lon.toFixed(3)]})]})]})},n.id))})]}):r("div",{class:"sbb-empty",children:['No stations matched "',e.query,'".']})}function C(e){try{return new Date(e).toLocaleTimeString("de-CH",{hour:"2-digit",minute:"2-digit",timeZone:"Europe/Zurich"})}catch{return e}}function X(e){try{return new Date(e).toLocaleDateString("en-CH",{weekday:"short",day:"2-digit",month:"short",timeZone:"Europe/Zurich"})}catch{return e}}function O(e){if(!Number.isFinite(e)||e<0)return"—";const n=Math.floor(e/60),t=e%60;return n===0?`${t}m`:t===0?`${n}h`:`${n}h ${t}m`}function ue(e,n="CHF"){return`${n} ${e.toFixed(2)}`}function Le({legs:e}){const n=e.filter(t=>t.type==="train");return n.length===0?null:r("span",{class:"sbb-connection__route",children:n.map((t,l)=>r("span",{children:[l>0&&r("span",{style:{color:"var(--text-muted)",margin:"0 2px"},children:"›"}),r("span",{class:"sbb-badge",children:[t.line??"Train",t.platform?` · Pl. ${t.platform}`:""]})]},l))})}function Be({c:e,onPrice:n}){return r("div",{class:"sbb-connection",children:[r("div",{children:[r("div",{class:"sbb-connection__times",children:[r("span",{class:"sbb-connection__time",children:C(e.departureTime)}),r("span",{class:"sbb-connection__arrow",children:"→"}),r("span",{class:"sbb-connection__time",children:C(e.arrivalTime)})]}),r("div",{class:"sbb-connection__meta",children:[r("span",{class:"sbb-badge",children:O(e.durationMinutes)}),r("span",{class:"sbb-badge",children:[e.transfers," transfer",e.transfers===1?"":"s"]}),r(Le,{legs:e.legs})]})]}),r("div",{class:"sbb-connection__actions",children:r("button",{type:"button",class:"sbb-button",onClick:n,children:"Price"})})]})}function De({data:e,theme:n}){const t=D(),l=c=>{t.callTool?.("get_prices",{trip_ids:[c]})},o=c=>{t.callTool?.("get_more_connections",{collection_id:e.collectionId,direction:c})},i=()=>{const c=e.connections.slice(0,5).map(a=>a.tripId);c.length&&t.callTool?.("get_prices",{trip_ids:c})};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:X(e.date)})]}),r("div",{class:"sbb-list",children:e.connections.map(c=>r(Be,{c,onPrice:()=>l(c.tripId)},c.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:()=>o("previous"),children:"← Earlier"}),r("button",{type:"button",class:"sbb-button",onClick:()=>o("next"),children:"Later →"}),r("button",{type:"button",class:"sbb-button sbb-button--primary",onClick:i,children:"See all prices"})]})]}):r("div",{class:"sbb-empty",children:"No connections found."})}function Me({leg:e,index:n}){return e.type==="walk"?r("div",{class:"sbb-card",style:{background:"var(--bg)"},children:r("div",{style:{fontWeight:600},children:["Transfer · walk ",O(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 ",n+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:O(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:C(e.from.time)})]}),e.intermediateStops?.map((t,l)=>r("div",{class:"sbb-timeline__stop sbb-timeline__stop--intermediate",children:[r("span",{children:t.name}),r("span",{class:"sbb-timeline__time",children:t.arrivalTime?C(t.arrivalTime):""})]},l)),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:C(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 Ie({data:e}){const n=D();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:X(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:C(e.departureTime)}),r("span",{class:"sbb-connection__arrow",children:"→"}),r("span",{class:"sbb-connection__time",children:C(e.arrivalTime)})]}),r("div",{class:"sbb-connection__meta",children:[r("span",{class:"sbb-badge",children:O(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((t,l)=>r(Me,{leg:t,index:l},l))}),r("div",{class:"sbb-footer",children:r("button",{type:"button",class:"sbb-button sbb-button--primary",onClick:()=>n.callTool?.("get_prices",{trip_ids:[e.tripId]}),children:"See price"})})]})}function Ne({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(n=>r("tr",{children:[r("td",{style:{fontFamily:"ui-monospace, monospace",fontSize:11},children:[n.tripId.slice(0,12),"…"]}),r("td",{children:n.secondClass?ue(n.secondClass.amount,n.secondClass.currency):"—"}),r("td",{children:n.firstClass?ue(n.firstClass.amount,n.firstClass.currency):"—"})]},n.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 be(e){return`https://swisstrip.app/go?url=${encodeURIComponent(e)}`}function $e({data:e}){const n=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:X(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:[C(e.departureTime)," – ",C(e.arrivalTime)]})]}),r("div",{style:{display:"flex",gap:8,flexWrap:"wrap"},children:[r("a",{class:"sbb-button sbb-button--primary",href:be(n),target:"_blank",rel:"noopener noreferrer",children:"Buy on SBB.ch →"}),e.affiliateLink&&e.affiliateLink!==e.primaryLink&&r("a",{class:"sbb-button",href:be(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 Ee(){const n=document.getElementById("sbb-widget-root")?.getAttribute("data-widget");if(n)return n;const l=D().toolName;return l?{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"}[l]??null:null}function Ae(){const e=Ee(),n=D(),t=n.toolOutput?.structuredContent,l=n.theme==="dark"?"dark":"light";if(!e)return r("div",{class:"sbb-empty",children:"Widget not initialised."});if(!t)return r("div",{class:"sbb-empty",children:"Loading…"});switch(e){case"stations-list":return r(Pe,{data:t});case"connection-list":return r(De,{data:t,theme:l});case"trip-details":return r(Ie,{data:t});case"prices-table":return r(Ne,{data:t});case"ticket-card":return r($e,{data:t})}}function fe(){const e=document.getElementById("sbb-widget-root");if(!e){console.warn("[sbb-widgets] #sbb-widget-root not found");return}const n=D().theme==="dark"?"dark":"light";e.classList.add("sbb-root"),e.setAttribute("data-theme",n),Se(r(Ae,{}),e)}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",fe):fe()})();
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()})();