@tellegen/svelte 0.1.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 (121) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +70 -0
  3. package/dist/Sparkline.svelte +100 -0
  4. package/dist/Sparkline.svelte.d.ts +8 -0
  5. package/dist/Sparkline.svelte.d.ts.map +1 -0
  6. package/dist/TellegenMap.svelte +1430 -0
  7. package/dist/TellegenMap.svelte.d.ts +14 -0
  8. package/dist/TellegenMap.svelte.d.ts.map +1 -0
  9. package/dist/TellegenProvider.svelte +38 -0
  10. package/dist/TellegenProvider.svelte.d.ts +9 -0
  11. package/dist/TellegenProvider.svelte.d.ts.map +1 -0
  12. package/dist/TellegenShell.svelte +126 -0
  13. package/dist/TellegenShell.svelte.d.ts +4 -0
  14. package/dist/TellegenShell.svelte.d.ts.map +1 -0
  15. package/dist/TellegenViewer.svelte +19 -0
  16. package/dist/TellegenViewer.svelte.d.ts +4 -0
  17. package/dist/TellegenViewer.svelte.d.ts.map +1 -0
  18. package/dist/api.d.ts +44 -0
  19. package/dist/api.d.ts.map +1 -0
  20. package/dist/api.js +137 -0
  21. package/dist/colors.d.ts +51 -0
  22. package/dist/colors.d.ts.map +1 -0
  23. package/dist/colors.js +140 -0
  24. package/dist/components/AppFooter.svelte +73 -0
  25. package/dist/components/AppFooter.svelte.d.ts +19 -0
  26. package/dist/components/AppFooter.svelte.d.ts.map +1 -0
  27. package/dist/components/AppHeader.svelte +405 -0
  28. package/dist/components/AppHeader.svelte.d.ts +4 -0
  29. package/dist/components/AppHeader.svelte.d.ts.map +1 -0
  30. package/dist/components/BindingLines.svelte +101 -0
  31. package/dist/components/BindingLines.svelte.d.ts +4 -0
  32. package/dist/components/BindingLines.svelte.d.ts.map +1 -0
  33. package/dist/components/BusPicker.svelte +227 -0
  34. package/dist/components/BusPicker.svelte.d.ts +4 -0
  35. package/dist/components/BusPicker.svelte.d.ts.map +1 -0
  36. package/dist/components/ControlPanel.svelte +154 -0
  37. package/dist/components/ControlPanel.svelte.d.ts +19 -0
  38. package/dist/components/ControlPanel.svelte.d.ts.map +1 -0
  39. package/dist/components/DemandSlider.svelte +271 -0
  40. package/dist/components/DemandSlider.svelte.d.ts +4 -0
  41. package/dist/components/DemandSlider.svelte.d.ts.map +1 -0
  42. package/dist/components/DisplayControls.svelte +81 -0
  43. package/dist/components/DisplayControls.svelte.d.ts +19 -0
  44. package/dist/components/DisplayControls.svelte.d.ts.map +1 -0
  45. package/dist/components/DropZone.svelte +46 -0
  46. package/dist/components/DropZone.svelte.d.ts +19 -0
  47. package/dist/components/DropZone.svelte.d.ts.map +1 -0
  48. package/dist/components/FormulationSelector.svelte +82 -0
  49. package/dist/components/FormulationSelector.svelte.d.ts +19 -0
  50. package/dist/components/FormulationSelector.svelte.d.ts.map +1 -0
  51. package/dist/components/LocalCaseDetails.svelte +253 -0
  52. package/dist/components/LocalCaseDetails.svelte.d.ts +4 -0
  53. package/dist/components/LocalCaseDetails.svelte.d.ts.map +1 -0
  54. package/dist/components/MulticonductorDetails.svelte +280 -0
  55. package/dist/components/MulticonductorDetails.svelte.d.ts +19 -0
  56. package/dist/components/MulticonductorDetails.svelte.d.ts.map +1 -0
  57. package/dist/components/NetworkStats.svelte +75 -0
  58. package/dist/components/NetworkStats.svelte.d.ts +19 -0
  59. package/dist/components/NetworkStats.svelte.d.ts.map +1 -0
  60. package/dist/components/PlacementCue.svelte +35 -0
  61. package/dist/components/PlacementCue.svelte.d.ts +19 -0
  62. package/dist/components/PlacementCue.svelte.d.ts.map +1 -0
  63. package/dist/components/RatingSlider.svelte +273 -0
  64. package/dist/components/RatingSlider.svelte.d.ts +4 -0
  65. package/dist/components/RatingSlider.svelte.d.ts.map +1 -0
  66. package/dist/components/RestoreDefaultsButton.svelte +34 -0
  67. package/dist/components/RestoreDefaultsButton.svelte.d.ts +19 -0
  68. package/dist/components/RestoreDefaultsButton.svelte.d.ts.map +1 -0
  69. package/dist/components/SensitivityReadout.svelte +126 -0
  70. package/dist/components/SensitivityReadout.svelte.d.ts +4 -0
  71. package/dist/components/SensitivityReadout.svelte.d.ts.map +1 -0
  72. package/dist/components/SizeLegend.svelte +55 -0
  73. package/dist/components/SizeLegend.svelte.d.ts +19 -0
  74. package/dist/components/SizeLegend.svelte.d.ts.map +1 -0
  75. package/dist/components/SolveCard.svelte +84 -0
  76. package/dist/components/SolveCard.svelte.d.ts +4 -0
  77. package/dist/components/SolveCard.svelte.d.ts.map +1 -0
  78. package/dist/components/TopMovers.svelte +68 -0
  79. package/dist/components/TopMovers.svelte.d.ts +19 -0
  80. package/dist/components/TopMovers.svelte.d.ts.map +1 -0
  81. package/dist/components.d.ts +19 -0
  82. package/dist/components.d.ts.map +1 -0
  83. package/dist/components.js +18 -0
  84. package/dist/context.svelte.d.ts +20 -0
  85. package/dist/context.svelte.d.ts.map +1 -0
  86. package/dist/context.svelte.js +43 -0
  87. package/dist/controller.svelte.d.ts +334 -0
  88. package/dist/controller.svelte.d.ts.map +1 -0
  89. package/dist/controller.svelte.js +2026 -0
  90. package/dist/display.d.ts +31 -0
  91. package/dist/display.d.ts.map +1 -0
  92. package/dist/display.js +63 -0
  93. package/dist/drop-classify.d.ts +48 -0
  94. package/dist/drop-classify.d.ts.map +1 -0
  95. package/dist/drop-classify.js +92 -0
  96. package/dist/format.d.ts +30 -0
  97. package/dist/format.d.ts.map +1 -0
  98. package/dist/format.js +49 -0
  99. package/dist/index.d.ts +21 -0
  100. package/dist/index.d.ts.map +1 -0
  101. package/dist/index.js +12 -0
  102. package/dist/map.d.ts +9 -0
  103. package/dist/map.d.ts.map +1 -0
  104. package/dist/map.js +4 -0
  105. package/dist/multiconductor.d.ts +63 -0
  106. package/dist/multiconductor.d.ts.map +1 -0
  107. package/dist/multiconductor.js +255 -0
  108. package/dist/state.svelte.d.ts +258 -0
  109. package/dist/state.svelte.d.ts.map +1 -0
  110. package/dist/state.svelte.js +323 -0
  111. package/dist/study-package.d.ts +9 -0
  112. package/dist/study-package.d.ts.map +1 -0
  113. package/dist/study-package.js +17 -0
  114. package/dist/styles.css +393 -0
  115. package/dist/synthetic-layout.d.ts +12 -0
  116. package/dist/synthetic-layout.d.ts.map +1 -0
  117. package/dist/synthetic-layout.js +271 -0
  118. package/dist/types.d.ts +6 -0
  119. package/dist/types.d.ts.map +1 -0
  120. package/dist/types.js +1 -0
  121. package/package.json +67 -0
