sbb-mcp 0.4.2 → 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.
Files changed (74) hide show
  1. package/LICENSE +50 -57
  2. package/README.md +26 -296
  3. package/dist/index.js +47 -17
  4. package/package.json +10 -29
  5. package/dist/auth.d.ts +0 -2
  6. package/dist/auth.js +0 -44
  7. package/dist/auth.js.map +0 -1
  8. package/dist/cache.d.ts +0 -12
  9. package/dist/cache.js +0 -58
  10. package/dist/cache.js.map +0 -1
  11. package/dist/client.d.ts +0 -17
  12. package/dist/client.js +0 -70
  13. package/dist/client.js.map +0 -1
  14. package/dist/formatters.d.ts +0 -22
  15. package/dist/formatters.js +0 -210
  16. package/dist/formatters.js.map +0 -1
  17. package/dist/http.d.ts +0 -2
  18. package/dist/http.js +0 -61
  19. package/dist/http.js.map +0 -1
  20. package/dist/i18n.d.ts +0 -22
  21. package/dist/i18n.js +0 -36
  22. package/dist/i18n.js.map +0 -1
  23. package/dist/index.d.ts +0 -2
  24. package/dist/index.js.map +0 -1
  25. package/dist/journey.d.ts +0 -5
  26. package/dist/journey.js +0 -67
  27. package/dist/journey.js.map +0 -1
  28. package/dist/prices.d.ts +0 -3
  29. package/dist/prices.js +0 -51
  30. package/dist/prices.js.map +0 -1
  31. package/dist/profile.d.ts +0 -16
  32. package/dist/profile.js +0 -77
  33. package/dist/profile.js.map +0 -1
  34. package/dist/rate-limit.d.ts +0 -5
  35. package/dist/rate-limit.js +0 -44
  36. package/dist/rate-limit.js.map +0 -1
  37. package/dist/structured.d.ts +0 -119
  38. package/dist/structured.js +0 -133
  39. package/dist/structured.js.map +0 -1
  40. package/dist/swisstrip.d.ts +0 -41
  41. package/dist/swisstrip.js +0 -119
  42. package/dist/swisstrip.js.map +0 -1
  43. package/dist/tools.d.ts +0 -2
  44. package/dist/tools.js +0 -464
  45. package/dist/tools.js.map +0 -1
  46. package/dist/transport/index.d.ts +0 -9
  47. package/dist/transport/index.js +0 -9
  48. package/dist/transport/index.js.map +0 -1
  49. package/dist/transport/smapi-auth.d.ts +0 -2
  50. package/dist/transport/smapi-auth.js +0 -39
  51. package/dist/transport/smapi-auth.js.map +0 -1
  52. package/dist/transport/smapi-client.d.ts +0 -22
  53. package/dist/transport/smapi-client.js +0 -83
  54. package/dist/transport/smapi-client.js.map +0 -1
  55. package/dist/transport/smapi-journey.d.ts +0 -23
  56. package/dist/transport/smapi-journey.js +0 -82
  57. package/dist/transport/smapi-journey.js.map +0 -1
  58. package/dist/transport/smapi-mock.d.ts +0 -9
  59. package/dist/transport/smapi-mock.js +0 -151
  60. package/dist/transport/smapi-mock.js.map +0 -1
  61. package/dist/transport/smapi-prices.d.ts +0 -14
  62. package/dist/transport/smapi-prices.js +0 -73
  63. package/dist/transport/smapi-prices.js.map +0 -1
  64. package/dist/transport/smapi-types.d.ts +0 -153
  65. package/dist/transport/smapi-types.js +0 -2
  66. package/dist/transport/smapi-types.js.map +0 -1
  67. package/dist/types.d.ts +0 -139
  68. package/dist/types.js +0 -3
  69. package/dist/types.js.map +0 -1
  70. package/dist/widgets.d.ts +0 -33
  71. package/dist/widgets.js +0 -120
  72. package/dist/widgets.js.map +0 -1
  73. package/web/dist/widgets.css +0 -1
  74. package/web/dist/widgets.js +0 -1
