@tellegen/svelte 0.1.2 → 0.3.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 (140) hide show
  1. package/README.md +46 -0
  2. package/dist/Sparkline.svelte.d.ts +0 -1
  3. package/dist/TellegenMap.svelte +328 -200
  4. package/dist/TellegenMap.svelte.d.ts +0 -1
  5. package/dist/TellegenProvider.svelte +4 -0
  6. package/dist/TellegenProvider.svelte.d.ts +0 -1
  7. package/dist/TellegenShell.svelte +19 -13
  8. package/dist/TellegenShell.svelte.d.ts +0 -1
  9. package/dist/TellegenViewer.svelte.d.ts +0 -1
  10. package/dist/api.d.ts +8 -2
  11. package/dist/api.js +6 -2
  12. package/dist/colors.d.ts +7 -8
  13. package/dist/colors.js +13 -13
  14. package/dist/components/AppFooter.svelte.d.ts +0 -1
  15. package/dist/components/AppHeader.svelte +27 -13
  16. package/dist/components/AppHeader.svelte.d.ts +0 -1
  17. package/dist/components/BindingLines.svelte +3 -6
  18. package/dist/components/BindingLines.svelte.d.ts +0 -1
  19. package/dist/components/BusPicker.svelte +8 -2
  20. package/dist/components/BusPicker.svelte.d.ts +0 -1
  21. package/dist/components/ControlPanel.svelte +106 -343
  22. package/dist/components/ControlPanel.svelte.d.ts +0 -1
  23. package/dist/components/DemandSlider.svelte +26 -8
  24. package/dist/components/DemandSlider.svelte.d.ts +0 -1
  25. package/dist/components/DiagramCanvas.svelte +486 -0
  26. package/dist/components/DiagramCanvas.svelte.d.ts +13 -0
  27. package/dist/components/DisplayControls.svelte +4 -2
  28. package/dist/components/DisplayControls.svelte.d.ts +0 -1
  29. package/dist/components/DropZone.svelte.d.ts +0 -1
  30. package/dist/components/FormulationSelector.svelte +8 -3
  31. package/dist/components/FormulationSelector.svelte.d.ts +0 -1
  32. package/dist/components/LocalCaseDetails.svelte +57 -44
  33. package/dist/components/LocalCaseDetails.svelte.d.ts +0 -1
  34. package/dist/components/McResults.svelte +365 -0
  35. package/dist/components/McResults.svelte.d.ts +11 -0
  36. package/dist/components/ModelDetails.svelte +230 -0
  37. package/dist/components/ModelDetails.svelte.d.ts +7 -0
  38. package/dist/components/MulticonductorDetails.svelte +227 -78
  39. package/dist/components/MulticonductorDetails.svelte.d.ts +0 -1
  40. package/dist/components/NetworkStats.svelte +20 -18
  41. package/dist/components/NetworkStats.svelte.d.ts +0 -1
  42. package/dist/components/NotificationCenter.svelte +243 -0
  43. package/dist/components/NotificationCenter.svelte.d.ts +3 -0
  44. package/dist/components/PanelCard.svelte +319 -0
  45. package/dist/components/PanelCard.svelte.d.ts +9 -0
  46. package/dist/components/PanelFrame.svelte +46 -0
  47. package/dist/components/PanelFrame.svelte.d.ts +16 -0
  48. package/dist/components/PanelHost.svelte +212 -0
  49. package/dist/components/PanelHost.svelte.d.ts +3 -0
  50. package/dist/components/PlacementCue.svelte.d.ts +0 -1
  51. package/dist/components/PowerFlowDetails.svelte +99 -0
  52. package/dist/components/PowerFlowDetails.svelte.d.ts +3 -0
  53. package/dist/components/RatingSlider.svelte +5 -9
  54. package/dist/components/RatingSlider.svelte.d.ts +0 -1
  55. package/dist/components/RestoreDefaultsButton.svelte.d.ts +0 -1
  56. package/dist/components/SavedNetworkDetails.svelte +214 -0
  57. package/dist/components/SavedNetworkDetails.svelte.d.ts +3 -0
  58. package/dist/components/SensitivityReadout.svelte +24 -16
  59. package/dist/components/SensitivityReadout.svelte.d.ts +0 -1
  60. package/dist/components/SizeLegend.svelte.d.ts +0 -1
  61. package/dist/components/SolveCard.svelte +46 -49
  62. package/dist/components/SolveCard.svelte.d.ts +0 -1
  63. package/dist/components/TopMovers.svelte +8 -3
  64. package/dist/components/TopMovers.svelte.d.ts +2 -18
  65. package/dist/components.d.ts +2 -1
  66. package/dist/components.js +2 -0
  67. package/dist/context.svelte.d.ts +0 -1
  68. package/dist/controller.svelte.d.ts +74 -41
  69. package/dist/controller.svelte.js +1095 -284
  70. package/dist/diagram-labels.d.ts +9 -0
  71. package/dist/diagram-labels.js +40 -0
  72. package/dist/display.d.ts +3 -8
  73. package/dist/display.js +31 -27
  74. package/dist/drop-classify.d.ts +5 -42
  75. package/dist/drop-classify.js +5 -78
  76. package/dist/drop-limits.d.ts +27 -0
  77. package/dist/drop-limits.js +83 -0
  78. package/dist/format.d.ts +0 -1
  79. package/dist/format.js +5 -4
  80. package/dist/index.d.ts +11 -7
  81. package/dist/index.js +8 -3
  82. package/dist/map-bounds.d.ts +5 -0
  83. package/dist/map-bounds.js +41 -0
  84. package/dist/map.d.ts +1 -2
  85. package/dist/map.js +1 -1
  86. package/dist/multiconductor.d.ts +11 -3
  87. package/dist/multiconductor.js +107 -14
  88. package/dist/notices.svelte.d.ts +29 -0
  89. package/dist/notices.svelte.js +82 -0
  90. package/dist/panels.svelte.d.ts +65 -0
  91. package/dist/panels.svelte.js +215 -0
  92. package/dist/state.svelte.d.ts +90 -24
  93. package/dist/state.svelte.js +94 -20
  94. package/dist/synthetic-layout.d.ts +0 -1
  95. package/dist/synthetic-layout.js +17 -2
  96. package/dist/transformer-icon.d.ts +0 -1
  97. package/dist/types.d.ts +0 -1
  98. package/package.json +28 -12
  99. package/dist/Sparkline.svelte.d.ts.map +0 -1
  100. package/dist/TellegenMap.svelte.d.ts.map +0 -1
  101. package/dist/TellegenProvider.svelte.d.ts.map +0 -1
  102. package/dist/TellegenShell.svelte.d.ts.map +0 -1
  103. package/dist/TellegenViewer.svelte.d.ts.map +0 -1
  104. package/dist/api.d.ts.map +0 -1
  105. package/dist/colors.d.ts.map +0 -1
  106. package/dist/components/AppFooter.svelte.d.ts.map +0 -1
  107. package/dist/components/AppHeader.svelte.d.ts.map +0 -1
  108. package/dist/components/BindingLines.svelte.d.ts.map +0 -1
  109. package/dist/components/BusPicker.svelte.d.ts.map +0 -1
  110. package/dist/components/ControlPanel.svelte.d.ts.map +0 -1
  111. package/dist/components/DemandSlider.svelte.d.ts.map +0 -1
  112. package/dist/components/DisplayControls.svelte.d.ts.map +0 -1
  113. package/dist/components/DropZone.svelte.d.ts.map +0 -1
  114. package/dist/components/FormulationSelector.svelte.d.ts.map +0 -1
  115. package/dist/components/LocalCaseDetails.svelte.d.ts.map +0 -1
  116. package/dist/components/MulticonductorDetails.svelte.d.ts.map +0 -1
  117. package/dist/components/NetworkStats.svelte.d.ts.map +0 -1
  118. package/dist/components/PlacementCue.svelte.d.ts.map +0 -1
  119. package/dist/components/RatingSlider.svelte.d.ts.map +0 -1
  120. package/dist/components/RestoreDefaultsButton.svelte.d.ts.map +0 -1
  121. package/dist/components/SensitivityReadout.svelte.d.ts.map +0 -1
  122. package/dist/components/SizeLegend.svelte.d.ts.map +0 -1
  123. package/dist/components/SolveCard.svelte.d.ts.map +0 -1
  124. package/dist/components/TopMovers.svelte.d.ts.map +0 -1
  125. package/dist/components.d.ts.map +0 -1
  126. package/dist/context.svelte.d.ts.map +0 -1
  127. package/dist/controller.svelte.d.ts.map +0 -1
  128. package/dist/display.d.ts.map +0 -1
  129. package/dist/drop-classify.d.ts.map +0 -1
  130. package/dist/format.d.ts.map +0 -1
  131. package/dist/index.d.ts.map +0 -1
  132. package/dist/map.d.ts.map +0 -1
  133. package/dist/multiconductor.d.ts.map +0 -1
  134. package/dist/state.svelte.d.ts.map +0 -1
  135. package/dist/study-package.d.ts +0 -9
  136. package/dist/study-package.d.ts.map +0 -1
  137. package/dist/study-package.js +0 -17
  138. package/dist/synthetic-layout.d.ts.map +0 -1
  139. package/dist/transformer-icon.d.ts.map +0 -1
  140. package/dist/types.d.ts.map +0 -1
