openDetail(log)}
- class="cursor-pointer transition-colors hover:bg-indigo-50/50 dark:hover:bg-indigo-900/10"
+ class="cursor-pointer transition-colors hover:bg-green-50/50 dark:hover:bg-green-900/10"
>
-
+
{formatTimestamp(log.timestamp)}
-
- {log.key_path}
+
+ {lastPathSegment(log.key_path)}
-
+
{#if isArray}
-
+
{formatValue(log.server1_value)}
{:else}
-
+ {@const trend = trendByLogId.get(log.id)}
+
+ {#if trend?.server1}
+ {#if trend.server1 === 'up'}
+
+
+
+ {:else if trend.server1 === 'down'}
+
+
+
+ {:else}
+
+
+
+ {/if}
+ {/if}
{formatValue(log.server1_value)}
{/if}
-
+
{#if isArray}
-
+
{formatValue(log.server2_value)}
{:else}
-
+ {@const trend = trendByLogId.get(log.id)}
+
+ {#if trend?.server2}
+ {#if trend.server2 === 'up'}
+
+
+
+ {:else if trend.server2 === 'down'}
+
+
+
+ {:else}
+
+
+
+ {/if}
+ {/if}
{formatValue(log.server2_value)}
{/if}
@@ -740,7 +1335,7 @@
>
- Log Detail: {selectedLogEntry.key_path}
@@ -836,7 +1431,7 @@
(showDetailModal = false)}
- class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none"
+ class="rounded-lg bg-green-600 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-green-700 focus:ring-2 focus:ring-green-500/20 focus:outline-none"
>
Close
@@ -883,7 +1478,7 @@
Array Compare:
- {arrayDetailLog.key_path}
@@ -1059,7 +1654,7 @@
Close
diff --git a/src/lib/components/ProtobufPanel.svelte b/src/lib/components/ProtobufPanel.svelte
index 1ef5e11..5118e31 100644
--- a/src/lib/components/ProtobufPanel.svelte
+++ b/src/lib/components/ProtobufPanel.svelte
@@ -373,7 +373,7 @@
type="text"
bind:value={pbState.tripUpdatesUrl}
placeholder="https://example.com/gtfs-rt/trip-updates"
- class="mt-2 w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600 dark:focus:ring-indigo-500/40"
+ class="mt-2 w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-green-500 focus:ring-2 focus:ring-green-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600 dark:focus:ring-green-500/40"
/>
@@ -386,7 +386,7 @@
type="text"
bind:value={pbState.vehiclePositionsUrl}
placeholder="https://example.com/gtfs-rt/vehicle-positions"
- class="mt-2 w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600 dark:focus:ring-indigo-500/40"
+ class="mt-2 w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-green-500 focus:ring-2 focus:ring-green-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600 dark:focus:ring-green-500/40"
/>
@@ -399,7 +399,7 @@
type="text"
bind:value={pbState.serviceAlertsUrl}
placeholder="https://example.com/gtfs-rt/service-alerts"
- class="mt-2 w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600 dark:focus:ring-indigo-500/40"
+ class="mt-2 w-full rounded-lg border border-gray-200 bg-gray-50 px-4 py-3 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-green-500 focus:ring-2 focus:ring-green-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600 dark:focus:ring-green-500/40"
/>
@@ -412,7 +412,7 @@
>
+ Add Header
@@ -425,14 +425,14 @@
value={header.key}
oninput={(e) => updateHeader(index, 'key', e.currentTarget.value)}
placeholder="Header Name (e.g., x-api-key)"
- class="flex-1 rounded-lg border border-gray-200 bg-gray-50 px-4 py-2.5 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600"
+ class="flex-1 rounded-lg border border-gray-200 bg-gray-50 px-4 py-2.5 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-green-500 focus:ring-2 focus:ring-green-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600"
/>
updateHeader(index, 'value', e.currentTarget.value)}
placeholder="Header Value"
- class="flex-1 rounded-lg border border-gray-200 bg-gray-50 px-4 py-2.5 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600"
+ class="flex-1 rounded-lg border border-gray-200 bg-gray-50 px-4 py-2.5 font-mono text-sm text-gray-700 transition-all placeholder:text-gray-400 focus:border-green-500 focus:ring-2 focus:ring-green-500/20 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 dark:placeholder:text-gray-600"
/>
{#if pbState.headers.length > 1}
Auto-refresh
@@ -480,7 +480,7 @@
bind:value={pbState.refreshInterval}
min="5"
max="300"
- class="w-16 rounded-lg border border-gray-200 bg-white px-2 py-1.5 text-center text-sm font-medium text-gray-700 focus:border-indigo-500 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300"
+ class="w-16 rounded-lg border border-gray-200 bg-white px-2 py-1.5 text-center text-sm font-medium text-gray-700 focus:border-green-500 focus:outline-none dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300"
/>
sec
@@ -491,7 +491,7 @@
type="checkbox"
bind:checked={pbState.loggingEnabled}
onchange={saveToLocalStorage}
- class="h-4 w-4 rounded border-gray-300 bg-white text-indigo-600 focus:ring-indigo-500 focus:ring-offset-0 dark:border-gray-600 dark:bg-gray-700"
+ class="h-4 w-4 rounded border-gray-300 bg-white text-green-600 focus:ring-green-500 focus:ring-offset-0 dark:border-gray-600 dark:bg-gray-700"
/>
Enable logging
@@ -505,7 +505,7 @@
{#if pbState.loading}
@@ -526,7 +526,7 @@
Fetching...
{:else}
Fetch All Feeds
- Ctrl+↵
+ Ctrl+↵
{/if}
diff --git a/src/lib/components/ProtobufViewer.svelte b/src/lib/components/ProtobufViewer.svelte
index e6875f8..e999e34 100644
--- a/src/lib/components/ProtobufViewer.svelte
+++ b/src/lib/components/ProtobufViewer.svelte
@@ -325,7 +325,7 @@
onclick={() => (protobufState.activeTab = tab.id)}
class="flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-all {protobufState.activeTab ===
tab.id
- ? 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400'
+ ? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
: 'text-gray-600 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700'}"
>
{#if tab.icon === 'trip'}
@@ -389,7 +389,7 @@
? 'bg-red-200 text-red-800 dark:bg-red-800 dark:text-red-200'
: 'bg-red-100 text-red-600 dark:bg-red-700 dark:text-red-300'
: protobufState.activeTab === tab.id
- ? 'bg-indigo-200 text-indigo-800 dark:bg-indigo-800 dark:text-indigo-200'
+ ? 'bg-green-200 text-green-800 dark:bg-green-800 dark:text-green-200'
: 'bg-gray-200 text-gray-600 dark:bg-gray-600 dark:text-gray-300'}"
title={tab.isLimited
? `Showing ${tab.count} of ${tab.total} (limited to prevent memory issues)`
@@ -413,7 +413,7 @@
{#if isSearching}
{#if protobufState.searchQuery}
(activeRawTextTab = tab.id)}
class="flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium shadow-sm transition-colors {activeRawTextTab ===
tab.id
- ? 'bg-indigo-500 text-white dark:bg-indigo-600'
- : 'bg-white text-gray-700 hover:bg-blue-50 hover:text-indigo-700 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-indigo-900/30 dark:hover:text-indigo-400'}"
+ ? 'bg-green-500 text-white dark:bg-green-600'
+ : 'bg-white text-gray-700 hover:bg-blue-50 hover:text-green-700 dark:bg-gray-600 dark:text-gray-200 dark:hover:bg-green-900/30 dark:hover:text-green-400'}"
>
{#if tab.icon === 'trip'}
#{index + 1}
@@ -770,6 +770,10 @@
{@const tripUpdate = item as Record
}
{@const trip = tripUpdate.trip as Record | undefined}
{@const vehicleInfo = tripUpdate.vehicle as Record | undefined}
+ {@const stopTimeUpdates = tripUpdate.stopTimeUpdate as
+ | Array>
+ | undefined}
+ {@const tripStopId = stopTimeUpdates?.[0]?.stopId}
{trip?.tripId || tripUpdate.id || 'Unknown Trip'}
@@ -780,6 +784,13 @@
Route: {trip.routeId}
{/if}
+ {#if tripStopId}
+
+ Stop: {tripStopId}
+
+ {/if}
{#if vehicleInfo?.id || vehicleInfo?.label}
}
{@const vehicleInfo = vehicle.vehicle as Record | undefined}
{@const trip = vehicle.trip as Record | undefined}
+ {@const vehicleStopId = (vehicle as Record).stopId}
{vehicleInfo?.id || vehicleInfo?.label || vehicle.id || 'Unknown Vehicle'}
@@ -816,6 +828,13 @@
Route: {trip.routeId}
{/if}
+ {#if vehicleStopId}
+
+ Stop: {vehicleStopId}
+
+ {/if}
{#if trip?.tripId}
{#if paginationLoading?.[protobufState.activeTab as 'tripUpdates' | 'vehiclePositions' | 'alerts']}
diff --git a/src/lib/components/SimpleJsonTree.svelte b/src/lib/components/SimpleJsonTree.svelte
index b000e00..a3a6450 100644
--- a/src/lib/components/SimpleJsonTree.svelte
+++ b/src/lib/components/SimpleJsonTree.svelte
@@ -110,7 +110,7 @@
e.stopPropagation();
expandAllChildren();
}}
- class="flex items-center gap-1 rounded-md border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-[10px] font-medium text-emerald-700 transition-colors hover:bg-emerald-100 dark:border-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-400 dark:hover:bg-emerald-900/50"
+ class="flex items-center gap-1 rounded-md border border-green-200 bg-green-50 px-2 py-0.5 text-[10px] font-medium text-green-700 transition-colors hover:bg-green-100 dark:border-green-800 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50"
title="Expand all items"
>
{#if result.isValid}
@@ -160,8 +160,8 @@
{#if result.serviceDay}
-
GTFS Service Time
-
+
GTFS Service Time
+
{result.serviceDay}
@@ -170,7 +170,7 @@
Relative
{result.relative}
-
+
diff --git a/src/lib/panelState.svelte.ts b/src/lib/panelState.svelte.ts
index e2bfc3f..9a6f006 100644
--- a/src/lib/panelState.svelte.ts
+++ b/src/lib/panelState.svelte.ts
@@ -26,10 +26,29 @@ export class ComparatorState {
focusPath = $state('');
isExpanded = $state(false);
+ toggleWatchKey(key: string) {
+ const current = new SvelteSet(
+ this.watchedKeysInput
+ .split(',')
+ .map((k) => k.trim())
+ .filter((k) => k.length > 0)
+ );
+ if (current.has(key)) {
+ current.delete(key);
+ } else {
+ current.add(key);
+ }
+ this.watchedKeysInput = Array.from(current).join(', ');
+ if (typeof localStorage !== 'undefined') {
+ localStorage.setItem(`watch_${this.selectedEndpoint}`, this.watchedKeysInput);
+ }
+ }
+
autoRefresh = $state(false);
refreshInterval = $state(5);
lastLoggedTime = $state(null);
refreshTimer: number | undefined = undefined;
+ numericTolerancePercent = $state(0);
}
export interface ProtobufFeedData {
diff --git a/src/lib/utils/jsonCompare.ts b/src/lib/utils/jsonCompare.ts
index f49ff50..e1ae49b 100644
--- a/src/lib/utils/jsonCompare.ts
+++ b/src/lib/utils/jsonCompare.ts
@@ -175,12 +175,24 @@ function scheduleCacheCleanup() {
}, 60000);
}
-export function deepEqualIgnoreOrder(a: unknown, b: unknown, ignoredKeys: string[] = []): boolean {
+export function deepEqualIgnoreOrder(
+ a: unknown,
+ b: unknown,
+ ignoredKeys: string[] = [],
+ numericTolerancePercent: number = 0
+): boolean {
if (Object.is(a, b)) return true;
if (typeof a !== typeof b) return false;
if (a === null || b === null) return a === b;
+ if (typeof a === 'number' && typeof b === 'number' && numericTolerancePercent > 0) {
+ const maxAbs = Math.max(Math.abs(a), Math.abs(b));
+ if (maxAbs === 0) return true;
+ const percentDiff = (Math.abs(a - b) / maxAbs) * 100;
+ if (percentDiff <= numericTolerancePercent) return true;
+ }
+
if (typeof a === 'object' && typeof b === 'object' && ignoredKeys.length === 0) {
const aCache = equalityCache.get(a as object);
if (aCache?.has(b as object)) {
@@ -195,9 +207,15 @@ export function deepEqualIgnoreOrder(a: unknown, b: unknown, ignoredKeys: string
result = false;
} else if (a.length === 0) {
result = true;
+ } else if (numericTolerancePercent > 0) {
+ const aSorted = [...a].sort(sortById);
+ const bSorted = [...b].sort(sortById);
+ result = aSorted.every((item, index) =>
+ deepEqualIgnoreOrder(item, bSorted[index], ignoredKeys, numericTolerancePercent)
+ );
} else {
- const aSorted = a.map((item) => stableStringify(item, ignoredKeys)).sort(sortById);
- const bSorted = b.map((item) => stableStringify(item, ignoredKeys)).sort(sortById);
+ const aSorted = [...a].sort(sortById).map((item) => stableStringify(item, ignoredKeys));
+ const bSorted = [...b].sort(sortById).map((item) => stableStringify(item, ignoredKeys));
result = aSorted.every((value, index) => value === bSorted[index]);
}
} else if (isObject(a) && isObject(b)) {
@@ -213,7 +231,9 @@ export function deepEqualIgnoreOrder(a: unknown, b: unknown, ignoredKeys: string
result = true;
} else {
result = aKeys.every(
- (key, index) => key === bKeys[index] && deepEqualIgnoreOrder(a[key], b[key], ignoredKeys)
+ (key, index) =>
+ key === bKeys[index] &&
+ deepEqualIgnoreOrder(a[key], b[key], ignoredKeys, numericTolerancePercent)
);
}
} else {
@@ -237,12 +257,15 @@ export function getDiffStatus(
value: unknown,
otherValue: unknown,
side: 'left' | 'right',
- ignoredKeys: string[] = []
+ ignoredKeys: string[] = [],
+ numericTolerancePercent: number = 0
): DiffStatus {
if (otherValue === undefined) {
return side === 'left' ? 'missing' : 'added';
}
- return deepEqualIgnoreOrder(value, otherValue, ignoredKeys) ? 'same' : 'different';
+ return deepEqualIgnoreOrder(value, otherValue, ignoredKeys, numericTolerancePercent)
+ ? 'same'
+ : 'different';
}
const MAX_DIFF_COUNT = 999;
@@ -251,12 +274,13 @@ export function countDifferences(
a: unknown,
b: unknown,
ignoredKeys: string[] = [],
- maxCount: number = MAX_DIFF_COUNT
+ maxCount: number = MAX_DIFF_COUNT,
+ numericTolerancePercent: number = 0
): number {
if (a === undefined && b === undefined) return 0;
if (a === undefined || b === undefined) return 1;
if (isPrimitive(a) || isPrimitive(b)) {
- return deepEqualIgnoreOrder(a, b, ignoredKeys) ? 0 : 1;
+ return deepEqualIgnoreOrder(a, b, ignoredKeys, numericTolerancePercent) ? 0 : 1;
}
if (isArray(a) && isArray(b)) {
@@ -269,7 +293,7 @@ export function countDifferences(
let sampleDiffs = 0;
for (let i = 0; i < sampleSize && i < a.length; i++) {
const idx = Math.floor((i / sampleSize) * a.length);
- if (!deepEqualIgnoreOrder(a[idx], b[idx], ignoredKeys)) {
+ if (!deepEqualIgnoreOrder(a[idx], b[idx], ignoredKeys, numericTolerancePercent)) {
sampleDiffs++;
}
}
@@ -312,11 +336,17 @@ export function countDifferences(
for (const key of keys) {
if (ignoredKeys.includes(key)) continue;
if (diff >= maxCount) break;
- diff += countDifferences(a[key], b[key], ignoredKeys, maxCount - diff);
+ diff += countDifferences(
+ a[key],
+ b[key],
+ ignoredKeys,
+ maxCount - diff,
+ numericTolerancePercent
+ );
}
return Math.min(diff, maxCount);
}
- return deepEqualIgnoreOrder(a, b, ignoredKeys) ? 0 : 1;
+ return deepEqualIgnoreOrder(a, b, ignoredKeys, numericTolerancePercent) ? 0 : 1;
}
export function sortEntries(obj: Record): [string, unknown][] {
diff --git a/src/lib/utils/search.ts b/src/lib/utils/search.ts
index dab8061..c984e1b 100644
--- a/src/lib/utils/search.ts
+++ b/src/lib/utils/search.ts
@@ -81,6 +81,7 @@ export interface SearchIndex {
vehicleIds: Map>;
tripIds: Map>;
routeIds: Map>;
+ stopIds: Map>;
labels: Map>;
}
@@ -92,6 +93,7 @@ export function buildSearchIndex(
vehicleIds: new Map(),
tripIds: new Map(),
routeIds: new Map(),
+ stopIds: new Map(),
labels: new Map()
};
@@ -114,6 +116,14 @@ export function buildSearchIndex(
addToIndex(index.routeIds, tripUpdate.trip?.routeId, idx);
addToIndex(index.vehicleIds, tripUpdate.vehicle?.id, idx);
addToIndex(index.labels, tripUpdate.vehicle?.label, idx);
+ const stopTimeUpdates = tripUpdate.stopTimeUpdate as
+ | Array>
+ | undefined;
+ if (stopTimeUpdates) {
+ stopTimeUpdates.forEach((stu) => {
+ addToIndex(index.stopIds, stu.stopId as string, idx);
+ });
+ }
} else if (type === 'vehiclePositions') {
const vehicle = e as GTFSVehiclePosition;
addToIndex(index.vehicleIds, vehicle.vehicle?.id, idx);
@@ -121,6 +131,7 @@ export function buildSearchIndex(
addToIndex(index.labels, vehicle.vehicle?.label, idx);
addToIndex(index.tripIds, vehicle.trip?.tripId, idx);
addToIndex(index.routeIds, vehicle.trip?.routeId, idx);
+ addToIndex(index.stopIds, (e as Record).stopId, idx);
} else if (type === 'alerts') {
const alert = e as GTFSAlert;
addToIndex(index.tripIds, alert.id, idx);
@@ -138,7 +149,13 @@ export function searchWithIndex(entities: unknown[], query: string, index: Searc
const lowerQuery = query.toLowerCase().trim();
const matchingIndices = new Set();
- for (const map of [index.vehicleIds, index.tripIds, index.routeIds, index.labels]) {
+ for (const map of [
+ index.vehicleIds,
+ index.tripIds,
+ index.routeIds,
+ index.stopIds,
+ index.labels
+ ]) {
for (const [key, indices] of map) {
if (key.includes(lowerQuery)) {
indices.forEach((idx) => matchingIndices.add(idx));
@@ -223,22 +240,34 @@ export function filterGTFSEntities(
if (type === 'tripUpdates') {
const tripUpdate = e as GTFSTripUpdate;
+ const stopTimeUpdates = tripUpdate.stopTimeUpdate as
+ | Array>
+ | undefined;
+ const hasStopIdMatch = stopTimeUpdates?.some((stu) =>
+ stu.stopId?.toLowerCase().includes(lowerQuery)
+ );
+ const tripRaw = tripUpdate.trip as Record | undefined;
return (
tripUpdate.id?.toLowerCase().includes(lowerQuery) ||
tripUpdate.trip?.tripId?.toLowerCase().includes(lowerQuery) ||
tripUpdate.trip?.routeId?.toLowerCase().includes(lowerQuery) ||
tripUpdate.trip?.scheduleRelationship?.toLowerCase().includes(lowerQuery) ||
tripUpdate.vehicle?.id?.toLowerCase().includes(lowerQuery) ||
- tripUpdate.vehicle?.label?.toLowerCase().includes(lowerQuery)
+ tripUpdate.vehicle?.label?.toLowerCase().includes(lowerQuery) ||
+ tripRaw?.stopId?.toLowerCase().includes(lowerQuery) ||
+ hasStopIdMatch
);
} else if (type === 'vehiclePositions') {
const vehicle = e as GTFSVehiclePosition;
+ const raw = e as Record;
return (
vehicle.id?.toLowerCase().includes(lowerQuery) ||
vehicle.vehicle?.id?.toLowerCase().includes(lowerQuery) ||
vehicle.vehicle?.label?.toLowerCase().includes(lowerQuery) ||
vehicle.trip?.tripId?.toLowerCase().includes(lowerQuery) ||
- vehicle.trip?.routeId?.toLowerCase().includes(lowerQuery)
+ vehicle.trip?.routeId?.toLowerCase().includes(lowerQuery) ||
+ raw.stopId?.toLowerCase().includes(lowerQuery) ||
+ (raw.trip as unknown as Record)?.stopId?.toLowerCase().includes(lowerQuery)
);
} else if (type === 'alerts') {
const alert = e as GTFSAlert;
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 346d526..876d80f 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -209,7 +209,7 @@
@@ -278,7 +278,7 @@
{#each leftPanelOptions as option (option.value)}
{option.label}
@@ -302,7 +302,7 @@
{#each rightPanelOptions as option (option.value)}
{option.label}
@@ -311,7 +311,7 @@
splitPaneRef?.resetSplit()}
- class="rounded-lg bg-indigo-100 p-2 text-indigo-600 transition-colors hover:bg-indigo-200 dark:bg-indigo-900 dark:text-indigo-300 dark:hover:bg-indigo-800"
+ class="rounded-lg bg-green-100 p-2 text-green-600 transition-colors hover:bg-green-200 dark:bg-green-900 dark:text-green-300 dark:hover:bg-green-800"
title="Reset split to 50/50"
>
@@ -381,7 +381,7 @@
(loggerSubTab = 'api')}
class="rounded-lg px-6 py-2 text-sm font-bold transition-all {loggerSubTab === 'api'
- ? 'bg-white text-indigo-600 shadow-sm dark:bg-gray-700 dark:text-indigo-400'
+ ? 'bg-white text-green-600 shadow-sm dark:bg-gray-700 dark:text-green-400'
: 'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'}"
>
API Response Logs
@@ -390,7 +390,7 @@
onclick={() => (loggerSubTab = 'gtfsrt')}
class="rounded-lg px-6 py-2 text-sm font-bold transition-all {loggerSubTab ===
'gtfsrt'
- ? 'bg-white text-indigo-600 shadow-sm dark:bg-gray-700 dark:text-indigo-400'
+ ? 'bg-white text-green-600 shadow-sm dark:bg-gray-700 dark:text-green-400'
: 'text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'}"
>
GTFS-RT Snapshots
From 628544316fd1d5eb7edb6924712854ac3101d60d Mon Sep 17 00:00:00 2001
From: Ahmedhossamdev
Date: Mon, 25 May 2026 05:31:27 +0300
Subject: [PATCH 2/3] feat: enhance KeyLogViewer with SvelteMap for improved
data handling and performance
---
src/lib/components/KeyLogViewer.svelte | 36 ++++++++++++--------------
1 file changed, 17 insertions(+), 19 deletions(-)
diff --git a/src/lib/components/KeyLogViewer.svelte b/src/lib/components/KeyLogViewer.svelte
index fb78f63..c76545c 100644
--- a/src/lib/components/KeyLogViewer.svelte
+++ b/src/lib/components/KeyLogViewer.svelte
@@ -1,7 +1,7 @@