@@ -1,153 +0,0 @@
1
- export type SmapiPlaceType = 'StopPlace' | 'Address' | 'PointOfInterest';
2
- export type SmapiGeoPosition = {
3
- longitude: number;
4
- latitude: number;
5
- };
6
- export type SmapiPlaceRef = {
7
- objectType: 'StopPlaceRef' | 'AddressRef' | 'PointOfInterestRef';
8
- stopPlaceRef?: string;
9
- };
10
- export type SmapiPlace = {
11
- objectType: SmapiPlaceType;
12
- id: string;
13
- name: string;
14
- geoPosition?: SmapiGeoPosition;
15
- ref?: SmapiPlaceRef;
16
- };
17
- export type SmapiTripStatus = 'PLANNED' | 'CONFIRMED' | 'CHANGED' | 'CANCELLED';
18
- export type SmapiTimedLeg = {
19
- type: 'timed';
20
- board: {
21
- stopPlace: SmapiPlace;
22
- departureTime: string;
23
- platform?: string;
24
- };
25
- alight: {
26
- stopPlace: SmapiPlace;
27
- arrivalTime: string;
28
- platform?: string;
29
- };
30
- service: {
31
- publishedLineName?: string;
32
- operatorName?: string;
33
- };
34
- duration: string;
35
- intermediateStops?: Array<{
36
- stopPlace: SmapiPlace;
37
- arrivalTime?: string;
38
- departureTime?: string;
39
- }>;
40
- occupancy?: {
41
- firstClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
42
- secondClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
43
- };
44
- };
45
- export type SmapiTransferLeg = {
46
- type: 'transfer';
47
- start: SmapiPlaceRef;
48
- end: SmapiPlaceRef;
49
- duration: string;
50
- transferMode: string;
51
- };
52
- export type SmapiContinuousLeg = {
53
- type: 'continuous';
54
- service: {
55
- providerName?: string;
56
- };
57
- duration: string;
58
- };
59
- export type SmapiTripLeg = SmapiTimedLeg | SmapiTransferLeg | SmapiContinuousLeg;
60
- export type SmapiTrip = {
61
- id: string;
62
- origin: SmapiPlace;
63
- destination: SmapiPlace;
64
- startTime: string;
65
- endTime: string;
66
- duration: string;
67
- transfers: number;
68
- legs: SmapiTripLeg[];
69
- tripStatus: SmapiTripStatus;
70
- };
71
- export type SmapiTripSummary = {
72
- id: string;
73
- origin: SmapiPlace;
74
- destination: SmapiPlace;
75
- startTime: string;
76
- endTime: string;
77
- duration: string;
78
- transfers: number;
79
- };
80
- export type SmapiTripsCollection = {
81
- id: string;
82
- trips: SmapiTrip[];
83
- tripSummaries: SmapiTripSummary[];
84
- links?: {
85
- next?: string;
86
- previous?: string;
87
- };
88
- warnings?: string[];
89
- };
90
- export type SmapiTransportMode = 'HIGH_SPEED_TRAIN' | 'HISTORIC_TRAIN' | 'INTERCITY' | 'REGIONAL' | 'INTERREGIONAL' | 'URBAN' | 'TRAIN' | 'TRAM' | 'UNDERGROUND' | 'NIGHT_TRAIN' | 'SHARED_TAXI' | 'MOTOR_RAIL' | 'MOUNTAIN_TRAIN' | 'PLANE' | 'COACH_GROUP' | 'SHIP' | 'BUS';
91
- export type SmapiTripSearchParams = {
92
- origin: string;
93
- destination: string;
94
- departureTime?: string;
95
- arrivalTime?: string;
96
- transferLimit?: number;
97
- vias?: Array<{
98
- stopPlaceRef: string;
99
- dwellTime?: string;
100
- }>;
101
- ptModeFilter?: {
102
- exclude: boolean;
103
- transportModes: SmapiTransportMode[];
104
- };
105
- };
106
- export type SmapiPlaceSearchParams = {
107
- name: string;
108
- type?: 'STOP' | 'ADDRESS' | 'POI';
109
- numberOfResults?: number;
110
- };
111
- export type SmapiReductionCard = 'HALF_FARE' | 'GA' | 'NONE';
112
- export type SmapiTraveler = {
113
- id: string;
114
- type: 'ADULT' | 'CHILD';
115
- dateOfBirth?: string;
116
- reductionCard?: SmapiReductionCard;
117
- firstName?: string;
118
- lastName?: string;
119
- };
120
- export type SmapiPrice = {
121
- amount: number;
122
- currency: string;
123
- class: '1' | '2';
124
- reductionCard?: SmapiReductionCard;
125
- };
126
- export type SmapiPriceResult = {
127
- tripId: string;
128
- prices: SmapiPrice[];
129
- };
130
- export type SmapiOfferCategory = 'TRANSPORT' | 'RESERVATION' | 'ANCILLARY';
131
- export type SmapiOffer = {
132
- offerId: string;
133
- category: SmapiOfferCategory;
134
- price: SmapiPrice;
135
- validUntil: string;
136
- affiliateLink?: string;
137
- };
138
- export type SmapiOfferContainer = {
139
- offers: SmapiOffer[];
140
- totalPrice: SmapiPrice;
141
- };
142
- export type SmapiOfferResult = {
143
- tripId: string;
144
- containers: SmapiOfferContainer[];
145
- affiliateDeepLink?: string;
146
- };
147
- export type SmapiProblem = {
148
- type: string;
149
- title?: string;
150
- status?: number;
151
- detail?: string;
152
- displayMessages?: string[];
153
- };
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=smapi-types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"smapi-types.js","sourceRoot":"","sources":["../../src/transport/smapi-types.ts"],"names":[],"mappings":""}
package/dist/types.d.ts DELETED
@@ -1,139 +0,0 @@
1
- export type PlaceType = 'StopPlace' | 'Address' | 'PointOfInterest';
2
- export type GeoPosition = {
3
- longitude: number;
4
- latitude: number;
5
- };
6
- export type PlaceRef = {
7
- objectType: 'StopPlaceRef' | 'AddressRef' | 'PointOfInterestRef';
8
- stopPlaceRef?: string;
9
- };
10
- export type Place = {
11
- objectType: PlaceType;
12
- id: string;
13
- name: string;
14
- geoPosition?: GeoPosition;
15
- ref?: PlaceRef;
16
- };
17
- export type TripStatus = 'PLANNED' | 'CONFIRMED' | 'CHANGED' | 'CANCELLED';
18
- export type TimedLeg = {
19
- type: 'timed';
20
- board: {
21
- stopPlace: Place;
22
- departureTime: string;
23
- platform?: string;
24
- };
25
- alight: {
26
- stopPlace: Place;
27
- arrivalTime: string;
28
- platform?: string;
29
- };
30
- service: {
31
- publishedLineName?: string;
32
- operatorName?: string;
33
- };
34
- duration: string;
35
- intermediateStops?: Array<{
36
- stopPlace: Place;
37
- arrivalTime?: string;
38
- departureTime?: string;
39
- }>;
40
- occupancy?: {
41
- firstClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
42
- secondClass?: 'LOW' | 'MEDIUM' | 'HIGH' | 'UNKNOWN';
43
- };
44
- };
45
- export type TransferLeg = {
46
- type: 'transfer';
47
- start: PlaceRef;
48
- end: PlaceRef;
49
- duration: string;
50
- transferMode: string;
51
- };
52
- export type ContinuousLeg = {
53
- type: 'continuous';
54
- service: {
55
- providerName?: string;
56
- };
57
- duration: string;
58
- };
59
- export type TripLeg = TimedLeg | TransferLeg | ContinuousLeg;
60
- export type Trip = {
61
- id: string;
62
- origin: Place;
63
- destination: Place;
64
- startTime: string;
65
- endTime: string;
66
- duration: string;
67
- transfers: number;
68
- legs: TripLeg[];
69
- tripStatus: TripStatus;
70
- };
71
- export type TripsCollection = {
72
- id: string;
73
- trips: Trip[];
74
- tripSummaries: Array<{
75
- id: string;
76
- origin: Place;
77
- destination: Place;
78
- startTime: string;
79
- endTime: string;
80
- duration: string;
81
- transfers: number;
82
- }>;
83
- links?: {
84
- next?: string;
85
- previous?: string;
86
- };
87
- warnings?: string[];
88
- };
89
- export type TransportMode = 'HIGH_SPEED_TRAIN' | 'HISTORIC_TRAIN' | 'INTERCITY' | 'REGIONAL' | 'INTERREGIONAL' | 'URBAN' | 'TRAIN' | 'TRAM' | 'UNDERGROUND' | 'NIGHT_TRAIN' | 'SHARED_TAXI' | 'MOTOR_RAIL' | 'MOUNTAIN_TRAIN' | 'PLANE' | 'COACH_GROUP' | 'SHIP' | 'BUS';
90
- export type TripSearchParams = {
91
- origin: string;
92
- destination: string;
93
- departureTime?: string;
94
- arrivalTime?: string;
95
- transferLimit?: number;
96
- vias?: Array<{
97
- stopPlaceRef: string;
98
- dwellTime?: string;
99
- }>;
100
- ptModeFilter?: {
101
- exclude: boolean;
102
- transportModes: TransportMode[];
103
- };
104
- };
105
- export type PlaceSearchParams = {
106
- name: string;
107
- type?: 'STOP' | 'ADDRESS' | 'POI';
108
- numberOfResults?: number;
109
- };
110
- export type ReductionCard = 'HALF_FARE' | 'GA' | 'NONE';
111
- export type Traveler = {
112
- id: string;
113
- type: 'ADULT' | 'CHILD';
114
- dateOfBirth?: string;
115
- reductionCard?: ReductionCard;
116
- firstName?: string;
117
- lastName?: string;
118
- };
119
- export type Price = {
120
- amount: number;
121
- currency: string;
122
- class: '1' | '2';
123
- reductionCard?: ReductionCard;
124
- };
125
- export type PriceResult = {
126
- tripId: string;
127
- prices: Price[];
128
- };
129
- export type OfferResult = {
130
- tripId: string;
131
- affiliateDeepLink?: string;
132
- };
133
- export type SmapiProblem = {
134
- type: string;
135
- title?: string;
136
- status?: number;
137
- detail?: string;
138
- displayMessages?: string[];
139
- };
package/dist/types.js DELETED
@@ -1,3 +0,0 @@
1
- // SBB SMAPI types — standalone, no external dependencies
2
- export {};
3
- //# sourceMappingURL=types.js.map
package/dist/types.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,yDAAyD"}
package/dist/widgets.d.ts DELETED
@@ -1,33 +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 type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
- /** Registered widget IDs (mirrored in web/src/main.tsx). */
13
- export declare const WIDGETS: {
14
- readonly STATIONS_LIST: "stations-list";
15
- readonly CONNECTION_LIST: "connection-list";
16
- readonly TRIP_DETAILS: "trip-details";
17
- readonly PRICES_TABLE: "prices-table";
18
- readonly TICKET_CARD: "ticket-card";
19
- };
20
- export type WidgetId = (typeof WIDGETS)[keyof typeof WIDGETS];
21
- export declare function widgetUri(id: WidgetId): string;
22
- /**
23
- * Descriptor `_meta` (on the tool and resource registrations).
24
- * This is what tells ChatGPT to render a UI widget for the tool.
25
- */
26
- export declare function widgetToolMeta(id: WidgetId, invoking: string, invoked: string): Record<string, unknown>;
27
- /**
28
- * Invocation `_meta` (on each CallTool response).
29
- * Mirrors the tool-level template so ChatGPT associates the response with the widget.
30
- */
31
- export declare function widgetResponseMeta(id: WidgetId): Record<string, unknown>;
32
- /** Registers all widget resources on the given MCP server. Call once per server instance. */
33
- export declare function registerWidgets(server: McpServer): void;
package/dist/widgets.js DELETED
@@ -1,120 +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
- const __dirname = dirname(fileURLToPath(import.meta.url));
15
- /** Widgets ship under packages/sbb-mcp/web/dist, i.e. two levels above dist/*.js at runtime. */
16
- const WEB_DIST = resolve(__dirname, '..', 'web', 'dist');
17
- /** Registered widget IDs (mirrored in web/src/main.tsx). */
18
- export const WIDGETS = {
19
- STATIONS_LIST: 'stations-list',
20
- CONNECTION_LIST: 'connection-list',
21
- TRIP_DETAILS: 'trip-details',
22
- PRICES_TABLE: 'prices-table',
23
- TICKET_CARD: 'ticket-card',
24
- };
25
- export function widgetUri(id) {
26
- return `ui://widget/${id}.html`;
27
- }
28
- /**
29
- * Descriptor `_meta` (on the tool and resource registrations).
30
- * This is what tells ChatGPT to render a UI widget for the tool.
31
- */
32
- export function widgetToolMeta(id, invoking, invoked) {
33
- return {
34
- 'openai/outputTemplate': widgetUri(id),
35
- 'openai/toolInvocation/invoking': invoking,
36
- 'openai/toolInvocation/invoked': invoked,
37
- 'openai/widgetAccessible': true,
38
- };
39
- }
40
- /**
41
- * Invocation `_meta` (on each CallTool response).
42
- * Mirrors the tool-level template so ChatGPT associates the response with the widget.
43
- */
44
- export function widgetResponseMeta(id) {
45
- return {
46
- 'openai/outputTemplate': widgetUri(id),
47
- 'openai/widgetAccessible': true,
48
- };
49
- }
50
- let cachedBundles = null;
51
- function loadBundles() {
52
- if (cachedBundles)
53
- return cachedBundles;
54
- const jsPath = resolve(WEB_DIST, 'widgets.js');
55
- const cssPath = resolve(WEB_DIST, 'widgets.css');
56
- if (!existsSync(jsPath)) {
57
- throw new Error(`Widget bundle not found at ${jsPath}. Run 'npm run build' in packages/sbb-mcp (which runs the web build first).`);
58
- }
59
- const js = readFileSync(jsPath, 'utf8');
60
- const css = existsSync(cssPath) ? readFileSync(cssPath, 'utf8') : '';
61
- cachedBundles = { js, css };
62
- return cachedBundles;
63
- }
64
- function renderWidgetHtml(id) {
65
- 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');
71
- }
72
- const DEFINITIONS = [
73
- {
74
- id: WIDGETS.STATIONS_LIST,
75
- title: 'Stations',
76
- description: 'Card view of Swiss train station search results.',
77
- },
78
- {
79
- id: WIDGETS.CONNECTION_LIST,
80
- title: 'Train connections',
81
- description: 'Card list of Swiss train connections with times, duration, and transfers.',
82
- },
83
- {
84
- id: WIDGETS.TRIP_DETAILS,
85
- title: 'Trip details',
86
- description: 'Timeline view of a single train journey with stops, platforms, and occupancy.',
87
- },
88
- {
89
- id: WIDGETS.PRICES_TABLE,
90
- title: 'Ticket prices',
91
- description: 'Table view of 1st- and 2nd-class ticket prices for selected trips.',
92
- },
93
- {
94
- id: WIDGETS.TICKET_CARD,
95
- title: 'Buy ticket',
96
- description: 'SBB ticket purchase card with deep link that opens SBB.ch or the SBB mobile app.',
97
- },
98
- ];
99
- /** Registers all widget resources on the given MCP server. Call once per server instance. */
100
- export function registerWidgets(server) {
101
- for (const def of DEFINITIONS) {
102
- const uri = widgetUri(def.id);
103
- server.registerResource(`widget-${def.id}`, uri, {
104
- title: def.title,
105
- description: def.description,
106
- mimeType: 'text/html+skybridge',
107
- _meta: widgetToolMeta(def.id, 'Loading…', 'Done'),
108
- }, async () => ({
109
- contents: [
110
- {
111
- uri,
112
- mimeType: 'text/html+skybridge',
113
- text: renderWidgetHtml(def.id),
114
- _meta: widgetToolMeta(def.id, 'Loading…', 'Done'),
115
- },
116
- ],
117
- }));
118
- }
119
- }
120
- //# sourceMappingURL=widgets.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"widgets.js","sourceRoot":"","sources":["../src/widgets.ts"],"names":[],"mappings":"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"}
@@ -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__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 +0,0 @@
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()})();