@@ -12,4 +12,3 @@ type $$ComponentProps = {
12
12
  declare const TellegenMap: import("svelte").Component<$$ComponentProps, {}, "">;
13
13
  type TellegenMap = ReturnType<typeof TellegenMap>;
14
14
  export default TellegenMap;
15
- //# sourceMappingURL=TellegenMap.svelte.d.ts.map
@@ -9,6 +9,8 @@
9
9
  } from './context.svelte.js';
10
10
  import { createController } from './controller.svelte.js';
11
11
  import { createAppState } from './state.svelte.js';
12
+ import { PanelLayout, setPanelLayout } from './panels.svelte.js';
13
+ import { NoticeCenter, setNoticeCenter } from './notices.svelte.js';
12
14
 
13
15
  interface Props extends TellegenUiOptions {
14
16
  children?: Snippet;
@@ -30,6 +32,8 @@
30
32
  const app = createAppState();
31
33
  const ctrl = createController(app, { apiBase: config.apiBase });
32
34
 
35
+ setPanelLayout(new PanelLayout());
36
+ setNoticeCenter(new NoticeCenter());
33
37
  setUiConfig(config);
34
38
  setAppState(app);
35
39
  setController(ctrl);
@@ -6,4 +6,3 @@ interface Props extends TellegenUiOptions {
6
6
  declare const TellegenProvider: import("svelte").Component<Props, {}, "">;
7
7
  type TellegenProvider = ReturnType<typeof TellegenProvider>;
8
8
  export default TellegenProvider;
9
- //# sourceMappingURL=TellegenProvider.svelte.d.ts.map
@@ -1,18 +1,24 @@
1
1
  <script lang="ts">
2
2
  import { onMount } from 'svelte';
3
3
  import AppFooter from './components/AppFooter.svelte';
4
+ import NotificationCenter from './components/NotificationCenter.svelte';
4
5
  import AppHeader from './components/AppHeader.svelte';
5
- import BusPicker from './components/BusPicker.svelte';
6
6
  import ControlPanel from './components/ControlPanel.svelte';
7
7
  import DropZone from './components/DropZone.svelte';
8
8
  import PlacementCue from './components/PlacementCue.svelte';
9
9
  import RestoreDefaultsButton from './components/RestoreDefaultsButton.svelte';
10
10
  import SolveCard from './components/SolveCard.svelte';
11
+ import PanelHost from './components/PanelHost.svelte';
12
+ import { PANEL_COMPACT_QUERY } from './panels.svelte.js';
11
13
  import { getAppState, getController, getUiConfig } from './context.svelte.js';
12
14
  import TellegenMap from './TellegenMap.svelte';
13
15
 
14
- const FILE_DROP_QUERY = '(hover: hover) and (pointer: fine) and (min-width: 761px)';
15
- const COMPACT_QUERY = '(max-width: 760px)';
16
+ // Gates the drag-and-drop affordance only. The upload button in the header
17
+ // is always rendered: a file picker works on every device, and the plain
18
+ // `hover`/`pointer` queries resolve false on a touchscreen laptop that also
19
+ // has a mouse, which hid the whole file flow there (#110). `any-*` match
20
+ // when at least one attached input can hover and point precisely.
21
+ const FILE_DROP_QUERY = '(any-hover: hover) and (any-pointer: fine)';
16
22
 
17
23
  const app = getAppState();
18
24
  const ctrl = getController();
@@ -21,6 +27,7 @@
21
27
  let dragDepth = 0;
22
28
 
23
29
  $effect(() => {
30
+ if (app.studyView) return;
24
31
  let hasDisplayMode = false;
25
32
  for (const option of ctrl.displayOptions) {
26
33
  if (option.mode === app.displayMode) {
@@ -29,7 +36,7 @@
29
36
  }
30
37
  }
31
38
  if (ctrl.displayOptions.length > 0 && !hasDisplayMode) {
32
- app.displayMode = 'lmp';
39
+ app.displayMode = 'price';
33
40
  }
34
41
  });
35
42
 
@@ -52,7 +59,7 @@
52
59
  syncFileDropUi();
53
60
  query.addEventListener('change', syncFileDropUi);
54
61
 
55
- const compact = window.matchMedia(COMPACT_QUERY);
62
+ const compact = window.matchMedia(PANEL_COMPACT_QUERY);
56
63
  const syncCompact = () => {
57
64
  app.compactLayout = compact.matches;
58
65
  if (!compact.matches) app.sheetInset = 0;
@@ -60,8 +67,7 @@
60
67
  syncCompact();
61
68
  compact.addEventListener('change', syncCompact);
62
69
 
63
- // The sheet sizes its snaps against the viewport and the map caps how far
64
- // it lifts the basemap chrome, so both read one measurement taken here.
70
+ // Shared viewport measurements keep panel and map framing dimensions aligned.
65
71
  const syncViewport = () => (app.viewportHeight = window.innerHeight);
66
72
  syncViewport();
67
73
  window.addEventListener('resize', syncViewport);
@@ -129,15 +135,15 @@
129
135
  />
130
136
 
131
137
  <AppHeader />
138
+ <PanelHost />
139
+ <NotificationCenter />
132
140
  <ControlPanel />
133
141
  <SolveCard />
134
- <!-- ControlPanel mounts the lookup and the footer inline when compact. -->
135
- {#if !app.compactLayout}
136
- <BusPicker />
142
+ {#if !app.studyView}
143
+ <DropZone />
144
+ <PlacementCue />
145
+ <RestoreDefaultsButton />
137
146
  {/if}
138
- <DropZone />
139
- <PlacementCue />
140
- <RestoreDefaultsButton />
141
147
  {#if config.showFooter && !app.compactLayout}
142
148
  <AppFooter />
143
149
  {/if}
@@ -1,4 +1,3 @@
1
1
  declare const TellegenShell: import("svelte").Component<Record<string, never>, {}, "">;
2
2
  type TellegenShell = ReturnType<typeof TellegenShell>;
3
3
  export default TellegenShell;
4
- //# sourceMappingURL=TellegenShell.svelte.d.ts.map
@@ -1,4 +1,3 @@
1
1
  declare const TellegenViewer: import("svelte").Component<Partial<import("./context.svelte.js").TellegenUiConfig>, {}, "">;
2
2
  type TellegenViewer = ReturnType<typeof TellegenViewer>;
3
3
  export default TellegenViewer;
4
- //# sourceMappingURL=TellegenViewer.svelte.d.ts.map
package/dist/api.d.ts CHANGED
@@ -9,8 +9,15 @@ export interface TellegenApiClientOptions {
9
9
  export interface TellegenApiClient {
10
10
  getCases(): Promise<CaseSummary[]>;
11
11
  getNetwork(caseId: string): Promise<Network>;
12
- getCaseNetworkJson(caseId: string, signal?: AbortSignal): Promise<string>;
12
+ getCaseModuleJson(caseId: string, signal?: AbortSignal): Promise<string>;
13
13
  getSolution(caseId: string): Promise<Solution>;
14
+ getSavedCase?(caseId: string): Promise<{
15
+ base_input: string;
16
+ input: string;
17
+ solution: string;
18
+ view: import('@tellegen/engine').StudyView;
19
+ display_solution: Solution;
20
+ }>;
14
21
  getSensitivity(caseId: string, bus: number, deltas?: DemandDeltas, signal?: AbortSignal): Promise<SensitivityColumn>;
15
22
  getBranchSensitivity(caseId: string, branch: number, deltas?: DemandDeltas, signal?: AbortSignal): Promise<SensitivityColumn>;
16
23
  /** Whether the server's compute endpoints (solve stream, sensitivities) are
@@ -41,4 +48,3 @@ export interface SolveStreamHandlers {
41
48
  ondone?: () => void;
42
49
  }
43
50
  export declare function createApiClient(options?: TellegenApiClientOptions): TellegenApiClient;
44
- //# sourceMappingURL=api.d.ts.map
package/dist/api.js CHANGED
@@ -77,11 +77,12 @@ export function createApiClient(options = {}) {
77
77
  return {
78
78
  getCases: () => getJson(fetchImpl, apiPath(apiBase, '/cases')),
79
79
  getNetwork: (caseId) => getJson(fetchImpl, apiPath(apiBase, `/cases/${caseId}/network`)),
80
- async getCaseNetworkJson(caseId, signal) {
80
+ async getCaseModuleJson(caseId, signal) {
81
81
  const url = apiPath(apiBase, `/cases/${caseId}/case`);
82
82
  const res = await checkedFetch(fetchImpl, url, signal);
83
83
  return res.text();
84
84
  },
85
+ getSavedCase: (caseId) => getJson(fetchImpl, apiPath(apiBase, `/cases/${caseId}/snapshot`)),
85
86
  getSolution: (caseId) => getJson(fetchImpl, apiPath(apiBase, `/cases/${caseId}/solution`)),
86
87
  getSensitivity(caseId, bus, deltas = {}, signal) {
87
88
  const d = encodeDeltas(deltas);
@@ -111,7 +112,10 @@ export function createApiClient(options = {}) {
111
112
  finished = true;
112
113
  es.close();
113
114
  };
114
- es.addEventListener('solution', (e) => handlers.onsolution?.(JSON.parse(e.data)));
115
+ es.addEventListener('solution', (e) => {
116
+ const solution = JSON.parse(e.data);
117
+ handlers.onsolution?.(solution);
118
+ });
115
119
  es.addEventListener('sensitivity', (e) => handlers.onsensitivity?.(JSON.parse(e.data)));
116
120
  es.addEventListener('fail', (e) => {
117
121
  finish();
package/dist/colors.d.ts CHANGED
@@ -8,20 +8,20 @@ export interface SensitivityDomain {
8
8
  scale: number;
9
9
  flat: boolean;
10
10
  }
11
- export declare const lmpColor: (t: number) => RGBA;
12
- export declare const lmpGradient: string;
13
- /** Trimmed LMP color domain: 5th to 95th percentile, with a $1/MWh
14
- * minimum span. LMP distributions are heavy tailed (one binding line pins a
11
+ export declare const priceColor: (t: number) => RGBA;
12
+ export declare const priceGradient: string;
13
+ /** Trimmed nodal value color domain: 5th to 95th percentile, with a one-unit
14
+ * minimum span. Nodal values are heavy tailed when one binding line pins a
15
15
  * handful of buses far from the pack) so min-max scaling compresses the pack
16
16
  * into a single hue, and a congestion-free case is one price plus solver
17
17
  * noise that an exact scale would stretch into fake structure. Values beyond
18
18
  * the domain clamp to the ramp ends. */
19
- export declare function lmpDomain(values: number[]): {
19
+ export declare function priceDomain(values: number[]): {
20
20
  lo: number;
21
21
  hi: number;
22
22
  };
23
23
  /** Center a scalar display variable (bus voltage angle, |V|) on a symmetric domain
24
- * with a per-mode minimum span; LMP keeps its own robust quantile domain. Shared by
24
+ * with a per-mode minimum span; nodal values keep their own robust quantile domain. Shared by
25
25
  * the control panel readout and the map color scale. */
26
26
  export declare function scalarDomain(mode: DisplayMode, values: number[]): {
27
27
  lo: number;
@@ -33,7 +33,7 @@ export declare const sensGradient: string;
33
33
  export declare const sensNeutral: RGBA;
34
34
  export declare const busNeutral: RGBA;
35
35
  export declare function sensFlatColor(domain: Pick<SensitivityDomain, 'mean'>): RGBA;
36
- /** The fixed normalization for a slider drag: predicted ΔLMP divides by
36
+ /** The fixed normalization for a slider drag: predicted nodal value change divides by
37
37
  * scale × maxAbsStep, so color intensity is linear in the step and saturates at
38
38
  * full deflection for the column's robust-max buses. A per-frame domain over
39
39
  * column × step would cancel the step exactly (sensitivityDomain is degree-1
@@ -48,4 +48,3 @@ export declare function branchWidth(loading: number): number;
48
48
  /** Bus radius in px; area tracks max(load, gen) so big plants and big loads
49
49
  * both read at a glance. Shared by the map layers and the size legend. */
50
50
  export declare function busRadius(maxMw: number): number;
51
- //# sourceMappingURL=colors.d.ts.map
package/dist/colors.js CHANGED
@@ -19,25 +19,25 @@ function cssGradient(stops) {
19
19
  const parts = stops.map((s) => `rgb(${s[0]} ${s[1]} ${s[2]})`);
20
20
  return `linear-gradient(90deg, ${parts.join(', ')})`;
21
21
  }
22
- /** Sequential LMP ramp, cheap to expensive: pale straw darkening through
22
+ /** Sequential nodal value ramp: pale straw darkening through
23
23
  * amber and rust to near-black maroon. Lightness falls monotonically, so the
24
24
  * ordering survives every kind of color vision deficiency. */
25
- const LMP_STOPS = [
25
+ const PRICE_STOPS = [
26
26
  [246, 227, 180, 235],
27
27
  [236, 178, 66, 235],
28
28
  [212, 116, 34, 240],
29
29
  [160, 56, 21, 245],
30
30
  [84, 18, 7, 250]
31
31
  ];
32
- export const lmpColor = (t) => ramp(LMP_STOPS, t);
33
- export const lmpGradient = cssGradient(LMP_STOPS);
34
- /** Trimmed LMP color domain: 5th to 95th percentile, with a $1/MWh
35
- * minimum span. LMP distributions are heavy tailed (one binding line pins a
32
+ export const priceColor = (t) => ramp(PRICE_STOPS, t);
33
+ export const priceGradient = cssGradient(PRICE_STOPS);
34
+ /** Trimmed nodal value color domain: 5th to 95th percentile, with a one-unit
35
+ * minimum span. Nodal values are heavy tailed when one binding line pins a
36
36
  * handful of buses far from the pack) so min-max scaling compresses the pack
37
37
  * into a single hue, and a congestion-free case is one price plus solver
38
38
  * noise that an exact scale would stretch into fake structure. Values beyond
39
39
  * the domain clamp to the ramp ends. */
40
- export function lmpDomain(values) {
40
+ export function priceDomain(values) {
41
41
  if (values.length === 0)
42
42
  return { lo: 0, hi: 1 };
43
43
  const sorted = [...values].sort((a, b) => a - b);
@@ -49,11 +49,11 @@ export function lmpDomain(values) {
49
49
  return { lo: mid - span / 2, hi: mid + span / 2 };
50
50
  }
51
51
  /** Center a scalar display variable (bus voltage angle, |V|) on a symmetric domain
52
- * with a per-mode minimum span; LMP keeps its own robust quantile domain. Shared by
52
+ * with a per-mode minimum span; nodal values keep their own robust quantile domain. Shared by
53
53
  * the control panel readout and the map color scale. */
54
54
  export function scalarDomain(mode, values) {
55
- if (mode === 'lmp')
56
- return lmpDomain(values);
55
+ if (mode === 'price')
56
+ return priceDomain(values);
57
57
  if (values.length === 0)
58
58
  return { lo: 0, hi: 1 };
59
59
  const { min: rawLo, max: rawHi } = extent(values);
@@ -62,9 +62,9 @@ export function scalarDomain(mode, values) {
62
62
  const mid = (rawLo + rawHi) / 2;
63
63
  return { lo: mid - span / 2, hi: mid + span / 2 };
64
64
  }
65
- /** Diverging sensitivity ramp: green (price falls per MW of demand) through
65
+ /** Diverging sensitivity ramp: green (nodal value falls per MW of demand) through
66
66
  * a paper-toned neutral to purple (price rises). The green/purple pair stays
67
- * legible under CVD and shares no hue with the warm LMP ramp, so the two
67
+ * legible under CVD and shares no hue with the warm nodal value ramp, so the two
68
68
  * modes cannot be confused. */
69
69
  const SENS_STOPS = [
70
70
  [27, 120, 55, 240],
@@ -84,7 +84,7 @@ export function sensFlatColor(domain) {
84
84
  return sensNeutral;
85
85
  return sensColor(domain.mean > 0 ? FLAT_SENSITIVITY_TINT : -FLAT_SENSITIVITY_TINT);
86
86
  }
87
- /** The fixed normalization for a slider drag: predicted ΔLMP divides by
87
+ /** The fixed normalization for a slider drag: predicted nodal value change divides by
88
88
  * scale × maxAbsStep, so color intensity is linear in the step and saturates at
89
89
  * full deflection for the column's robust-max buses. A per-frame domain over
90
90
  * column × step would cancel the step exactly (sensitivityDomain is degree-1
@@ -4,4 +4,3 @@ type $$ComponentProps = {
4
4
  declare const AppFooter: import("svelte").Component<$$ComponentProps, {}, "">;
5
5
  type AppFooter = ReturnType<typeof AppFooter>;
6
6
  export default AppFooter;
7
- //# sourceMappingURL=AppFooter.svelte.d.ts.map
@@ -37,11 +37,16 @@
37
37
  {#each app.cases as c (c.id)}
38
38
  {@const [cname, cregion] = splitName(c.name)}
39
39
  <div class="case-chip" class:active={app.activeCaseId === c.id}>
40
- <button class="case-activate" onclick={() => ctrl.activateCase(c.id)}>
40
+ <button
41
+ class="case-activate"
42
+ disabled={!!c.unavailableReason}
43
+ title={c.unavailableReason ?? undefined}
44
+ onclick={() => ctrl.activateCase(c.id)}
45
+ >
41
46
  <span class="cname"
42
47
  >{cname}{#if c.perturbed}<i class="mark" title="demand perturbed"></i>{/if}</span
43
48
  >
44
- <span class="cregion mono">{cregion}</span>
49
+ <span class="cregion mono">{c.unavailableReason ? 'Unavailable' : cregion}</span>
45
50
  </button>
46
51
  <button
47
52
  class="case-remove mono"
@@ -79,16 +84,18 @@
79
84
  >
80
85
  </div>
81
86
  {/each}
82
- {#if ctrl.showFileDropUi}
83
- <button
84
- class="ghost filedrop-ui"
85
- title="parsed in your browser; the file never uploads"
86
- onclick={() => fileInput?.click()}
87
+ <button
88
+ class="ghost filedrop-ui"
89
+ title="parsed in your browser; the file never uploads"
90
+ onclick={() => fileInput?.click()}
91
+ >
92
+ <span class="cname"
93
+ ><span class="arrow">&#8675;</span>{ctrl.showFileDropUi
94
+ ? 'drop a case file'
95
+ : 'open a case file'}</span
87
96
  >
88
- <span class="cname"><span class="arrow">&#8675;</span>drop a case file</span>
89
- <span class="cregion mono">case + geo files</span>
90
- </button>
91
- {/if}
97
+ <span class="cregion mono">case + geo files</span>
98
+ </button>
92
99
  </nav>
93
100
  <span class="kicker mono">
94
101
  <a href={config.orgHref} target="_blank" rel="noreferrer">{config.orgLabel}</a>
@@ -97,13 +104,15 @@
97
104
  </span>
98
105
  <input
99
106
  type="file"
100
- accept=".m,.raw,.aux,.dss,.pwd,.csv,.json,.geojson"
107
+ accept=".m,.raw,.aux,.epc,.pwb,.dss,.pwd,.csv,.json,.geojson"
101
108
  multiple
102
109
  hidden
103
110
  bind:this={fileInput}
104
111
  onchange={(e) => {
105
112
  const input = e.currentTarget;
106
- if (input.files) ctrl.ingestFiles(Array.from(input.files));
113
+ // Pass the FileList through unchanged: the controller checks its declared
114
+ // count before materializing any entries.
115
+ if (input.files) ctrl.ingestFiles(input.files);
107
116
  input.value = '';
108
117
  }}
109
118
  />
@@ -201,6 +210,11 @@
201
210
  border: 0;
202
211
  }
203
212
 
213
+ .case-activate:disabled {
214
+ color: var(--text-secondary);
215
+ cursor: not-allowed;
216
+ }
217
+
204
218
  .case-remove {
205
219
  position: absolute;
206
220
  top: 1px;
@@ -1,4 +1,3 @@
1
1
  declare const AppHeader: import("svelte").Component<Record<string, never>, {}, "">;
2
2
  type AppHeader = ReturnType<typeof AppHeader>;
3
3
  export default AppHeader;
4
- //# sourceMappingURL=AppHeader.svelte.d.ts.map
@@ -32,7 +32,7 @@
32
32
  <div class="binding-lines">
33
33
  <p
34
34
  class="mono dim head"
35
- title="lines loaded to their thermal rating; select one to see &part;LMP/&part;rating"
35
+ title="lines loaded to their thermal rating; select one to see LMP sensitivity to the rating"
36
36
  >
37
37
  binding lines
38
38
  </p>
@@ -45,9 +45,7 @@
45
45
  aria-pressed={app.selectedBranch === branch.id}
46
46
  onclick={() => select(branch.id)}
47
47
  >
48
- line {branch.from}&#8201;&ndash;&#8201;{branch.to} &middot; {Math.round(
49
- loading * 100
50
- )}%
48
+ line {branch.from}&#8201;&ndash;&#8201;{branch.to}, {Math.round(loading * 100)}%
51
49
  </button>
52
50
  </li>
53
51
  {/each}
@@ -93,8 +91,7 @@
93
91
  li button.selected {
94
92
  color: var(--text-accent);
95
93
  background:
96
- linear-gradient(90deg, rgba(177, 104, 27, 0.16), rgba(177, 104, 27, 0.05)),
97
- var(--accent-soft);
94
+ linear-gradient(90deg, rgba(177, 104, 27, 0.16), rgba(177, 104, 27, 0.05)), var(--accent-soft);
98
95
  box-shadow: inset 3px 0 0 var(--accent);
99
96
  font-weight: 600;
100
97
  }
@@ -1,4 +1,3 @@
1
1
  declare const BindingLines: import("svelte").Component<Record<string, never>, {}, "">;
2
2
  type BindingLines = ReturnType<typeof BindingLines>;
3
3
  export default BindingLines;
4
- //# sourceMappingURL=BindingLines.svelte.d.ts.map
@@ -8,7 +8,9 @@
8
8
  /** Render in the parent's flow instead of as a card floating over the map. */
9
9
  let { inline = false }: { inline?: boolean } = $props();
10
10
 
11
- const buses = $derived(app.active?.network?.buses ?? app.activeLocal?.view?.buses ?? []);
11
+ const buses = $derived(
12
+ app.studyView?.network.buses ?? app.active?.network?.buses ?? app.activeLocal?.view?.buses ?? []
13
+ );
12
14
 
13
15
  let query = $state('');
14
16
  let open = $state(false);
@@ -25,7 +27,11 @@
25
27
  function commit(id: number) {
26
28
  const caseId = app.activeCaseId;
27
29
  const localId = app.activeLocalId;
28
- if (caseId && app.active) ctrl.selectBus(caseId, id);
30
+ if (app.studyView) {
31
+ app.selectedBranch = null;
32
+ app.selectedBus = id;
33
+ void app.requestFrame({ caseId: app.studyView.caseId, busId: id });
34
+ } else if (caseId && app.active) ctrl.selectBus(caseId, id);
29
35
  else if (localId && app.activeLocal) ctrl.selectLocalBus(localId, id);
30
36
  query = '';
31
37
  open = false;
@@ -4,4 +4,3 @@ type $$ComponentProps = {
4
4
  declare const BusPicker: import("svelte").Component<$$ComponentProps, {}, "">;
5
5
  type BusPicker = ReturnType<typeof BusPicker>;
6
6
  export default BusPicker;
7
- //# sourceMappingURL=BusPicker.svelte.d.ts.map