@@ -0,0 +1,273 @@
1
+ <script lang="ts">
2
+ import { getController } from '../context.svelte.js';
3
+ import { fmt, signed } from '../format.js';
4
+
5
+ const ctrl = getController();
6
+
7
+ // Fill from the committed rating to the live thumb. The number line stays
8
+ // stable after release; only the committed tick moves.
9
+ const thumbSizePx = 14;
10
+ const bounds = $derived(ctrl.ratingBounds);
11
+ const sliderSpan = $derived(Math.max(bounds.max - bounds.min, 1e-9));
12
+ const valFrac = $derived(
13
+ Math.max(0, Math.min(1, (ctrl.ratingSliderValue - bounds.min) / sliderSpan))
14
+ );
15
+ const neutralFrac = $derived(
16
+ Math.max(0, Math.min(1, (ctrl.committedRating - bounds.min) / sliderSpan))
17
+ );
18
+ const thumbAlignedPos = (frac: number) =>
19
+ `calc(${frac * 100}% + ${(0.5 - frac) * thumbSizePx}px)`;
20
+ const valPos = $derived(thumbAlignedPos(valFrac));
21
+ const neutralPos = $derived(thumbAlignedPos(neutralFrac));
22
+ const fillLo = $derived(valFrac < neutralFrac ? valPos : neutralPos);
23
+ const fillHi = $derived(valFrac < neutralFrac ? neutralPos : valPos);
24
+ const sliderTip =
25
+ 'The black tick marks the last committed rating change. Drag the knob to preview a new thermal rating; release to solve and move the tick to that point.';
26
+ const disabledTip = "this line's rating is synthesized; not adjustable";
27
+ const scoreTip =
28
+ 'Gradient is the estimate of total cost change versus base before the solve finishes, from the engine preview at the committed point. Exact is the resolved OPF objective change.';
29
+ </script>
30
+
31
+ {#if ctrl.activeSolvable}
32
+ {@const c = ctrl.activeSolvable}
33
+ {@const branch = ctrl.selectedBranchData}
34
+ <div class="slider-block">
35
+ <div class="slider-head mono">
36
+ <span>&Delta; rating</span>
37
+ <span class="val">{signed(ctrl.ratingSliderValue)} MW</span>
38
+ </div>
39
+ <div class="line-row mono">
40
+ {#if branch}
41
+ <span class="dim"
42
+ >line {branch.from}&#8201;&ndash;&#8201;{branch.to} &middot; {fmt.format(
43
+ branch.rate_mw
44
+ )} MW</span
45
+ >
46
+ {/if}
47
+ <span class="dim">
48
+ {#if bounds.disabled}
49
+ not adjustable
50
+ {:else}
51
+ {fmt.format(bounds.min)} to +{fmt.format(bounds.max)} MW
52
+ {/if}
53
+ </span>
54
+ </div>
55
+ <div
56
+ class="slider-track"
57
+ style="--fill-lo:{fillLo}; --fill-hi:{fillHi}; --neutral-pos:{neutralPos}"
58
+ title={bounds.disabled ? disabledTip : sliderTip}
59
+ >
60
+ <input
61
+ type="range"
62
+ min={bounds.min}
63
+ max={bounds.max}
64
+ step="any"
65
+ disabled={bounds.disabled}
66
+ bind:value={ctrl.ratingSliderCurrent, ctrl.setRatingPreview}
67
+ aria-label="rating delta at selected line"
68
+ onpointerdown={() => ctrl.setRatingPreview(ctrl.ratingSliderValue)}
69
+ onkeydown={() => ctrl.setRatingPreview(ctrl.ratingSliderValue)}
70
+ onpointerup={(e) => ctrl.finishRatingInput(Number(e.currentTarget.value))}
71
+ onkeyup={(e) => ctrl.finishRatingInput(Number(e.currentTarget.value))}
72
+ onchange={(e) => ctrl.finishRatingInput(Number(e.currentTarget.value))}
73
+ />
74
+ </div>
75
+ <div class="rating-feedback" class:idle={!ctrl.previewing && !ctrl.isPerturbed(c)}>
76
+ <p class="pred mono dim" aria-hidden={!(ctrl.predictedDeltaObj !== null && ctrl.previewing)}>
77
+ {#if ctrl.predictedDeltaObj !== null && ctrl.previewing}
78
+ predicted &Delta;cost {signed(ctrl.predictedDeltaObj)} $/h
79
+ {:else}
80
+ &nbsp;
81
+ {/if}
82
+ </p>
83
+ <p class="score mono" aria-hidden={!(ctrl.gradientScore && ctrl.isPerturbed(c))}>
84
+ {#if ctrl.gradientScore && ctrl.isPerturbed(c)}
85
+ <span title={scoreTip}>
86
+ gradient {signed(ctrl.gradientScore.pred)} &middot; exact {signed(
87
+ ctrl.gradientScore.exact
88
+ )}
89
+ $/h
90
+ </span>
91
+ {:else}
92
+ &nbsp;
93
+ {/if}
94
+ </p>
95
+ <div class="reset-row">
96
+ {#if ctrl.isPerturbed(c)}
97
+ <button class="reset mono" onclick={() => ctrl.resetCase(c)}>reset rating</button>
98
+ {/if}
99
+ </div>
100
+ </div>
101
+ </div>
102
+ {/if}
103
+
104
+ <style>
105
+ .slider-block {
106
+ margin-top: 14px;
107
+ }
108
+
109
+ .slider-head {
110
+ display: flex;
111
+ justify-content: space-between;
112
+ font-size: 11.5px;
113
+ color: var(--text-secondary);
114
+ margin-bottom: 4px;
115
+ }
116
+
117
+ .slider-head .val {
118
+ color: var(--ink);
119
+ }
120
+
121
+ .line-row {
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: space-between;
125
+ gap: 8px;
126
+ margin: 6px 0 7px;
127
+ font-size: 10.5px;
128
+ }
129
+
130
+ .slider-track {
131
+ position: relative;
132
+ margin: 6px 0;
133
+ isolation: isolate;
134
+ }
135
+
136
+ .slider-track::before {
137
+ content: '';
138
+ position: absolute;
139
+ top: 50%;
140
+ left: var(--neutral-pos, 50%);
141
+ z-index: 0;
142
+ width: 2px;
143
+ height: 16px;
144
+ background: var(--ink);
145
+ border-radius: 1px;
146
+ box-shadow: 0 0 0 1px rgb(var(--paper-rgb) / 0.72);
147
+ transform: translate(-50%, -50%);
148
+ pointer-events: none;
149
+ }
150
+
151
+ input[type='range'] {
152
+ -webkit-appearance: none;
153
+ appearance: none;
154
+ position: relative;
155
+ z-index: 1;
156
+ width: 100%;
157
+ height: 4px;
158
+ padding: 7px 0;
159
+ box-sizing: content-box;
160
+ background:
161
+ linear-gradient(
162
+ 90deg,
163
+ transparent 0 var(--fill-lo, 0%),
164
+ rgb(var(--accent-rgb) / 0.32) var(--fill-lo, 0%) var(--fill-hi, 0%),
165
+ transparent var(--fill-hi, 0%) 100%
166
+ ),
167
+ var(--line);
168
+ background-clip: content-box;
169
+ border-radius: var(--radius-xs);
170
+ outline-offset: 4px;
171
+ margin: 0;
172
+ }
173
+
174
+ input[type='range']:disabled {
175
+ opacity: 0.45;
176
+ }
177
+
178
+ input[type='range']::-webkit-slider-thumb {
179
+ -webkit-appearance: none;
180
+ appearance: none;
181
+ width: 14px;
182
+ height: 14px;
183
+ border-radius: 50%;
184
+ background: var(--accent);
185
+ border: 2px solid var(--paper);
186
+ box-shadow: var(--shadow-thumb);
187
+ cursor: ew-resize;
188
+ transition:
189
+ transform var(--dur-fast) var(--ease-out),
190
+ box-shadow var(--dur-fast) var(--ease-out);
191
+ }
192
+
193
+ input[type='range']::-moz-range-thumb {
194
+ width: 14px;
195
+ height: 14px;
196
+ border-radius: 50%;
197
+ background: var(--accent);
198
+ border: 2px solid var(--paper);
199
+ box-shadow: var(--shadow-thumb);
200
+ cursor: ew-resize;
201
+ transition:
202
+ transform var(--dur-fast) var(--ease-out),
203
+ box-shadow var(--dur-fast) var(--ease-out);
204
+ }
205
+
206
+ input[type='range']:active::-webkit-slider-thumb,
207
+ input[type='range']:focus-visible::-webkit-slider-thumb {
208
+ transform: scale(1.15);
209
+ box-shadow: 0 2px 7px rgb(var(--ink-rgb) / 0.35);
210
+ }
211
+
212
+ input[type='range']:active::-moz-range-thumb,
213
+ input[type='range']:focus-visible::-moz-range-thumb {
214
+ transform: scale(1.15);
215
+ box-shadow: 0 2px 7px rgb(var(--ink-rgb) / 0.35);
216
+ }
217
+
218
+ /* Coarse pointers: a larger track and thumb for a comfortable touch target.
219
+ The input's own border box grows to a 44px tap target (padding is symmetric,
220
+ so the thumb -- centered on the input's box by default -- stays centered on
221
+ the thinner painted line); background-clip keeps the visible line itself thin. */
222
+ @media (hover: none), (pointer: coarse) {
223
+ input[type='range'] {
224
+ height: 6px;
225
+ padding: 19px 0;
226
+ box-sizing: content-box;
227
+ background-clip: content-box;
228
+ }
229
+
230
+ input[type='range']::-webkit-slider-thumb {
231
+ width: 20px;
232
+ height: 20px;
233
+ }
234
+
235
+ input[type='range']::-moz-range-thumb {
236
+ width: 20px;
237
+ height: 20px;
238
+ }
239
+ }
240
+
241
+ .pred {
242
+ margin: 2px 0 0;
243
+ font-size: 11px;
244
+ min-height: 16px;
245
+ }
246
+
247
+ .score {
248
+ margin: 8px 0 0;
249
+ font-size: 11px;
250
+ color: var(--ink);
251
+ min-height: 16px;
252
+ }
253
+
254
+ .rating-feedback {
255
+ min-height: 78px;
256
+ }
257
+
258
+ /* On a fresh selection (no preview, no perturbation) the predicted/gradient/
259
+ reset rows are empty; collapse the reserved block so the panel isn't padded
260
+ with whitespace. The reservation returns during interaction to avoid jumps. */
261
+ .rating-feedback.idle {
262
+ min-height: 0;
263
+ }
264
+
265
+ .rating-feedback.idle .pred,
266
+ .rating-feedback.idle .score {
267
+ display: none;
268
+ }
269
+
270
+ .reset-row {
271
+ min-height: 28px;
272
+ }
273
+ </style>
@@ -0,0 +1,4 @@
1
+ declare const RatingSlider: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type RatingSlider = ReturnType<typeof RatingSlider>;
3
+ export default RatingSlider;
4
+ //# sourceMappingURL=RatingSlider.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RatingSlider.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/RatingSlider.svelte.ts"],"names":[],"mappings":"AAqFA,QAAA,MAAM,YAAY,2DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,34 @@
1
+ <script lang="ts">
2
+ import { getController, getUiConfig } from '../context.svelte.js';
3
+
4
+ const ctrl = getController();
5
+ const config = getUiConfig();
6
+ </script>
7
+
8
+ {#if config.loadDefaultCases && ctrl.casesLoaded && ctrl.hiddenDefaults.size > 0}
9
+ <button class="restore-defaults mono" onclick={ctrl.restoreDefaultCases}>
10
+ &#8634; restore default cases
11
+ </button>
12
+ {/if}
13
+
14
+ <style>
15
+ .restore-defaults {
16
+ position: absolute;
17
+ bottom: 34px;
18
+ left: 20px;
19
+ z-index: 10;
20
+ padding: 6px 11px;
21
+ background: var(--panel);
22
+ border: 1px solid var(--line);
23
+ border-radius: 3px;
24
+ color: var(--text-secondary);
25
+ font-size: 11px;
26
+ cursor: pointer;
27
+ box-shadow: 0 2px 10px rgba(32, 36, 43, 0.1);
28
+ }
29
+
30
+ .restore-defaults:hover {
31
+ border-color: var(--accent);
32
+ color: var(--text-accent);
33
+ }
34
+ </style>
@@ -0,0 +1,19 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const RestoreDefaultsButton: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type RestoreDefaultsButton = InstanceType<typeof RestoreDefaultsButton>;
18
+ export default RestoreDefaultsButton;
19
+ //# sourceMappingURL=RestoreDefaultsButton.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RestoreDefaultsButton.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/RestoreDefaultsButton.svelte.ts"],"names":[],"mappings":"AAsBA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,qBAAqB;;kBAA+E,CAAC;AACzF,KAAK,qBAAqB,GAAG,YAAY,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAC1E,eAAe,qBAAqB,CAAC"}
@@ -0,0 +1,126 @@
1
+ <script lang="ts">
2
+ import { sensGradient } from '../colors.js';
3
+ import { getAppState, getController } from '../context.svelte.js';
4
+ import { signedExp } from '../format.js';
5
+
6
+ const app = getAppState();
7
+ const ctrl = getController();
8
+ const busUnitTitle =
9
+ 'LMP is measured in $/MWh and demand is perturbed in MW, so dLMP/dd has units ($/MWh)/MW. ' +
10
+ 'DC LMPs move only when a binding constraint changes, so most nudges in this slider range ' +
11
+ 'shift prices very little unless the active set changes.';
12
+ const branchUnitTitle =
13
+ 'LMP is measured in $/MWh and the rating is perturbed in MVA, so dLMP/dfmax has units ' +
14
+ "($/MWh)/MVA. The column is zero unless the line's limit is binding.";
15
+
16
+ // Branch mode: the selection is a line and the column is ∂LMP/∂rating; the
17
+ // legend and preview pipeline are identical (both columns are bus-keyed).
18
+ const branchMode = $derived(app.selectedBranch !== null);
19
+ const unitTitle = $derived(branchMode ? branchUnitTitle : busUnitTitle);
20
+ const units = $derived(branchMode ? '($/MWh)/MVA' : '($/MWh)/MW');
21
+
22
+ const previewStep = $derived(
23
+ branchMode
24
+ ? ctrl.ratingSliderValue - ctrl.committedRating
25
+ : ctrl.sliderValue - ctrl.committedDelta
26
+ );
27
+
28
+ // Label the selected line by its bus pair, matching BindingLines and
29
+ // RatingSlider; fall back to the raw branch id while the network loads.
30
+ const branchLabel = $derived.by(() => {
31
+ const b = ctrl.selectedBranchData;
32
+ return b ? `${b.from} – ${b.to}` : String(app.selectedBranch);
33
+ });
34
+ </script>
35
+
36
+ <div class="mode">
37
+ <span class="chip">
38
+ {ctrl.previewing ? 'LMP preview' : branchMode ? '∂LMP/∂rating' : '∂LMP/∂d'}
39
+ </span>
40
+ <span class="mono dim">
41
+ {#if branchMode}line {branchLabel}{:else}bus {app.selectedBus}{/if}
42
+ </span>
43
+ <button class="mono" onclick={ctrl.clearSelection}>esc&nbsp;clear</button>
44
+ </div>
45
+ <div class="sensitivity-readout" aria-live="polite">
46
+ {#if ctrl.previewing}
47
+ <p class="dim small">
48
+ {ctrl.activeSolvable?.solving
49
+ ? 'Exact solve running; the map keeps the LMP preview.'
50
+ : 'First order LMP preview. Release for the exact solve.'}
51
+ </p>
52
+ {#if ctrl.sensSummary?.flat}
53
+ <div class="legend flat" style:background={ctrl.flatSensBackground}></div>
54
+ <div class="legend-labels mono single">
55
+ <span>uniform {signedExp(ctrl.sensSummary.mean * previewStep)} $/MWh</span>
56
+ </div>
57
+ {:else if ctrl.previewScale}
58
+ <!-- The bounds are fixed for the whole drag (column scale × full slider
59
+ deflection), so the ramp ends label the colors: full green/purple is
60
+ the predicted ΔLMP at full deflection, and intensity grows with the
61
+ step instead of renormalizing every frame. -->
62
+ <div class="legend" style:background={sensGradient}></div>
63
+ <div class="legend-labels mono">
64
+ <span>&minus;{ctrl.previewScale.toExponential(1)}</span>
65
+ <span>&Delta;LMP $/MWh</span>
66
+ <span>+{ctrl.previewScale.toExponential(1)}</span>
67
+ </div>
68
+ {/if}
69
+ {:else}
70
+ <p class="dim small sensitivity-copy" title={unitTitle}>
71
+ {#if branchMode}
72
+ LMP response per MVA of rating on line {branchLabel}.
73
+ {:else}
74
+ LMP response per MW of demand at bus {app.selectedBus}.
75
+ {/if}
76
+ <span class="hint-dot mono" title={unitTitle} aria-label={unitTitle}>i</span>
77
+ </p>
78
+ {#if ctrl.sensSummary?.flat}
79
+ <div class="legend flat" style:background={ctrl.flatSensBackground}></div>
80
+ <div class="legend-labels mono single">
81
+ <span>uniform {signedExp(ctrl.sensSummary.mean)} {units}</span>
82
+ </div>
83
+ {:else if ctrl.sensSummary}
84
+ <div class="legend" style:background={sensGradient}></div>
85
+ <div class="legend-labels mono">
86
+ <span>&minus;{ctrl.sensSummary.scale.toExponential(1)}</span>
87
+ <span>0</span>
88
+ <span>+{ctrl.sensSummary.scale.toExponential(1)}</span>
89
+ </div>
90
+ {:else if app.sensitivityLoading}
91
+ <div class="legend" style:background="var(--line)" style:opacity="0.4"></div>
92
+ <div class="legend-labels mono single">
93
+ <span class="blink">
94
+ computing {#if branchMode}&part;LMP/&part;fmax{:else}&part;LMP/&part;d{/if}&hellip;
95
+ </span>
96
+ </div>
97
+ {/if}
98
+ {/if}
99
+ </div>
100
+
101
+ <style>
102
+ .chip {
103
+ font-family: var(--font-mono);
104
+ font-size: 11px;
105
+ padding: 2px 8px;
106
+ border: 1px solid var(--accent);
107
+ color: var(--text-accent);
108
+ background: var(--accent-soft);
109
+ border-radius: 2px;
110
+ white-space: nowrap;
111
+ }
112
+
113
+ /* .mode > button lives in the global .mode block in app.css, not here: scoping
114
+ it would raise its specificity above the global @media (max-width: 760px)
115
+ override and break the mobile layout. See the note by .mode in app.css. */
116
+
117
+ .sensitivity-readout {
118
+ min-height: 58px;
119
+ }
120
+
121
+ .sensitivity-copy {
122
+ font-size: 11.5px;
123
+ line-height: 1.35;
124
+ white-space: nowrap;
125
+ }
126
+ </style>
@@ -0,0 +1,4 @@
1
+ declare const SensitivityReadout: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type SensitivityReadout = ReturnType<typeof SensitivityReadout>;
3
+ export default SensitivityReadout;
4
+ //# sourceMappingURL=SensitivityReadout.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SensitivityReadout.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/SensitivityReadout.svelte.ts"],"names":[],"mappings":"AAwGA,QAAA,MAAM,kBAAkB,2DAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,55 @@
1
+ <script lang="ts">
2
+ import { busRadius } from '../colors.js';
3
+
4
+ const SIZE_SAMPLES = [10, 100, 500];
5
+ </script>
6
+
7
+ <div class="sizes">
8
+ {#each SIZE_SAMPLES as mw (mw)}
9
+ <span class="size mono">
10
+ <i style:width="{2 * busRadius(mw)}px" style:height="{2 * busRadius(mw)}px"></i>
11
+ {mw}
12
+ </span>
13
+ {/each}
14
+ <span class="mono dim caption">MW, max(load,&#8201;gen)</span>
15
+ </div>
16
+
17
+ <style>
18
+ .sizes {
19
+ display: flex;
20
+ align-items: center;
21
+ gap: 12px;
22
+ font-size: 10px;
23
+ color: var(--text-secondary);
24
+ }
25
+
26
+ .size {
27
+ display: inline-flex;
28
+ align-items: center;
29
+ gap: 5px;
30
+ }
31
+
32
+ .size i {
33
+ display: inline-block;
34
+ border-radius: 50%;
35
+ background: rgba(212, 116, 34, 0.55);
36
+ border: 1px solid rgba(46, 42, 34, 0.45);
37
+ }
38
+
39
+ .caption {
40
+ margin-left: auto;
41
+ font-size: 9.5px;
42
+ }
43
+
44
+ @media (max-width: 760px) {
45
+ .sizes {
46
+ flex-wrap: wrap;
47
+ gap: 8px 12px;
48
+ }
49
+
50
+ .caption {
51
+ margin-left: 0;
52
+ flex-basis: 100%;
53
+ }
54
+ }
55
+ </style>
@@ -0,0 +1,19 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const SizeLegend: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type SizeLegend = InstanceType<typeof SizeLegend>;
18
+ export default SizeLegend;
19
+ //# sourceMappingURL=SizeLegend.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SizeLegend.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/SizeLegend.svelte.ts"],"names":[],"mappings":"AAyBA,UAAU,kCAAkC,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,KAAK,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,MAAM;IACpM,KAAK,OAAO,EAAE,OAAO,QAAQ,EAAE,2BAA2B,CAAC,KAAK,CAAC,GAAG,OAAO,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC;IACjK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,KAAK,CAAA;KAAC,GAAG,OAAO,GAAG;QAAE,IAAI,CAAC,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;IACtG,YAAY,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAKD,QAAA,MAAM,UAAU;;kBAA+E,CAAC;AAC9E,KAAK,UAAU,GAAG,YAAY,CAAC,OAAO,UAAU,CAAC,CAAC;AACpD,eAAe,UAAU,CAAC"}
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import { getController } from '../context.svelte.js';
3
+ import { formulationLabel, solveMetaLabel } from '../format.js';
4
+ import Sparkline from '../Sparkline.svelte';
5
+
6
+ const ctrl = getController();
7
+ const active = $derived(ctrl.activeSolvable);
8
+ const iterations = $derived(active?.iterations ?? []);
9
+ </script>
10
+
11
+ {#if active && (active.solving || active.solveMs != null)}
12
+ <div class="solvecard">
13
+ <div class="solvecard-head mono">
14
+ <span><b>OPF solve</b></span>
15
+ </div>
16
+ {#if iterations.length > 1}
17
+ {#key iterations}
18
+ <Sparkline {iterations} />
19
+ {/key}
20
+ {/if}
21
+ <div class="solve-meta mono dim">
22
+ <span class="solve-formulation">{formulationLabel(active.formulation)}</span>
23
+ <span>{solveMetaLabel(active)}</span>
24
+ {#if active.solveMs != null}<span>{active.solveMs} ms</span>{/if}
25
+ </div>
26
+ {#if active.solveBackend === 'rust-server' && active.solveFallbackReason}
27
+ <p class="fallback-reason mono dim" title={active.solveFallbackReason}>
28
+ fallback: {active.solveFallbackReason}
29
+ </p>
30
+ {/if}
31
+ </div>
32
+ {/if}
33
+
34
+ <style>
35
+ .solvecard {
36
+ position: absolute;
37
+ top: 76px;
38
+ right: 20px;
39
+ z-index: 10;
40
+ width: 300px;
41
+ padding: 13px 15px 11px;
42
+ background: var(--panel);
43
+ border: 1px solid var(--line);
44
+ border-radius: 3px;
45
+ backdrop-filter: blur(6px);
46
+ box-shadow: 0 4px 24px rgba(32, 36, 43, 0.08);
47
+ animation: rise 0.3s ease-out both;
48
+ }
49
+
50
+ .solvecard-head {
51
+ font-size: 10.5px;
52
+ margin-bottom: 6px;
53
+ white-space: nowrap;
54
+ }
55
+
56
+ .solve-meta {
57
+ display: flex;
58
+ align-items: center;
59
+ flex-wrap: wrap;
60
+ gap: 10px 14px;
61
+ font-size: 10px;
62
+ margin-top: 6px;
63
+ }
64
+
65
+ .solve-formulation {
66
+ color: var(--text-accent);
67
+ }
68
+
69
+ .fallback-reason {
70
+ margin: 6px 0 0;
71
+ font-size: 10px;
72
+ line-height: 1.35;
73
+ overflow-wrap: anywhere;
74
+ }
75
+
76
+ @media (max-width: 760px) {
77
+ .solvecard {
78
+ top: 184px;
79
+ left: auto;
80
+ right: 10px;
81
+ width: min(300px, calc(100% - 20px));
82
+ }
83
+ }
84
+ </style>
@@ -0,0 +1,4 @@
1
+ declare const SolveCard: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type SolveCard = ReturnType<typeof SolveCard>;
3
+ export default SolveCard;
4
+ //# sourceMappingURL=SolveCard.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SolveCard.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/SolveCard.svelte.ts"],"names":[],"mappings":"AA4CA,QAAA,MAAM,SAAS,2DAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}