@railtownai/railtracks-visualizer 0.0.67 → 0.0.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -16050,6 +16050,137 @@ const lightTheme = {
16050
16050
  slow: "0.3s ease-in-out"
16051
16051
  }
16052
16052
  };
16053
+ /** Light “Liquid Glass”–inspired palette (web approximation). */ const liquidGlassLightTheme = {
16054
+ colors: {
16055
+ background: "hsl(210 35% 92%)",
16056
+ foreground: "hsl(222 47% 11%)",
16057
+ card: "hsla(0 0% 100% / 0.88)",
16058
+ cardForeground: "hsl(222 47% 11%)",
16059
+ popover: "hsla(0 0% 100% / 0.94)",
16060
+ popoverForeground: "hsl(222 47% 11%)",
16061
+ primary: "hsl(211 92% 45%)",
16062
+ primaryForeground: "hsl(210 40% 98%)",
16063
+ secondary: "hsla(210 40% 98% / 0.45)",
16064
+ secondaryForeground: "hsl(222 47% 18%)",
16065
+ muted: "hsla(210 25% 98% / 0.35)",
16066
+ mutedForeground: "hsl(215 16% 40%)",
16067
+ accent: "hsla(210 60% 96% / 0.55)",
16068
+ accentForeground: "hsl(222 47% 18%)",
16069
+ destructive: "hsl(0 72% 51%)",
16070
+ destructiveForeground: "hsl(210 40% 98%)",
16071
+ success: "hsl(142.1 76.2% 32%)",
16072
+ successForeground: "hsl(210 40% 98%)",
16073
+ border: "hsla(210 28% 72% / 0.55)",
16074
+ mutedBorder: "hsla(210 22% 80% / 0.45)",
16075
+ input: "hsla(210 28% 88% / 0.65)",
16076
+ ring: "hsl(211 92% 45%)",
16077
+ tagTooluse: {
16078
+ bg: "hsla(199 89% 48% / 0.14)",
16079
+ border: "hsla(199 89% 48% / 0.35)",
16080
+ color: "hsl(200 98% 32%)"
16081
+ },
16082
+ tagLlminference: {
16083
+ bg: "hsla(271 91% 56% / 0.12)",
16084
+ border: "hsla(271 91% 56% / 0.35)",
16085
+ color: "hsl(271 81% 38%)"
16086
+ },
16087
+ tagJudge: {
16088
+ bg: "hsla(0 84% 60% / 0.12)",
16089
+ border: "hsla(0 84% 55% / 0.35)",
16090
+ color: "hsl(0 72% 40%)"
16091
+ },
16092
+ tagToolLevel1: {
16093
+ bg: "hsla(142 70% 42% / 0.15)",
16094
+ color: "hsl(142 71% 28%)"
16095
+ },
16096
+ tagToolLevel2: {
16097
+ bg: "hsla(217 91% 52% / 0.12)",
16098
+ color: "hsl(217 91% 38%)"
16099
+ },
16100
+ tagToolLevel3: {
16101
+ bg: "hsla(271 91% 56% / 0.12)",
16102
+ color: "hsl(271 81% 38%)"
16103
+ }
16104
+ },
16105
+ spacing: lightTheme.spacing,
16106
+ borderRadius: {
16107
+ sm: "calc(var(--radius) - 4px)",
16108
+ md: "calc(var(--radius) - 2px)",
16109
+ lg: "var(--radius)",
16110
+ xl: "1rem"
16111
+ },
16112
+ fontSize: lightTheme.fontSize,
16113
+ fontWeight: lightTheme.fontWeight,
16114
+ shadows: {
16115
+ sm: "0 1px 2px hsla(210 22% 25% / 0.06)",
16116
+ md: "0 8px 24px hsla(210 28% 18% / 0.08)",
16117
+ lg: "0 18px 48px hsla(210 28% 15% / 0.1)"
16118
+ },
16119
+ transitions: lightTheme.transitions
16120
+ };
16121
+ /** Dark “Liquid Glass”–inspired palette — translucent panels on a deep mesh-like base. */ const liquidGlassDarkTheme = {
16122
+ colors: {
16123
+ background: "hsl(222 28% 9%)",
16124
+ foreground: "hsl(210 36% 96%)",
16125
+ card: "hsla(222 22% 14% / 0.88)",
16126
+ cardForeground: "hsl(210 36% 96%)",
16127
+ popover: "hsla(222 24% 12% / 0.94)",
16128
+ popoverForeground: "hsl(210 36% 96%)",
16129
+ primary: "hsl(212 92% 62%)",
16130
+ primaryForeground: "hsl(222 47% 8%)",
16131
+ secondary: "hsla(217 28% 22% / 0.55)",
16132
+ secondaryForeground: "hsl(210 36% 94%)",
16133
+ muted: "hsla(217 22% 18% / 0.45)",
16134
+ mutedForeground: "hsl(215 18% 72%)",
16135
+ accent: "hsla(217 26% 22% / 0.55)",
16136
+ accentForeground: "hsl(210 36% 96%)",
16137
+ destructive: "hsl(0 72% 58%)",
16138
+ destructiveForeground: "hsl(210 40% 98%)",
16139
+ success: "hsl(142 76% 45%)",
16140
+ successForeground: "hsl(222 47% 8%)",
16141
+ border: "hsla(215 22% 42% / 0.45)",
16142
+ mutedBorder: "hsla(222 18% 28% / 0.5)",
16143
+ input: "hsla(217 22% 22% / 0.65)",
16144
+ ring: "hsl(212 92% 62%)",
16145
+ tagTooluse: {
16146
+ bg: "hsla(217 91% 55% / 0.18)",
16147
+ border: "hsla(217 91% 45% / 0.35)",
16148
+ color: "hsl(199 89% 72%)"
16149
+ },
16150
+ tagLlminference: {
16151
+ bg: "hsla(271 91% 58% / 0.15)",
16152
+ border: "hsla(271 91% 48% / 0.35)",
16153
+ color: "hsl(271 91% 78%)"
16154
+ },
16155
+ tagJudge: {
16156
+ bg: "hsla(0 72% 48% / 0.18)",
16157
+ border: "hsla(0 72% 42% / 0.35)",
16158
+ color: "hsl(0 84% 72%)"
16159
+ },
16160
+ tagToolLevel1: {
16161
+ bg: "hsla(142 70% 42% / 0.22)",
16162
+ color: "hsl(142 70% 72%)"
16163
+ },
16164
+ tagToolLevel2: {
16165
+ bg: "hsla(217 91% 52% / 0.2)",
16166
+ color: "hsl(217 91% 76%)"
16167
+ },
16168
+ tagToolLevel3: {
16169
+ bg: "hsla(271 91% 56% / 0.2)",
16170
+ color: "hsl(271 91% 76%)"
16171
+ }
16172
+ },
16173
+ spacing: lightTheme.spacing,
16174
+ borderRadius: liquidGlassLightTheme.borderRadius,
16175
+ fontSize: lightTheme.fontSize,
16176
+ fontWeight: lightTheme.fontWeight,
16177
+ shadows: {
16178
+ sm: "0 1px 2px hsla(0 0% 0% / 0.35)",
16179
+ md: "0 10px 28px hsla(0 0% 0% / 0.35)",
16180
+ lg: "0 22px 52px hsla(0 0% 0% / 0.45)"
16181
+ },
16182
+ transitions: lightTheme.transitions
16183
+ };
16053
16184
  const darkTheme = {
16054
16185
  colors: {
16055
16186
  background: "hsl(222.2 84% 4.9%)",
@@ -16111,81 +16242,124 @@ const darkTheme = {
16111
16242
  };
16112
16243
  const defaultTheme$1 = lightTheme;
16113
16244
 
16245
+ function normalizeStoredTheme(raw) {
16246
+ if (raw === "dark") return "dark";
16247
+ if (raw === "liquidGlassDark") return "liquidGlassDark";
16248
+ if (raw === "liquidGlassLight") return "liquidGlassLight";
16249
+ /* Migrate legacy single “Liquid Glass” preset */ if (raw === "liquidGlass") return "liquidGlassLight";
16250
+ return "light";
16251
+ }
16252
+ function themeForMode(mode) {
16253
+ switch(mode){
16254
+ case "dark":
16255
+ return darkTheme;
16256
+ case "liquidGlassLight":
16257
+ return liquidGlassLightTheme;
16258
+ case "liquidGlassDark":
16259
+ return liquidGlassDarkTheme;
16260
+ default:
16261
+ return lightTheme;
16262
+ }
16263
+ }
16264
+ function themeToMode(theme) {
16265
+ if (theme === darkTheme) return "dark";
16266
+ if (theme === liquidGlassDarkTheme) return "liquidGlassDark";
16267
+ if (theme === liquidGlassLightTheme) return "liquidGlassLight";
16268
+ return "light";
16269
+ }
16114
16270
  const ThemeContext = /*#__PURE__*/ React.createContext(undefined);
16115
16271
  const ThemeProvider = ({ children, theme: propTheme })=>{
16116
- const [currentTheme, setCurrentTheme] = React.useState(()=>{
16117
- // If a theme prop is provided, use it immediately
16272
+ const [themeMode, setThemeModeState] = React.useState(()=>{
16118
16273
  if (propTheme) {
16119
- return propTheme;
16274
+ return themeToMode(propTheme);
16120
16275
  }
16121
- // Only check localStorage if no theme prop is provided
16122
- // Handle cases where localStorage might not be available (e.g., test environments)
16123
16276
  try {
16124
- const savedTheme = localStorage.getItem("theme");
16125
- if (savedTheme === "dark") {
16126
- return darkTheme;
16127
- }
16128
- } catch (e) {
16129
- // localStorage not available, fall back to light theme
16130
- // This can happen in test environments or when localStorage is disabled
16131
- }
16132
- return lightTheme;
16277
+ return normalizeStoredTheme(localStorage.getItem("theme"));
16278
+ } catch {
16279
+ /* localStorage unavailable */ }
16280
+ return "light";
16133
16281
  });
16134
- // Update theme when propTheme changes
16282
+ const currentTheme = React.useMemo(()=>themeForMode(themeMode), [
16283
+ themeMode
16284
+ ]);
16135
16285
  React.useEffect(()=>{
16136
16286
  if (propTheme) {
16137
- setCurrentTheme(propTheme);
16287
+ setThemeModeState(themeToMode(propTheme));
16138
16288
  }
16139
16289
  }, [
16140
16290
  propTheme
16141
16291
  ]);
16142
- // Sync html.dark class for GlobalStyles and other CSS that uses html.dark
16143
16292
  React.useEffect(()=>{
16144
- const isDark = currentTheme === darkTheme;
16145
16293
  try {
16146
- if (isDark) {
16147
- document.documentElement.classList.add("dark");
16148
- } else {
16149
- document.documentElement.classList.remove("dark");
16150
- }
16151
- } catch (e) {
16152
- // document might not be available (e.g. SSR)
16153
- }
16294
+ const isClassicDark = themeMode === "dark";
16295
+ const isGlassLight = themeMode === "liquidGlassLight";
16296
+ const isGlassDark = themeMode === "liquidGlassDark";
16297
+ document.documentElement.classList.toggle("dark", isClassicDark);
16298
+ document.documentElement.classList.toggle("liquid-glass-light", isGlassLight);
16299
+ document.documentElement.classList.toggle("liquid-glass-dark", isGlassDark);
16300
+ /* Remove legacy class if present */ document.documentElement.classList.remove("liquid-glass");
16301
+ document.documentElement.dataset.theme = isClassicDark ? "dark" : isGlassLight ? "liquid-glass-light" : isGlassDark ? "liquid-glass-dark" : "light";
16302
+ } catch {
16303
+ /* document unavailable */ }
16154
16304
  }, [
16155
- currentTheme
16305
+ themeMode
16306
+ ]);
16307
+ React.useEffect(()=>{
16308
+ if (propTheme) return;
16309
+ try {
16310
+ localStorage.setItem("theme", themeMode);
16311
+ } catch {
16312
+ /* ignore */ }
16313
+ }, [
16314
+ themeMode,
16315
+ propTheme
16156
16316
  ]);
16157
- // Only listen for storage events if no theme prop is provided
16158
16317
  React.useEffect(()=>{
16159
16318
  if (propTheme) {
16160
- // If a theme prop is provided, don't listen for localStorage changes
16161
16319
  return;
16162
16320
  }
16163
16321
  const handleStorageChange = ()=>{
16164
16322
  try {
16165
- const savedTheme = localStorage.getItem("theme");
16166
- if (savedTheme === "dark") {
16167
- setCurrentTheme(darkTheme);
16168
- } else {
16169
- setCurrentTheme(lightTheme);
16170
- }
16171
- } catch (e) {
16172
- // localStorage not available, ignore storage changes
16173
- }
16323
+ setThemeModeState(normalizeStoredTheme(localStorage.getItem("theme")));
16324
+ } catch {
16325
+ /* ignore */ }
16174
16326
  };
16175
- // Listen for storage events (when localStorage changes in other tabs)
16176
16327
  window.addEventListener("storage", handleStorageChange);
16177
- return ()=>{
16178
- window.removeEventListener("storage", handleStorageChange);
16179
- };
16328
+ return ()=>window.removeEventListener("storage", handleStorageChange);
16180
16329
  }, [
16181
16330
  propTheme
16182
16331
  ]);
16332
+ const setThemeMode = React.useCallback((mode)=>{
16333
+ setThemeModeState(mode);
16334
+ }, []);
16335
+ const setTheme = React.useCallback((next)=>{
16336
+ setThemeModeState(themeToMode(next));
16337
+ }, []);
16338
+ const isLiquidGlassLight = themeMode === "liquidGlassLight";
16339
+ const isLiquidGlassDark = themeMode === "liquidGlassDark";
16340
+ const isLiquidGlass = isLiquidGlassLight || isLiquidGlassDark;
16341
+ const isClassicDarkTheme = themeMode === "dark";
16342
+ const isDarkMode = isClassicDarkTheme || isLiquidGlassDark;
16183
16343
  const value = React.useMemo(()=>({
16184
16344
  theme: currentTheme,
16185
- setTheme: setCurrentTheme,
16186
- isDarkMode: currentTheme === darkTheme
16345
+ themeMode,
16346
+ setTheme,
16347
+ setThemeMode,
16348
+ isDarkMode,
16349
+ isClassicDarkTheme,
16350
+ isLiquidGlass,
16351
+ isLiquidGlassLight,
16352
+ isLiquidGlassDark
16187
16353
  }), [
16188
- currentTheme
16354
+ currentTheme,
16355
+ themeMode,
16356
+ setTheme,
16357
+ setThemeMode,
16358
+ isDarkMode,
16359
+ isClassicDarkTheme,
16360
+ isLiquidGlass,
16361
+ isLiquidGlassLight,
16362
+ isLiquidGlassDark
16189
16363
  ]);
16190
16364
  return /*#__PURE__*/ React.createElement(ThemeContext.Provider, {
16191
16365
  value: value
@@ -16196,14 +16370,20 @@ const ThemeProvider = ({ children, theme: propTheme })=>{
16196
16370
  const useTheme$1 = ()=>{
16197
16371
  const context = React.useContext(ThemeContext);
16198
16372
  if (context === undefined) {
16199
- // Return a graceful fallback instead of throwing an error
16200
16373
  return {
16201
16374
  theme: lightTheme,
16375
+ themeMode: "light",
16202
16376
  setTheme: ()=>{
16203
- // No-op function when context is not available
16204
16377
  console.warn("Theme context not available. setTheme called but ignored.");
16205
16378
  },
16206
- isDarkMode: false // Default to light mode when no context
16379
+ setThemeMode: ()=>{
16380
+ console.warn("Theme context not available. setThemeMode called but ignored.");
16381
+ },
16382
+ isDarkMode: false,
16383
+ isClassicDarkTheme: false,
16384
+ isLiquidGlass: false,
16385
+ isLiquidGlassLight: false,
16386
+ isLiquidGlassDark: false
16207
16387
  };
16208
16388
  }
16209
16389
  return context;
@@ -16212,7 +16392,6 @@ const useThemeValue = ()=>{
16212
16392
  const { theme } = useTheme$1();
16213
16393
  return theme;
16214
16394
  };
16215
- // Helper hook for components that can accept a theme prop
16216
16395
  const useComponentTheme = (themeProp)=>{
16217
16396
  const { theme: contextTheme } = useTheme$1();
16218
16397
  return themeProp || contextTheme;
@@ -48618,7 +48797,7 @@ const CountUpLatency = ({ value })=>{
48618
48797
  };
48619
48798
  }
48620
48799
  const NodeDetailsDrawer = ({ isVisible, onClose, nodeData })=>{
48621
- const { theme } = useTheme$1();
48800
+ const { theme, isLiquidGlass } = useTheme$1();
48622
48801
  const [selectedRowIndex, setSelectedRowIndex] = React.useState(null);
48623
48802
  const [selectedLlmDetails, setSelectedLlmDetails] = React.useState(null);
48624
48803
  // Default to last row (total/aggregate) when drawer opens (only on first open)
@@ -48692,11 +48871,11 @@ const NodeDetailsDrawer = ({ isVisible, onClose, nodeData })=>{
48692
48871
  "data-testid": "node-details-drawer",
48693
48872
  styles: {
48694
48873
  body: {
48695
- background: theme.colors.background,
48874
+ background: isLiquidGlass ? theme.colors.popover : theme.colors.background,
48696
48875
  color: theme.colors.foreground
48697
48876
  },
48698
48877
  header: {
48699
- background: theme.colors.card,
48878
+ background: isLiquidGlass ? theme.colors.popover : theme.colors.card,
48700
48879
  borderBottom: `1px solid ${theme.colors.border}`
48701
48880
  }
48702
48881
  }
@@ -52057,8 +52236,7 @@ const SheetOverlay = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...
52057
52236
  bottom: 0,
52058
52237
  left: 0,
52059
52238
  zIndex: 50,
52060
- backgroundColor: "hsla(var(--background), 0.8)",
52061
- backdropFilter: "blur(4px)"
52239
+ backgroundColor: "rgba(0, 0, 0, 0.45)"
52062
52240
  },
52063
52241
  className: className,
52064
52242
  ...props,
@@ -52070,7 +52248,7 @@ const getSheetStyles = (side = "right")=>{
52070
52248
  position: "fixed",
52071
52249
  zIndex: 50,
52072
52250
  gap: "16px",
52073
- backgroundColor: "hsl(var(--background))",
52251
+ backgroundColor: "var(--background)",
52074
52252
  padding: "24px",
52075
52253
  boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
52076
52254
  transition: "all 0.3s ease-in-out"
@@ -52082,7 +52260,7 @@ const getSheetStyles = (side = "right")=>{
52082
52260
  left: 0,
52083
52261
  right: 0,
52084
52262
  top: 0,
52085
- borderBottom: "1px solid hsl(var(--border))"
52263
+ borderBottom: "1px solid var(--border)"
52086
52264
  };
52087
52265
  case "bottom":
52088
52266
  return {
@@ -52090,7 +52268,7 @@ const getSheetStyles = (side = "right")=>{
52090
52268
  left: 0,
52091
52269
  right: 0,
52092
52270
  bottom: 0,
52093
- borderTop: "1px solid hsl(var(--border))"
52271
+ borderTop: "1px solid var(--border)"
52094
52272
  };
52095
52273
  case "left":
52096
52274
  return {
@@ -52100,7 +52278,7 @@ const getSheetStyles = (side = "right")=>{
52100
52278
  left: 0,
52101
52279
  height: "100%",
52102
52280
  width: "75%",
52103
- borderRight: "1px solid hsl(var(--border))",
52281
+ borderRight: "1px solid var(--border)",
52104
52282
  maxWidth: "384px"
52105
52283
  };
52106
52284
  case "right":
@@ -52112,7 +52290,7 @@ const getSheetStyles = (side = "right")=>{
52112
52290
  right: 0,
52113
52291
  height: "100%",
52114
52292
  width: "75%",
52115
- borderLeft: "1px solid hsl(var(--border))",
52293
+ borderLeft: "1px solid var(--border)",
52116
52294
  maxWidth: "384px"
52117
52295
  };
52118
52296
  }
@@ -52173,7 +52351,7 @@ const SheetTitle = /*#__PURE__*/ React__namespace.forwardRef(({ className, ...pr
52173
52351
  style: {
52174
52352
  fontSize: "18px",
52175
52353
  fontWeight: 600,
52176
- color: "hsl(var(--foreground))"
52354
+ color: "var(--foreground)"
52177
52355
  },
52178
52356
  className: className,
52179
52357
  ...props
@@ -52183,7 +52361,7 @@ const SheetDescription = /*#__PURE__*/ React__namespace.forwardRef(({ className,
52183
52361
  ref: ref,
52184
52362
  style: {
52185
52363
  fontSize: "14px",
52186
- color: "hsl(var(--muted-foreground))"
52364
+ color: "var(--muted-foreground)"
52187
52365
  },
52188
52366
  className: className,
52189
52367
  ...props
@@ -116350,7 +116528,18 @@ const useEvaluationsMock = ()=>{
116350
116528
  return React.useContext(EvaluationsMockContext);
116351
116529
  };
116352
116530
 
116353
- const API_BASE = "/api";
116531
+ /**
116532
+ * Railtracks HTTP API prefix. Same-origin `/api` by default (browser + viz proxy).
116533
+ * Set `VITE_API_ORIGIN` (e.g. `http://localhost:3030`) when the UI is served elsewhere (Electron, etc.).
116534
+ */ function resolveApiBase() {
116535
+ const origin = undefined.VITE_API_ORIGIN?.trim().replace(/\/$/, "") ?? "";
116536
+ if (!origin) {
116537
+ return "/api";
116538
+ }
116539
+ return `${origin}/api`;
116540
+ }
116541
+ const API_BASE = resolveApiBase();
116542
+
116354
116543
  const useEvaluations = ()=>{
116355
116544
  const mockContext = useEvaluationsMock();
116356
116545
  const [evaluations, setEvaluations] = React.useState([]);
@@ -120096,6 +120285,166 @@ const GlobalStyles = ()=>{
120096
120285
  --ring: #6366f1;
120097
120286
  --radius: 0.5rem;
120098
120287
  }
120288
+
120289
+ html.liquid-glass-light {
120290
+ color: hsl(222 47% 11%);
120291
+ }
120292
+
120293
+ html.liquid-glass-light::before {
120294
+ content: "";
120295
+ position: fixed;
120296
+ inset: 0;
120297
+ z-index: -1;
120298
+ pointer-events: none;
120299
+ background:
120300
+ radial-gradient(120% 90% at 10% 10%, hsl(210 55% 92%) 0%, transparent 52%),
120301
+ radial-gradient(90% 70% at 90% 20%, hsl(220 45% 88%) 0%, transparent 45%),
120302
+ radial-gradient(70% 60% at 50% 100%, hsl(200 35% 88%) 0%, transparent 50%), hsl(210 30% 91%);
120303
+ }
120304
+
120305
+ html.liquid-glass-light body {
120306
+ background-color: hsl(210 30% 91%);
120307
+ color: hsl(222 47% 11%);
120308
+ }
120309
+
120310
+ html.liquid-glass-light .react-flow {
120311
+ contain: paint;
120312
+ }
120313
+
120314
+ html.liquid-glass-light .react-flow__edge-label {
120315
+ background: hsla(0 0% 100% / 0.92);
120316
+ border: 1px solid hsla(210 28% 72% / 0.55);
120317
+ color: hsl(222 47% 11%);
120318
+ }
120319
+
120320
+ html.liquid-glass-light .file-selector-refresh-btn {
120321
+ border: 1px solid hsla(210 28% 72% / 0.55);
120322
+ background-color: hsla(0 0% 100% / 0.88);
120323
+ color: hsl(222 47% 11%);
120324
+ }
120325
+
120326
+ html.liquid-glass-light .file-selector-refresh-btn:hover {
120327
+ background-color: hsla(0 0% 100% / 0.96);
120328
+ color: hsl(222 47% 14%);
120329
+ }
120330
+
120331
+ html.liquid-glass-light .ant-table-tbody > tr.evaluations-compare-diff-row > td {
120332
+ background-color: hsla(48 96% 53% / 0.22) !important;
120333
+ }
120334
+
120335
+ html.liquid-glass-light {
120336
+ --background: hsl(210 30% 91%);
120337
+ --foreground: hsl(222 47% 11%);
120338
+ --card: hsla(0 0% 100% / 0.88);
120339
+ --card-foreground: hsl(222 47% 11%);
120340
+ --popover: hsla(0 0% 100% / 0.94);
120341
+ --popover-foreground: hsl(222 47% 11%);
120342
+ --primary: hsl(211 92% 45%);
120343
+ --primary-foreground: hsl(210 40% 98%);
120344
+ --secondary: hsla(210 40% 98% / 0.45);
120345
+ --secondary-foreground: hsl(222 47% 18%);
120346
+ --muted: hsla(210 25% 98% / 0.35);
120347
+ --muted-foreground: hsl(215 16% 40%);
120348
+ --accent: hsla(210 60% 96% / 0.55);
120349
+ --accent-foreground: hsl(222 47% 18%);
120350
+ --destructive: hsl(0 72% 51%);
120351
+ --destructive-foreground: hsl(210 40% 98%);
120352
+ --border: hsla(210 28% 72% / 0.55);
120353
+ --input: hsla(210 28% 88% / 0.65);
120354
+ --ring: hsl(211 92% 45%);
120355
+ --radius: 0.75rem;
120356
+ }
120357
+
120358
+ html.liquid-glass-dark {
120359
+ color: hsl(210 36% 96%);
120360
+ }
120361
+
120362
+ html.liquid-glass-dark::before {
120363
+ content: "";
120364
+ position: fixed;
120365
+ inset: 0;
120366
+ z-index: -1;
120367
+ pointer-events: none;
120368
+ background:
120369
+ radial-gradient(130% 100% at 20% 0%, hsl(222 42% 14%) 0%, transparent 55%),
120370
+ radial-gradient(90% 80% at 95% 30%, hsl(232 38% 18%) 0%, transparent 45%),
120371
+ radial-gradient(80% 70% at 50% 100%, hsl(215 36% 12%) 0%, transparent 50%), hsl(222 28% 9%);
120372
+ }
120373
+
120374
+ html.liquid-glass-dark body {
120375
+ background-color: hsl(222 28% 9%);
120376
+ color: hsl(210 36% 96%);
120377
+ }
120378
+
120379
+ html.liquid-glass-dark .react-flow {
120380
+ contain: paint;
120381
+ }
120382
+
120383
+ html.liquid-glass-dark .react-flow__edge-label {
120384
+ background: hsla(222 22% 18% / 0.92);
120385
+ border: 1px solid hsla(215 22% 42% / 0.45);
120386
+ color: hsl(210 36% 96%);
120387
+ }
120388
+
120389
+ html.liquid-glass-dark .file-selector-refresh-btn {
120390
+ border: 1px solid hsla(215 22% 42% / 0.45);
120391
+ background-color: hsla(222 22% 14% / 0.88);
120392
+ color: hsl(210 36% 96%);
120393
+ }
120394
+
120395
+ html.liquid-glass-dark .file-selector-refresh-btn:hover {
120396
+ background-color: hsla(222 22% 18% / 0.96);
120397
+ color: hsl(210 40% 98%);
120398
+ }
120399
+
120400
+ html.liquid-glass-dark .ant-table-tbody > tr.evaluations-compare-diff-row > td {
120401
+ background-color: hsla(217 32% 28% / 0.55) !important;
120402
+ }
120403
+
120404
+ html.liquid-glass-dark {
120405
+ --background: hsl(222 28% 9%);
120406
+ --foreground: hsl(210 36% 96%);
120407
+ --card: hsla(222 22% 14% / 0.88);
120408
+ --card-foreground: hsl(210 36% 96%);
120409
+ --popover: hsla(222 24% 12% / 0.94);
120410
+ --popover-foreground: hsl(210 36% 96%);
120411
+ --primary: hsl(212 92% 62%);
120412
+ --primary-foreground: hsl(222 47% 8%);
120413
+ --secondary: hsla(217 28% 22% / 0.55);
120414
+ --secondary-foreground: hsl(210 36% 94%);
120415
+ --muted: hsla(217 22% 18% / 0.45);
120416
+ --muted-foreground: hsl(215 18% 72%);
120417
+ --accent: hsla(217 26% 22% / 0.55);
120418
+ --accent-foreground: hsl(210 36% 96%);
120419
+ --destructive: hsl(0 72% 58%);
120420
+ --destructive-foreground: hsl(210 40% 98%);
120421
+ --border: hsla(215 22% 42% / 0.45);
120422
+ --input: hsla(217 22% 22% / 0.65);
120423
+ --ring: hsl(212 92% 62%);
120424
+ --radius: 0.75rem;
120425
+ }
120426
+
120427
+ @media (prefers-reduced-transparency: reduce) {
120428
+ html.liquid-glass-light::before,
120429
+ html.liquid-glass-dark::before {
120430
+ display: none;
120431
+ }
120432
+
120433
+ html.liquid-glass-light body {
120434
+ background-color: hsl(210 30% 91%);
120435
+ }
120436
+
120437
+ html.liquid-glass-dark body {
120438
+ background-color: hsl(222 28% 9%);
120439
+ }
120440
+
120441
+ html.liquid-glass-light .react-flow__edge-label,
120442
+ html.liquid-glass-dark .react-flow__edge-label,
120443
+ html.liquid-glass-light .file-selector-refresh-btn,
120444
+ html.liquid-glass-dark .file-selector-refresh-btn {
120445
+ background-color: var(--card);
120446
+ }
120447
+ }
120099
120448
  `
120100
120449
  });
120101
120450
  };
@@ -120290,10 +120639,14 @@ exports.getStatusColor = getStatusColor;
120290
120639
  exports.isEvaluationDto = isEvaluationDto;
120291
120640
  exports.isPlainObject = isPlainObject;
120292
120641
  exports.lightTheme = lightTheme;
120642
+ exports.liquidGlassDarkTheme = liquidGlassDarkTheme;
120643
+ exports.liquidGlassLightTheme = liquidGlassLightTheme;
120293
120644
  exports.safeStyled = safeStyled;
120294
120645
  exports.safeThemeColor = safeThemeColor;
120295
120646
  exports.safeThemeSpacing = safeThemeSpacing;
120296
120647
  exports.safeThemeValue = safeThemeValue;
120648
+ exports.themeForMode = themeForMode;
120649
+ exports.themeToMode = themeToMode;
120297
120650
  exports.toTitleCase = toTitleCase;
120298
120651
  exports.transformEvaluation = transformEvaluation;
120299
120652
  exports.truncateText = truncateText;
package/dist/esm/index.js CHANGED
@@ -16030,6 +16030,137 @@ const lightTheme = {
16030
16030
  slow: "0.3s ease-in-out"
16031
16031
  }
16032
16032
  };
16033
+ /** Light “Liquid Glass”–inspired palette (web approximation). */ const liquidGlassLightTheme = {
16034
+ colors: {
16035
+ background: "hsl(210 35% 92%)",
16036
+ foreground: "hsl(222 47% 11%)",
16037
+ card: "hsla(0 0% 100% / 0.88)",
16038
+ cardForeground: "hsl(222 47% 11%)",
16039
+ popover: "hsla(0 0% 100% / 0.94)",
16040
+ popoverForeground: "hsl(222 47% 11%)",
16041
+ primary: "hsl(211 92% 45%)",
16042
+ primaryForeground: "hsl(210 40% 98%)",
16043
+ secondary: "hsla(210 40% 98% / 0.45)",
16044
+ secondaryForeground: "hsl(222 47% 18%)",
16045
+ muted: "hsla(210 25% 98% / 0.35)",
16046
+ mutedForeground: "hsl(215 16% 40%)",
16047
+ accent: "hsla(210 60% 96% / 0.55)",
16048
+ accentForeground: "hsl(222 47% 18%)",
16049
+ destructive: "hsl(0 72% 51%)",
16050
+ destructiveForeground: "hsl(210 40% 98%)",
16051
+ success: "hsl(142.1 76.2% 32%)",
16052
+ successForeground: "hsl(210 40% 98%)",
16053
+ border: "hsla(210 28% 72% / 0.55)",
16054
+ mutedBorder: "hsla(210 22% 80% / 0.45)",
16055
+ input: "hsla(210 28% 88% / 0.65)",
16056
+ ring: "hsl(211 92% 45%)",
16057
+ tagTooluse: {
16058
+ bg: "hsla(199 89% 48% / 0.14)",
16059
+ border: "hsla(199 89% 48% / 0.35)",
16060
+ color: "hsl(200 98% 32%)"
16061
+ },
16062
+ tagLlminference: {
16063
+ bg: "hsla(271 91% 56% / 0.12)",
16064
+ border: "hsla(271 91% 56% / 0.35)",
16065
+ color: "hsl(271 81% 38%)"
16066
+ },
16067
+ tagJudge: {
16068
+ bg: "hsla(0 84% 60% / 0.12)",
16069
+ border: "hsla(0 84% 55% / 0.35)",
16070
+ color: "hsl(0 72% 40%)"
16071
+ },
16072
+ tagToolLevel1: {
16073
+ bg: "hsla(142 70% 42% / 0.15)",
16074
+ color: "hsl(142 71% 28%)"
16075
+ },
16076
+ tagToolLevel2: {
16077
+ bg: "hsla(217 91% 52% / 0.12)",
16078
+ color: "hsl(217 91% 38%)"
16079
+ },
16080
+ tagToolLevel3: {
16081
+ bg: "hsla(271 91% 56% / 0.12)",
16082
+ color: "hsl(271 81% 38%)"
16083
+ }
16084
+ },
16085
+ spacing: lightTheme.spacing,
16086
+ borderRadius: {
16087
+ sm: "calc(var(--radius) - 4px)",
16088
+ md: "calc(var(--radius) - 2px)",
16089
+ lg: "var(--radius)",
16090
+ xl: "1rem"
16091
+ },
16092
+ fontSize: lightTheme.fontSize,
16093
+ fontWeight: lightTheme.fontWeight,
16094
+ shadows: {
16095
+ sm: "0 1px 2px hsla(210 22% 25% / 0.06)",
16096
+ md: "0 8px 24px hsla(210 28% 18% / 0.08)",
16097
+ lg: "0 18px 48px hsla(210 28% 15% / 0.1)"
16098
+ },
16099
+ transitions: lightTheme.transitions
16100
+ };
16101
+ /** Dark “Liquid Glass”–inspired palette — translucent panels on a deep mesh-like base. */ const liquidGlassDarkTheme = {
16102
+ colors: {
16103
+ background: "hsl(222 28% 9%)",
16104
+ foreground: "hsl(210 36% 96%)",
16105
+ card: "hsla(222 22% 14% / 0.88)",
16106
+ cardForeground: "hsl(210 36% 96%)",
16107
+ popover: "hsla(222 24% 12% / 0.94)",
16108
+ popoverForeground: "hsl(210 36% 96%)",
16109
+ primary: "hsl(212 92% 62%)",
16110
+ primaryForeground: "hsl(222 47% 8%)",
16111
+ secondary: "hsla(217 28% 22% / 0.55)",
16112
+ secondaryForeground: "hsl(210 36% 94%)",
16113
+ muted: "hsla(217 22% 18% / 0.45)",
16114
+ mutedForeground: "hsl(215 18% 72%)",
16115
+ accent: "hsla(217 26% 22% / 0.55)",
16116
+ accentForeground: "hsl(210 36% 96%)",
16117
+ destructive: "hsl(0 72% 58%)",
16118
+ destructiveForeground: "hsl(210 40% 98%)",
16119
+ success: "hsl(142 76% 45%)",
16120
+ successForeground: "hsl(222 47% 8%)",
16121
+ border: "hsla(215 22% 42% / 0.45)",
16122
+ mutedBorder: "hsla(222 18% 28% / 0.5)",
16123
+ input: "hsla(217 22% 22% / 0.65)",
16124
+ ring: "hsl(212 92% 62%)",
16125
+ tagTooluse: {
16126
+ bg: "hsla(217 91% 55% / 0.18)",
16127
+ border: "hsla(217 91% 45% / 0.35)",
16128
+ color: "hsl(199 89% 72%)"
16129
+ },
16130
+ tagLlminference: {
16131
+ bg: "hsla(271 91% 58% / 0.15)",
16132
+ border: "hsla(271 91% 48% / 0.35)",
16133
+ color: "hsl(271 91% 78%)"
16134
+ },
16135
+ tagJudge: {
16136
+ bg: "hsla(0 72% 48% / 0.18)",
16137
+ border: "hsla(0 72% 42% / 0.35)",
16138
+ color: "hsl(0 84% 72%)"
16139
+ },
16140
+ tagToolLevel1: {
16141
+ bg: "hsla(142 70% 42% / 0.22)",
16142
+ color: "hsl(142 70% 72%)"
16143
+ },
16144
+ tagToolLevel2: {
16145
+ bg: "hsla(217 91% 52% / 0.2)",
16146
+ color: "hsl(217 91% 76%)"
16147
+ },
16148
+ tagToolLevel3: {
16149
+ bg: "hsla(271 91% 56% / 0.2)",
16150
+ color: "hsl(271 91% 76%)"
16151
+ }
16152
+ },
16153
+ spacing: lightTheme.spacing,
16154
+ borderRadius: liquidGlassLightTheme.borderRadius,
16155
+ fontSize: lightTheme.fontSize,
16156
+ fontWeight: lightTheme.fontWeight,
16157
+ shadows: {
16158
+ sm: "0 1px 2px hsla(0 0% 0% / 0.35)",
16159
+ md: "0 10px 28px hsla(0 0% 0% / 0.35)",
16160
+ lg: "0 22px 52px hsla(0 0% 0% / 0.45)"
16161
+ },
16162
+ transitions: lightTheme.transitions
16163
+ };
16033
16164
  const darkTheme = {
16034
16165
  colors: {
16035
16166
  background: "hsl(222.2 84% 4.9%)",
@@ -16091,81 +16222,124 @@ const darkTheme = {
16091
16222
  };
16092
16223
  const defaultTheme$1 = lightTheme;
16093
16224
 
16225
+ function normalizeStoredTheme(raw) {
16226
+ if (raw === "dark") return "dark";
16227
+ if (raw === "liquidGlassDark") return "liquidGlassDark";
16228
+ if (raw === "liquidGlassLight") return "liquidGlassLight";
16229
+ /* Migrate legacy single “Liquid Glass” preset */ if (raw === "liquidGlass") return "liquidGlassLight";
16230
+ return "light";
16231
+ }
16232
+ function themeForMode(mode) {
16233
+ switch(mode){
16234
+ case "dark":
16235
+ return darkTheme;
16236
+ case "liquidGlassLight":
16237
+ return liquidGlassLightTheme;
16238
+ case "liquidGlassDark":
16239
+ return liquidGlassDarkTheme;
16240
+ default:
16241
+ return lightTheme;
16242
+ }
16243
+ }
16244
+ function themeToMode(theme) {
16245
+ if (theme === darkTheme) return "dark";
16246
+ if (theme === liquidGlassDarkTheme) return "liquidGlassDark";
16247
+ if (theme === liquidGlassLightTheme) return "liquidGlassLight";
16248
+ return "light";
16249
+ }
16094
16250
  const ThemeContext = /*#__PURE__*/ createContext(undefined);
16095
16251
  const ThemeProvider = ({ children, theme: propTheme })=>{
16096
- const [currentTheme, setCurrentTheme] = React__default.useState(()=>{
16097
- // If a theme prop is provided, use it immediately
16252
+ const [themeMode, setThemeModeState] = React__default.useState(()=>{
16098
16253
  if (propTheme) {
16099
- return propTheme;
16254
+ return themeToMode(propTheme);
16100
16255
  }
16101
- // Only check localStorage if no theme prop is provided
16102
- // Handle cases where localStorage might not be available (e.g., test environments)
16103
16256
  try {
16104
- const savedTheme = localStorage.getItem("theme");
16105
- if (savedTheme === "dark") {
16106
- return darkTheme;
16107
- }
16108
- } catch (e) {
16109
- // localStorage not available, fall back to light theme
16110
- // This can happen in test environments or when localStorage is disabled
16111
- }
16112
- return lightTheme;
16257
+ return normalizeStoredTheme(localStorage.getItem("theme"));
16258
+ } catch {
16259
+ /* localStorage unavailable */ }
16260
+ return "light";
16113
16261
  });
16114
- // Update theme when propTheme changes
16262
+ const currentTheme = useMemo(()=>themeForMode(themeMode), [
16263
+ themeMode
16264
+ ]);
16115
16265
  useEffect(()=>{
16116
16266
  if (propTheme) {
16117
- setCurrentTheme(propTheme);
16267
+ setThemeModeState(themeToMode(propTheme));
16118
16268
  }
16119
16269
  }, [
16120
16270
  propTheme
16121
16271
  ]);
16122
- // Sync html.dark class for GlobalStyles and other CSS that uses html.dark
16123
16272
  useEffect(()=>{
16124
- const isDark = currentTheme === darkTheme;
16125
16273
  try {
16126
- if (isDark) {
16127
- document.documentElement.classList.add("dark");
16128
- } else {
16129
- document.documentElement.classList.remove("dark");
16130
- }
16131
- } catch (e) {
16132
- // document might not be available (e.g. SSR)
16133
- }
16274
+ const isClassicDark = themeMode === "dark";
16275
+ const isGlassLight = themeMode === "liquidGlassLight";
16276
+ const isGlassDark = themeMode === "liquidGlassDark";
16277
+ document.documentElement.classList.toggle("dark", isClassicDark);
16278
+ document.documentElement.classList.toggle("liquid-glass-light", isGlassLight);
16279
+ document.documentElement.classList.toggle("liquid-glass-dark", isGlassDark);
16280
+ /* Remove legacy class if present */ document.documentElement.classList.remove("liquid-glass");
16281
+ document.documentElement.dataset.theme = isClassicDark ? "dark" : isGlassLight ? "liquid-glass-light" : isGlassDark ? "liquid-glass-dark" : "light";
16282
+ } catch {
16283
+ /* document unavailable */ }
16134
16284
  }, [
16135
- currentTheme
16285
+ themeMode
16286
+ ]);
16287
+ useEffect(()=>{
16288
+ if (propTheme) return;
16289
+ try {
16290
+ localStorage.setItem("theme", themeMode);
16291
+ } catch {
16292
+ /* ignore */ }
16293
+ }, [
16294
+ themeMode,
16295
+ propTheme
16136
16296
  ]);
16137
- // Only listen for storage events if no theme prop is provided
16138
16297
  useEffect(()=>{
16139
16298
  if (propTheme) {
16140
- // If a theme prop is provided, don't listen for localStorage changes
16141
16299
  return;
16142
16300
  }
16143
16301
  const handleStorageChange = ()=>{
16144
16302
  try {
16145
- const savedTheme = localStorage.getItem("theme");
16146
- if (savedTheme === "dark") {
16147
- setCurrentTheme(darkTheme);
16148
- } else {
16149
- setCurrentTheme(lightTheme);
16150
- }
16151
- } catch (e) {
16152
- // localStorage not available, ignore storage changes
16153
- }
16303
+ setThemeModeState(normalizeStoredTheme(localStorage.getItem("theme")));
16304
+ } catch {
16305
+ /* ignore */ }
16154
16306
  };
16155
- // Listen for storage events (when localStorage changes in other tabs)
16156
16307
  window.addEventListener("storage", handleStorageChange);
16157
- return ()=>{
16158
- window.removeEventListener("storage", handleStorageChange);
16159
- };
16308
+ return ()=>window.removeEventListener("storage", handleStorageChange);
16160
16309
  }, [
16161
16310
  propTheme
16162
16311
  ]);
16163
- const value = React__default.useMemo(()=>({
16312
+ const setThemeMode = useCallback((mode)=>{
16313
+ setThemeModeState(mode);
16314
+ }, []);
16315
+ const setTheme = useCallback((next)=>{
16316
+ setThemeModeState(themeToMode(next));
16317
+ }, []);
16318
+ const isLiquidGlassLight = themeMode === "liquidGlassLight";
16319
+ const isLiquidGlassDark = themeMode === "liquidGlassDark";
16320
+ const isLiquidGlass = isLiquidGlassLight || isLiquidGlassDark;
16321
+ const isClassicDarkTheme = themeMode === "dark";
16322
+ const isDarkMode = isClassicDarkTheme || isLiquidGlassDark;
16323
+ const value = useMemo(()=>({
16164
16324
  theme: currentTheme,
16165
- setTheme: setCurrentTheme,
16166
- isDarkMode: currentTheme === darkTheme
16325
+ themeMode,
16326
+ setTheme,
16327
+ setThemeMode,
16328
+ isDarkMode,
16329
+ isClassicDarkTheme,
16330
+ isLiquidGlass,
16331
+ isLiquidGlassLight,
16332
+ isLiquidGlassDark
16167
16333
  }), [
16168
- currentTheme
16334
+ currentTheme,
16335
+ themeMode,
16336
+ setTheme,
16337
+ setThemeMode,
16338
+ isDarkMode,
16339
+ isClassicDarkTheme,
16340
+ isLiquidGlass,
16341
+ isLiquidGlassLight,
16342
+ isLiquidGlassDark
16169
16343
  ]);
16170
16344
  return /*#__PURE__*/ React__default.createElement(ThemeContext.Provider, {
16171
16345
  value: value
@@ -16176,14 +16350,20 @@ const ThemeProvider = ({ children, theme: propTheme })=>{
16176
16350
  const useTheme$1 = ()=>{
16177
16351
  const context = useContext(ThemeContext);
16178
16352
  if (context === undefined) {
16179
- // Return a graceful fallback instead of throwing an error
16180
16353
  return {
16181
16354
  theme: lightTheme,
16355
+ themeMode: "light",
16182
16356
  setTheme: ()=>{
16183
- // No-op function when context is not available
16184
16357
  console.warn("Theme context not available. setTheme called but ignored.");
16185
16358
  },
16186
- isDarkMode: false // Default to light mode when no context
16359
+ setThemeMode: ()=>{
16360
+ console.warn("Theme context not available. setThemeMode called but ignored.");
16361
+ },
16362
+ isDarkMode: false,
16363
+ isClassicDarkTheme: false,
16364
+ isLiquidGlass: false,
16365
+ isLiquidGlassLight: false,
16366
+ isLiquidGlassDark: false
16187
16367
  };
16188
16368
  }
16189
16369
  return context;
@@ -16192,7 +16372,6 @@ const useThemeValue = ()=>{
16192
16372
  const { theme } = useTheme$1();
16193
16373
  return theme;
16194
16374
  };
16195
- // Helper hook for components that can accept a theme prop
16196
16375
  const useComponentTheme = (themeProp)=>{
16197
16376
  const { theme: contextTheme } = useTheme$1();
16198
16377
  return themeProp || contextTheme;
@@ -48598,7 +48777,7 @@ const CountUpLatency = ({ value })=>{
48598
48777
  };
48599
48778
  }
48600
48779
  const NodeDetailsDrawer = ({ isVisible, onClose, nodeData })=>{
48601
- const { theme } = useTheme$1();
48780
+ const { theme, isLiquidGlass } = useTheme$1();
48602
48781
  const [selectedRowIndex, setSelectedRowIndex] = useState(null);
48603
48782
  const [selectedLlmDetails, setSelectedLlmDetails] = useState(null);
48604
48783
  // Default to last row (total/aggregate) when drawer opens (only on first open)
@@ -48672,11 +48851,11 @@ const NodeDetailsDrawer = ({ isVisible, onClose, nodeData })=>{
48672
48851
  "data-testid": "node-details-drawer",
48673
48852
  styles: {
48674
48853
  body: {
48675
- background: theme.colors.background,
48854
+ background: isLiquidGlass ? theme.colors.popover : theme.colors.background,
48676
48855
  color: theme.colors.foreground
48677
48856
  },
48678
48857
  header: {
48679
- background: theme.colors.card,
48858
+ background: isLiquidGlass ? theme.colors.popover : theme.colors.card,
48680
48859
  borderBottom: `1px solid ${theme.colors.border}`
48681
48860
  }
48682
48861
  }
@@ -52037,8 +52216,7 @@ const SheetOverlay = /*#__PURE__*/ React.forwardRef(({ className, ...props }, re
52037
52216
  bottom: 0,
52038
52217
  left: 0,
52039
52218
  zIndex: 50,
52040
- backgroundColor: "hsla(var(--background), 0.8)",
52041
- backdropFilter: "blur(4px)"
52219
+ backgroundColor: "rgba(0, 0, 0, 0.45)"
52042
52220
  },
52043
52221
  className: className,
52044
52222
  ...props,
@@ -52050,7 +52228,7 @@ const getSheetStyles = (side = "right")=>{
52050
52228
  position: "fixed",
52051
52229
  zIndex: 50,
52052
52230
  gap: "16px",
52053
- backgroundColor: "hsl(var(--background))",
52231
+ backgroundColor: "var(--background)",
52054
52232
  padding: "24px",
52055
52233
  boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
52056
52234
  transition: "all 0.3s ease-in-out"
@@ -52062,7 +52240,7 @@ const getSheetStyles = (side = "right")=>{
52062
52240
  left: 0,
52063
52241
  right: 0,
52064
52242
  top: 0,
52065
- borderBottom: "1px solid hsl(var(--border))"
52243
+ borderBottom: "1px solid var(--border)"
52066
52244
  };
52067
52245
  case "bottom":
52068
52246
  return {
@@ -52070,7 +52248,7 @@ const getSheetStyles = (side = "right")=>{
52070
52248
  left: 0,
52071
52249
  right: 0,
52072
52250
  bottom: 0,
52073
- borderTop: "1px solid hsl(var(--border))"
52251
+ borderTop: "1px solid var(--border)"
52074
52252
  };
52075
52253
  case "left":
52076
52254
  return {
@@ -52080,7 +52258,7 @@ const getSheetStyles = (side = "right")=>{
52080
52258
  left: 0,
52081
52259
  height: "100%",
52082
52260
  width: "75%",
52083
- borderRight: "1px solid hsl(var(--border))",
52261
+ borderRight: "1px solid var(--border)",
52084
52262
  maxWidth: "384px"
52085
52263
  };
52086
52264
  case "right":
@@ -52092,7 +52270,7 @@ const getSheetStyles = (side = "right")=>{
52092
52270
  right: 0,
52093
52271
  height: "100%",
52094
52272
  width: "75%",
52095
- borderLeft: "1px solid hsl(var(--border))",
52273
+ borderLeft: "1px solid var(--border)",
52096
52274
  maxWidth: "384px"
52097
52275
  };
52098
52276
  }
@@ -52153,7 +52331,7 @@ const SheetTitle = /*#__PURE__*/ React.forwardRef(({ className, ...props }, ref)
52153
52331
  style: {
52154
52332
  fontSize: "18px",
52155
52333
  fontWeight: 600,
52156
- color: "hsl(var(--foreground))"
52334
+ color: "var(--foreground)"
52157
52335
  },
52158
52336
  className: className,
52159
52337
  ...props
@@ -52163,7 +52341,7 @@ const SheetDescription = /*#__PURE__*/ React.forwardRef(({ className, ...props }
52163
52341
  ref: ref,
52164
52342
  style: {
52165
52343
  fontSize: "14px",
52166
- color: "hsl(var(--muted-foreground))"
52344
+ color: "var(--muted-foreground)"
52167
52345
  },
52168
52346
  className: className,
52169
52347
  ...props
@@ -116330,7 +116508,18 @@ const useEvaluationsMock = ()=>{
116330
116508
  return useContext(EvaluationsMockContext);
116331
116509
  };
116332
116510
 
116333
- const API_BASE = "/api";
116511
+ /**
116512
+ * Railtracks HTTP API prefix. Same-origin `/api` by default (browser + viz proxy).
116513
+ * Set `VITE_API_ORIGIN` (e.g. `http://localhost:3030`) when the UI is served elsewhere (Electron, etc.).
116514
+ */ function resolveApiBase() {
116515
+ const origin = import.meta.env.VITE_API_ORIGIN?.trim().replace(/\/$/, "") ?? "";
116516
+ if (!origin) {
116517
+ return "/api";
116518
+ }
116519
+ return `${origin}/api`;
116520
+ }
116521
+ const API_BASE = resolveApiBase();
116522
+
116334
116523
  const useEvaluations = ()=>{
116335
116524
  const mockContext = useEvaluationsMock();
116336
116525
  const [evaluations, setEvaluations] = useState([]);
@@ -120076,6 +120265,166 @@ const GlobalStyles = ()=>{
120076
120265
  --ring: #6366f1;
120077
120266
  --radius: 0.5rem;
120078
120267
  }
120268
+
120269
+ html.liquid-glass-light {
120270
+ color: hsl(222 47% 11%);
120271
+ }
120272
+
120273
+ html.liquid-glass-light::before {
120274
+ content: "";
120275
+ position: fixed;
120276
+ inset: 0;
120277
+ z-index: -1;
120278
+ pointer-events: none;
120279
+ background:
120280
+ radial-gradient(120% 90% at 10% 10%, hsl(210 55% 92%) 0%, transparent 52%),
120281
+ radial-gradient(90% 70% at 90% 20%, hsl(220 45% 88%) 0%, transparent 45%),
120282
+ radial-gradient(70% 60% at 50% 100%, hsl(200 35% 88%) 0%, transparent 50%), hsl(210 30% 91%);
120283
+ }
120284
+
120285
+ html.liquid-glass-light body {
120286
+ background-color: hsl(210 30% 91%);
120287
+ color: hsl(222 47% 11%);
120288
+ }
120289
+
120290
+ html.liquid-glass-light .react-flow {
120291
+ contain: paint;
120292
+ }
120293
+
120294
+ html.liquid-glass-light .react-flow__edge-label {
120295
+ background: hsla(0 0% 100% / 0.92);
120296
+ border: 1px solid hsla(210 28% 72% / 0.55);
120297
+ color: hsl(222 47% 11%);
120298
+ }
120299
+
120300
+ html.liquid-glass-light .file-selector-refresh-btn {
120301
+ border: 1px solid hsla(210 28% 72% / 0.55);
120302
+ background-color: hsla(0 0% 100% / 0.88);
120303
+ color: hsl(222 47% 11%);
120304
+ }
120305
+
120306
+ html.liquid-glass-light .file-selector-refresh-btn:hover {
120307
+ background-color: hsla(0 0% 100% / 0.96);
120308
+ color: hsl(222 47% 14%);
120309
+ }
120310
+
120311
+ html.liquid-glass-light .ant-table-tbody > tr.evaluations-compare-diff-row > td {
120312
+ background-color: hsla(48 96% 53% / 0.22) !important;
120313
+ }
120314
+
120315
+ html.liquid-glass-light {
120316
+ --background: hsl(210 30% 91%);
120317
+ --foreground: hsl(222 47% 11%);
120318
+ --card: hsla(0 0% 100% / 0.88);
120319
+ --card-foreground: hsl(222 47% 11%);
120320
+ --popover: hsla(0 0% 100% / 0.94);
120321
+ --popover-foreground: hsl(222 47% 11%);
120322
+ --primary: hsl(211 92% 45%);
120323
+ --primary-foreground: hsl(210 40% 98%);
120324
+ --secondary: hsla(210 40% 98% / 0.45);
120325
+ --secondary-foreground: hsl(222 47% 18%);
120326
+ --muted: hsla(210 25% 98% / 0.35);
120327
+ --muted-foreground: hsl(215 16% 40%);
120328
+ --accent: hsla(210 60% 96% / 0.55);
120329
+ --accent-foreground: hsl(222 47% 18%);
120330
+ --destructive: hsl(0 72% 51%);
120331
+ --destructive-foreground: hsl(210 40% 98%);
120332
+ --border: hsla(210 28% 72% / 0.55);
120333
+ --input: hsla(210 28% 88% / 0.65);
120334
+ --ring: hsl(211 92% 45%);
120335
+ --radius: 0.75rem;
120336
+ }
120337
+
120338
+ html.liquid-glass-dark {
120339
+ color: hsl(210 36% 96%);
120340
+ }
120341
+
120342
+ html.liquid-glass-dark::before {
120343
+ content: "";
120344
+ position: fixed;
120345
+ inset: 0;
120346
+ z-index: -1;
120347
+ pointer-events: none;
120348
+ background:
120349
+ radial-gradient(130% 100% at 20% 0%, hsl(222 42% 14%) 0%, transparent 55%),
120350
+ radial-gradient(90% 80% at 95% 30%, hsl(232 38% 18%) 0%, transparent 45%),
120351
+ radial-gradient(80% 70% at 50% 100%, hsl(215 36% 12%) 0%, transparent 50%), hsl(222 28% 9%);
120352
+ }
120353
+
120354
+ html.liquid-glass-dark body {
120355
+ background-color: hsl(222 28% 9%);
120356
+ color: hsl(210 36% 96%);
120357
+ }
120358
+
120359
+ html.liquid-glass-dark .react-flow {
120360
+ contain: paint;
120361
+ }
120362
+
120363
+ html.liquid-glass-dark .react-flow__edge-label {
120364
+ background: hsla(222 22% 18% / 0.92);
120365
+ border: 1px solid hsla(215 22% 42% / 0.45);
120366
+ color: hsl(210 36% 96%);
120367
+ }
120368
+
120369
+ html.liquid-glass-dark .file-selector-refresh-btn {
120370
+ border: 1px solid hsla(215 22% 42% / 0.45);
120371
+ background-color: hsla(222 22% 14% / 0.88);
120372
+ color: hsl(210 36% 96%);
120373
+ }
120374
+
120375
+ html.liquid-glass-dark .file-selector-refresh-btn:hover {
120376
+ background-color: hsla(222 22% 18% / 0.96);
120377
+ color: hsl(210 40% 98%);
120378
+ }
120379
+
120380
+ html.liquid-glass-dark .ant-table-tbody > tr.evaluations-compare-diff-row > td {
120381
+ background-color: hsla(217 32% 28% / 0.55) !important;
120382
+ }
120383
+
120384
+ html.liquid-glass-dark {
120385
+ --background: hsl(222 28% 9%);
120386
+ --foreground: hsl(210 36% 96%);
120387
+ --card: hsla(222 22% 14% / 0.88);
120388
+ --card-foreground: hsl(210 36% 96%);
120389
+ --popover: hsla(222 24% 12% / 0.94);
120390
+ --popover-foreground: hsl(210 36% 96%);
120391
+ --primary: hsl(212 92% 62%);
120392
+ --primary-foreground: hsl(222 47% 8%);
120393
+ --secondary: hsla(217 28% 22% / 0.55);
120394
+ --secondary-foreground: hsl(210 36% 94%);
120395
+ --muted: hsla(217 22% 18% / 0.45);
120396
+ --muted-foreground: hsl(215 18% 72%);
120397
+ --accent: hsla(217 26% 22% / 0.55);
120398
+ --accent-foreground: hsl(210 36% 96%);
120399
+ --destructive: hsl(0 72% 58%);
120400
+ --destructive-foreground: hsl(210 40% 98%);
120401
+ --border: hsla(215 22% 42% / 0.45);
120402
+ --input: hsla(217 22% 22% / 0.65);
120403
+ --ring: hsl(212 92% 62%);
120404
+ --radius: 0.75rem;
120405
+ }
120406
+
120407
+ @media (prefers-reduced-transparency: reduce) {
120408
+ html.liquid-glass-light::before,
120409
+ html.liquid-glass-dark::before {
120410
+ display: none;
120411
+ }
120412
+
120413
+ html.liquid-glass-light body {
120414
+ background-color: hsl(210 30% 91%);
120415
+ }
120416
+
120417
+ html.liquid-glass-dark body {
120418
+ background-color: hsl(222 28% 9%);
120419
+ }
120420
+
120421
+ html.liquid-glass-light .react-flow__edge-label,
120422
+ html.liquid-glass-dark .react-flow__edge-label,
120423
+ html.liquid-glass-light .file-selector-refresh-btn,
120424
+ html.liquid-glass-dark .file-selector-refresh-btn {
120425
+ background-color: var(--card);
120426
+ }
120427
+ }
120079
120428
  `
120080
120429
  });
120081
120430
  };
@@ -120216,4 +120565,4 @@ const GlobalStyles = ()=>{
120216
120565
  return getSafeColor(props.theme, path, fallback);
120217
120566
  };
120218
120567
 
120219
- export { AgenticFlowVisualizer, AnthropicIcon, AppleIcon, Badge, Drawer, Edge, EvaluationDetailsDrawer, EvaluationsCompareDrawer, EvaluationsComparePage, EvaluationsCompareView, EvaluationsErrorCard, EvaluationsTable, EvaluatorResult, GlobalStyles, GoogleIcon, JsonTreeViewer, Node$1 as Node, OllamaIcon, OpenAIIcon, SafeThemeProvider, SessionDetails, Sheet, ThemeProvider, Timeline, Visualizer, calculateAutoLayout, cn, conditionalStyle, createSafeTheme, darkTheme, defaultTheme$1 as defaultTheme, detectContentType, escapeDotPathSegment, evaluationsHaveSameMetrics, formatCurrency, formatDateFriendly, formatDateRelative, formatDateShort, formatDuration, formatJsonTreePrimitive, formatLatency, formatMetricValue$1 as formatMetricValue, formatNumberWithCommas, formatToolCalls, getColor, getDefaultTimelineStepForRun, getSafeColor, getSafeSpacing, getSafeThemeValue, getSpacing, getStatusColor, isEvaluationDto, isPlainObject, lightTheme, safeStyled, safeThemeColor, safeThemeSpacing, safeThemeValue, toTitleCase, transformEvaluation, truncateText, useComponentTheme, useSafeTheme, useTheme$1 as useTheme, useThemeValue };
120568
+ export { AgenticFlowVisualizer, AnthropicIcon, AppleIcon, Badge, Drawer, Edge, EvaluationDetailsDrawer, EvaluationsCompareDrawer, EvaluationsComparePage, EvaluationsCompareView, EvaluationsErrorCard, EvaluationsTable, EvaluatorResult, GlobalStyles, GoogleIcon, JsonTreeViewer, Node$1 as Node, OllamaIcon, OpenAIIcon, SafeThemeProvider, SessionDetails, Sheet, ThemeProvider, Timeline, Visualizer, calculateAutoLayout, cn, conditionalStyle, createSafeTheme, darkTheme, defaultTheme$1 as defaultTheme, detectContentType, escapeDotPathSegment, evaluationsHaveSameMetrics, formatCurrency, formatDateFriendly, formatDateRelative, formatDateShort, formatDuration, formatJsonTreePrimitive, formatLatency, formatMetricValue$1 as formatMetricValue, formatNumberWithCommas, formatToolCalls, getColor, getDefaultTimelineStepForRun, getSafeColor, getSafeSpacing, getSafeThemeValue, getSpacing, getStatusColor, isEvaluationDto, isPlainObject, lightTheme, liquidGlassDarkTheme, liquidGlassLightTheme, safeStyled, safeThemeColor, safeThemeSpacing, safeThemeValue, themeForMode, themeToMode, toTitleCase, transformEvaluation, truncateText, useComponentTheme, useSafeTheme, useTheme$1 as useTheme, useThemeValue };
@@ -1,5 +1,6 @@
1
1
  import { AgentSession } from "../dto/AgentSession";
2
- export declare const API_BASE = "/api";
2
+ import { API_BASE } from "../lib/apiBase";
3
+ export { API_BASE };
3
4
  export interface JsonFile {
4
5
  name: string;
5
6
  size?: number;
@@ -1,9 +1,20 @@
1
1
  import React, { ReactNode } from "react";
2
2
  import { Theme } from "./theme";
3
+ export type ThemeMode = "light" | "dark" | "liquidGlassLight" | "liquidGlassDark";
4
+ export declare function themeForMode(mode: ThemeMode): Theme;
5
+ export declare function themeToMode(theme: Theme): ThemeMode;
3
6
  interface ThemeContextType {
4
7
  theme: Theme;
8
+ themeMode: ThemeMode;
5
9
  setTheme: (theme: Theme) => void;
10
+ setThemeMode: (mode: ThemeMode) => void;
11
+ /** True for classic dark or dark liquid glass (graphs, muted text). */
6
12
  isDarkMode: boolean;
13
+ /** True only for solid classic dark — heavy Ant Design chrome overrides. */
14
+ isClassicDarkTheme: boolean;
15
+ isLiquidGlass: boolean;
16
+ isLiquidGlassLight: boolean;
17
+ isLiquidGlassDark: boolean;
7
18
  }
8
19
  interface ThemeProviderProps {
9
20
  children: ReactNode;
@@ -0,0 +1 @@
1
+ export declare const API_BASE: string;
@@ -92,5 +92,9 @@ export interface Theme {
92
92
  };
93
93
  }
94
94
  export declare const lightTheme: Theme;
95
+ /** Light “Liquid Glass”–inspired palette (web approximation). */
96
+ export declare const liquidGlassLightTheme: Theme;
97
+ /** Dark “Liquid Glass”–inspired palette — translucent panels on a deep mesh-like base. */
98
+ export declare const liquidGlassDarkTheme: Theme;
95
99
  export declare const darkTheme: Theme;
96
100
  export declare const defaultTheme: Theme;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@railtownai/railtracks-visualizer",
3
- "version": "0.0.67",
3
+ "version": "0.0.68",
4
4
  "license": "MIT",
5
5
  "author": "Railtown AI",
6
6
  "description": "A visualizer for Railtracks agentic flows",
@@ -71,7 +71,10 @@
71
71
  "storybook:preview": "npx http-server ./storybook-static",
72
72
  "test:ui": "vitest --ui",
73
73
  "test": "vitest run --silent",
74
- "up": "ncu -u -x react -x react-dom -x @types/react -x @types/react-dom -x react-hotkeys-hook"
74
+ "up": "ncu -u -x react -x react-dom -x @types/react -x @types/react-dom -x react-hotkeys-hook",
75
+ "electron": "npm run electron:start",
76
+ "electron:dev": "cross-env ELECTRON_DEV=1 VITE_API_ORIGIN=http://localhost:3030 concurrently -k \"vite\" \"wait-on http://127.0.0.1:3001 && electron electron/main.js\"",
77
+ "electron:start": "cross-env VITE_API_ORIGIN=http://localhost:3030 npm run build:spa && electron electron/main.js"
75
78
  },
76
79
  "devDependencies": {
77
80
  "@chromatic-com/storybook": "5.1.2",
@@ -92,7 +95,9 @@
92
95
  "@types/react-dom": "18.3.7",
93
96
  "@vitejs/plugin-react-swc": "4.3.0",
94
97
  "clsx": "2.1.1",
98
+ "concurrently": "9.2.1",
95
99
  "cross-env": "10.1.0",
100
+ "electron": "34.5.8",
96
101
  "gh-pages": "6.3.0",
97
102
  "jsdom": "29.0.2",
98
103
  "license-checker": "25.0.1",
@@ -104,7 +109,8 @@
104
109
  "typescript": "6.0.3",
105
110
  "uuid": "13.0.0",
106
111
  "vite": "8.0.8",
107
- "vitest": "4.1.4"
112
+ "vitest": "4.1.4",
113
+ "wait-on": "8.0.5"
108
114
  },
109
115
  "prettier": {
110
116
  "singleQuote": false,