@tellegen/svelte 0.4.0 → 0.4.2

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 (45) hide show
  1. package/README.md +38 -10
  2. package/dist/Sparkline.svelte +4 -4
  3. package/dist/TellegenMap.svelte +80 -66
  4. package/dist/TellegenProvider.svelte +29 -13
  5. package/dist/colors.d.ts +37 -1
  6. package/dist/colors.js +63 -0
  7. package/dist/components/AppFooter.svelte +4 -4
  8. package/dist/components/AppHeader.svelte +15 -19
  9. package/dist/components/BindingLines.svelte +10 -9
  10. package/dist/components/BusPicker.svelte +5 -6
  11. package/dist/components/ControlPanel.svelte +16 -11
  12. package/dist/components/ControlPanel.svelte.d.ts +6 -1
  13. package/dist/components/DemandSlider.svelte +26 -29
  14. package/dist/components/DiagramCanvas.svelte +16 -4
  15. package/dist/components/DisplayControls.svelte +5 -24
  16. package/dist/components/DropZone.svelte +7 -3
  17. package/dist/components/DropZone.svelte.d.ts +6 -17
  18. package/dist/components/FormulationSelector.svelte +2 -7
  19. package/dist/components/LocalCaseDetails.svelte +126 -126
  20. package/dist/components/LocalCaseDetails.svelte.d.ts +6 -1
  21. package/dist/components/MulticonductorDetails.svelte +48 -38
  22. package/dist/components/NetworkStats.svelte +14 -16
  23. package/dist/components/PanelCard.svelte +1 -1
  24. package/dist/components/PlacementCue.svelte +1 -1
  25. package/dist/components/RatingSlider.svelte +22 -23
  26. package/dist/components/RestoreDefaultsButton.svelte +2 -3
  27. package/dist/components/SensitivityReadout.svelte +9 -27
  28. package/dist/components/SolveCard.svelte +69 -35
  29. package/dist/components/TopMovers.svelte +5 -5
  30. package/dist/context.svelte.d.ts +6 -2
  31. package/dist/context.svelte.js +4 -2
  32. package/dist/controller.svelte.d.ts +7 -0
  33. package/dist/controller.svelte.js +37 -8
  34. package/dist/display.d.ts +3 -2
  35. package/dist/display.js +28 -11
  36. package/dist/format.d.ts +1 -9
  37. package/dist/format.js +1 -14
  38. package/dist/index.d.ts +1 -1
  39. package/dist/map.d.ts +1 -1
  40. package/dist/map.js +1 -1
  41. package/dist/state.svelte.d.ts +6 -1
  42. package/dist/state.svelte.js +1 -0
  43. package/dist/styles.css +3 -0
  44. package/dist/types.d.ts +1 -1
  45. package/package.json +10 -6
@@ -9,15 +9,10 @@
9
9
  {#if ctrl.activeSolvable}
10
10
  {@const c = ctrl.activeSolvable}
11
11
  <div class="formulation">
12
- <label
13
- class="formulation-row mono"
14
- for="formulation-select"
15
- title={formulationHint(c.formulation)}
16
- >
12
+ <label class="formulation-row" for="formulation-select" title={formulationHint(c.formulation)}>
17
13
  <span>Calculation</span>
18
14
  <select
19
15
  id="formulation-select"
20
- class="mono"
21
16
  disabled={c.solving}
22
17
  value={c.formulation}
23
18
  onchange={(e) => ctrl.changeFormulation(c, e.currentTarget.value as Formulation)}
@@ -52,7 +47,7 @@
52
47
  }
53
48
 
54
49
  .formulation-row select {
55
- font-family: var(--font-mono);
50
+ font-family: inherit;
56
51
  font-size: 13px;
57
52
  padding: 3px 22px 3px 8px;
58
53
  border: 1px solid var(--line);
@@ -1,9 +1,13 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
2
3
  import { getAppState, getController } from '../context.svelte.js';
3
4
  import { fmt } from '../format.js';
4
5
  import type { LocalCase } from '../state.svelte.js';
5
6
  import { formatPowerIoDiagnostic, type PowerIoDiagnostic } from '@tellegen/engine';
6
7
 
8
+ /** Optional host content rendered under the case stats. */
9
+ let { note }: { note?: Snippet<[LocalCase]> } = $props();
10
+
7
11
  const app = getAppState();
8
12
  const ctrl = getController();
9
13
 
@@ -22,6 +26,23 @@
22
26
  let exportOpen = $state(false);
23
27
  let exportDiagnostics = $state<PowerIoDiagnostic[]>([]);
24
28
 
29
+ /** Where the map coordinates came from. */
30
+ function coordinateSource(lc: LocalCase): string {
31
+ if (!lc.view) return 'None';
32
+ switch (lc.coordsKind) {
33
+ case 'file':
34
+ return 'From file';
35
+ case 'geofile':
36
+ return lc.geoSource ? `From ${lc.geoSource}` : 'From geographic file';
37
+ case 'manual':
38
+ return 'Manual layout';
39
+ case 'synthetic':
40
+ return lc.summary?.coords_method === 'generated' ? 'Generated' : 'Synthetic layout';
41
+ default:
42
+ return 'None';
43
+ }
44
+ }
45
+
25
46
  async function saveCase(lc: LocalCase) {
26
47
  busy = true;
27
48
  exportDiagnostics = [];
@@ -64,137 +85,128 @@
64
85
 
65
86
  {#if app.activeLocal}
66
87
  {@const lc = app.activeLocal}
67
- <h2>{lc.label} <span class="region mono">via {lc.fileName}</span></h2>
88
+ <h2>{lc.label}</h2>
68
89
  {#if lc.substations}
69
- <dl class="mono">
90
+ <dl>
91
+ <div>
92
+ <dt>Substations</dt>
93
+ <dd class="mono">{lc.substations.points.length}</dd>
94
+ </div>
95
+ <div>
96
+ <dt>Positions</dt>
97
+ <dd>Inferred from diagram</dd>
98
+ </div>
70
99
  <div>
71
- <dt>substations</dt>
72
- <dd>{lc.substations.points.length}</dd>
100
+ <dt>File</dt>
101
+ <dd class="mono">{lc.fileName}</dd>
73
102
  </div>
74
103
  </dl>
75
- <p class="footnote mono">
76
- display only: positions inferred from the PowerWorld diagram, not surveyed latitude and
77
- longitude
78
- </p>
79
- <p class="footnote mono">decoded in your browser by powerio (wasm); never uploaded</p>
80
104
  {:else if lc.summary}
81
- <dl class="mono">
105
+ <dl>
82
106
  <div>
83
- <dt>buses</dt>
84
- <dd>{lc.summary.n_bus}</dd>
107
+ <dt>Buses</dt>
108
+ <dd class="mono">{lc.summary.n_bus}</dd>
85
109
  </div>
86
110
  <div>
87
- <dt>branches</dt>
88
- <dd>{lc.summary.n_branch}</dd>
111
+ <dt>Branches</dt>
112
+ <dd class="mono">{lc.summary.n_branch}</dd>
89
113
  </div>
90
114
  <div>
91
- <dt>generators</dt>
92
- <dd>{lc.summary.n_gen}</dd>
115
+ <dt>Generators</dt>
116
+ <dd class="mono">{lc.summary.n_gen}</dd>
93
117
  </div>
94
118
  <div>
95
- <dt>load</dt>
96
- <dd>{fmt.format(lc.summary.load_mw)} MW</dd>
119
+ <dt>Load</dt>
120
+ <dd class="mono">{fmt.format(lc.summary.load_mw)} MW</dd>
97
121
  </div>
98
122
  <div>
99
- <dt>gen capacity</dt>
100
- <dd>{fmt.format(lc.summary.gen_mw)} MW</dd>
123
+ <dt>Generation capacity</dt>
124
+ <dd class="mono">{fmt.format(lc.summary.gen_mw)} MW</dd>
101
125
  </div>
102
126
  <div>
103
- <dt>base MVA</dt>
104
- <dd>{fmt.format(lc.summary.base_mva)}</dd>
127
+ <dt>Base MVA</dt>
128
+ <dd class="mono">{fmt.format(lc.summary.base_mva)}</dd>
105
129
  </div>
106
- </dl>
107
- {#if lc.displayMode === 'diagram' && lc.diagram}
108
- <p class="footnote mono">Drawing: {lc.diagram.name}</p>
109
- {#if lc.diagram.warnings.length}
110
- <details>
111
- <summary>Drawing details</summary>
112
- <ul class="warnings mono">
113
- {#each lc.diagram.warnings as warning, i (i)}<li>{warning}</li>{/each}
114
- </ul>
115
- </details>
116
- {/if}
117
- {:else if !lc.view}
118
- <p class="footnote mono">
119
- no coordinates in this file: click the map or drop a geographic file
120
- </p>
121
- {:else if lc.coordsKind === 'synthetic' && lc.summary.coords_method === 'generated'}
122
- <p class="footnote mono">coordinates: synthetic grid sited on real geography</p>
123
- {:else if lc.coordsKind === 'synthetic' || lc.coordsKind === 'manual'}
124
- <p class="footnote mono">
125
- coordinates: {lc.coordsKind} topology layout centered where it was placed
126
- </p>
127
- {:else if lc.coordsKind === 'geofile'}
128
- <p class="footnote mono">
129
- coordinates: geographic file data from {lc.geoSource}
130
- </p>
131
- {/if}
132
- <p class="footnote mono">parsed in your browser by powerio (wasm); never uploaded</p>
133
- {#if lc.studyInputJson}
134
- <div class="case-actions">
135
- <button class="reset mono" disabled={busy} onclick={() => saveCase(lc)}>
136
- save PowerIO module (.json)
137
- </button>
138
- {#if lc.formulation === 'dcopf' && lc.solution}
139
- <button class="reset mono" disabled={busy} onclick={() => saveSolution(lc)}>
140
- save exact solution (.json)
141
- </button>
142
- {/if}
143
- <div class="export">
144
- <button
145
- class="reset mono"
146
- disabled={busy}
147
- aria-expanded={exportOpen}
148
- onclick={() => (exportOpen = !exportOpen)}
149
- >
150
- export committed state…
151
- </button>
152
- {#if exportOpen}
153
- <ul class="export-menu mono">
154
- {#each EXPORT_FORMATS as f (f.token)}
155
- <li>
156
- <button
157
- class="reset mono"
158
- disabled={busy}
159
- onclick={() => exportCase(lc, f.token)}
160
- >
161
- {f.label}
162
- </button>
163
- </li>
164
- {/each}
165
- </ul>
166
- {/if}
130
+ {#if lc.displayMode === 'diagram' && lc.diagram}
131
+ <div>
132
+ <dt>Drawing</dt>
133
+ <dd>{lc.diagram.name}</dd>
167
134
  </div>
168
- {#if lc.view || lc.diagram}
169
- <button class="reset mono" disabled={busy} onclick={() => downloadLayout(lc)}>
170
- {lc.displayMode === 'diagram'
171
- ? 'Download drawing (.geo.json)'
172
- : 'Download geography (.geo.json)'}
173
- </button>
174
- {/if}
135
+ {:else}
136
+ <div>
137
+ <dt>Coordinates</dt>
138
+ <dd>{coordinateSource(lc)}</dd>
139
+ </div>
140
+ {/if}
141
+ <div>
142
+ <dt>File</dt>
143
+ <dd class="mono">{lc.fileName}</dd>
175
144
  </div>
176
- {#if exportDiagnostics.length > 0}
177
- <ul class="warnings mono">
178
- {#each exportDiagnostics.slice(0, 4) as diagnostic, i (i)}
179
- <li>{formatPowerIoDiagnostic(diagnostic)}</li>
180
- {/each}
181
- {#if exportDiagnostics.length > 4}
182
- <li>+{exportDiagnostics.length - 4} more</li>
183
- {/if}
145
+ </dl>
146
+ {#if lc.displayMode === 'diagram' && lc.diagram?.warnings.length}
147
+ <details>
148
+ <summary>Drawing details</summary>
149
+ <ul class="warnings">
150
+ {#each lc.diagram.warnings as warning, i (i)}<li>{warning}</li>{/each}
184
151
  </ul>
152
+ </details>
153
+ {/if}
154
+ {/if}
155
+ {@render note?.(lc)}
156
+ {#if lc.summary && lc.studyInputJson}
157
+ <div class="case-actions">
158
+ <button class="reset" disabled={busy} onclick={() => saveCase(lc)}>Save case</button>
159
+ {#if lc.formulation === 'dcopf' && lc.solution}
160
+ <button class="reset" disabled={busy} onclick={() => saveSolution(lc)}>Save solution</button
161
+ >
162
+ {/if}
163
+ <button
164
+ class="reset"
165
+ disabled={busy}
166
+ aria-expanded={exportOpen}
167
+ aria-controls="case-export-formats"
168
+ onclick={() => (exportOpen = !exportOpen)}
169
+ >
170
+ Export as
171
+ </button>
172
+ {#if lc.view || lc.diagram}
173
+ <button class="reset" disabled={busy} onclick={() => downloadLayout(lc)}>
174
+ {lc.displayMode === 'diagram' ? 'Save drawing' : 'Save geography'}
175
+ </button>
185
176
  {/if}
177
+ </div>
178
+ {#if exportOpen}
179
+ <ul class="case-actions" id="case-export-formats" aria-label="Export formats">
180
+ {#each EXPORT_FORMATS as f (f.token)}
181
+ <li>
182
+ <button class="reset" disabled={busy} onclick={() => exportCase(lc, f.token)}>
183
+ {f.label}
184
+ </button>
185
+ </li>
186
+ {/each}
187
+ </ul>
188
+ {/if}
189
+ {#if exportDiagnostics.length > 0}
190
+ <ul class="warnings">
191
+ {#each exportDiagnostics.slice(0, 4) as diagnostic, i (i)}
192
+ <li>{formatPowerIoDiagnostic(diagnostic)}</li>
193
+ {/each}
194
+ {#if exportDiagnostics.length > 4}
195
+ <li>+{exportDiagnostics.length - 4} more</li>
196
+ {/if}
197
+ </ul>
186
198
  {/if}
187
199
  {/if}
188
200
  {#if lc.displayMode !== 'diagram' && lc.topology && lc.coordsKind !== 'file'}
189
- <button class="reset mono" onclick={() => ctrl.moveLocalCase(lc)}>
201
+ <button class="reset" onclick={() => ctrl.moveLocalCase(lc)}>
190
202
  {lc.coordsKind === 'synthetic_pending'
191
- ? 'place on map'
203
+ ? 'Place on map'
192
204
  : lc.coordsKind === 'geofile'
193
- ? 'place manually'
194
- : 'move layout'}
205
+ ? 'Place manually'
206
+ : 'Move layout'}
195
207
  </button>
196
208
  {/if}
197
- <button class="reset mono" onclick={() => ctrl.removeLocalCase(lc)}>remove</button>
209
+ <button class="reset" onclick={() => ctrl.removeLocalCase(lc)}>Remove</button>
198
210
  {/if}
199
211
 
200
212
  <style>
@@ -212,22 +224,25 @@
212
224
  dl div {
213
225
  display: flex;
214
226
  justify-content: space-between;
227
+ gap: 12px;
215
228
  padding: 3px 0;
216
229
  }
217
230
 
218
231
  dt {
232
+ flex: none;
219
233
  color: var(--text-secondary);
220
234
  }
221
235
 
222
236
  dd {
237
+ min-width: 0;
223
238
  margin: 0;
239
+ text-align: right;
240
+ overflow-wrap: anywhere;
224
241
  }
225
242
 
226
- .footnote {
227
- margin: 8px 0 0;
243
+ details {
244
+ margin-top: 8px;
228
245
  font-size: 12px;
229
- color: var(--text-tertiary);
230
- letter-spacing: 0;
231
246
  }
232
247
 
233
248
  .warnings {
@@ -243,27 +258,12 @@
243
258
  display: flex;
244
259
  flex-wrap: wrap;
245
260
  gap: 8px;
246
- margin-top: 10px;
247
- }
248
-
249
- .export {
250
- position: relative;
251
- }
252
-
253
- .export-menu {
254
- position: absolute;
255
- z-index: 1;
256
- margin: 4px 0 0;
257
- padding: 4px;
261
+ margin: 10px 0 0;
262
+ padding: 0;
258
263
  list-style: none;
259
- min-width: 100%;
260
- background: var(--surface, var(--surface-menu));
261
- border: 1px solid var(--border, rgba(0, 0, 0, 0.15));
262
- border-radius: 6px;
263
- box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
264
264
  }
265
265
 
266
- .export-menu li {
267
- white-space: nowrap;
266
+ .case-actions .reset {
267
+ margin-top: 0;
268
268
  }
269
269
  </style>
@@ -1,3 +1,8 @@
1
- declare const LocalCaseDetails: import("svelte").Component<Record<string, never>, {}, "">;
1
+ import type { Snippet } from 'svelte';
2
+ import type { LocalCase } from '../state.svelte.js';
3
+ type $$ComponentProps = {
4
+ note?: Snippet<[LocalCase]>;
5
+ };
6
+ declare const LocalCaseDetails: import("svelte").Component<$$ComponentProps, {}, "">;
2
7
  type LocalCaseDetails = ReturnType<typeof LocalCaseDetails>;
3
8
  export default LocalCaseDetails;
@@ -46,51 +46,60 @@
46
46
  {#if app.activeMulti}
47
47
  {@const mc = app.activeMulti}
48
48
  {@const s = mc.summary}
49
- <h2>{mc.label} <span class="region mono">via {mc.fileName}</span></h2>
49
+ <h2>{mc.label}</h2>
50
50
  {#if s}
51
- <p class="tag mono">Multiconductor</p>
51
+ <p class="tag">Multiconductor</p>
52
52
  <details class="case-details">
53
53
  <summary>{s.n_bus} buses, {s.n_edge} {s.n_edge === 1 ? 'branch' : 'branches'}</summary>
54
- <dl class="mono">
54
+ <dl>
55
55
  <div>
56
- <dt>buses</dt>
57
- <dd>{s.n_bus}</dd>
56
+ <dt>Buses</dt>
57
+ <dd class="mono">{s.n_bus}</dd>
58
58
  </div>
59
59
  <div>
60
60
  <dt>Lines / switches / transformers</dt>
61
- <dd>{s.n_line} / {s.n_switch} / {s.n_transformer}</dd>
61
+ <dd class="mono">{s.n_line} / {s.n_switch} / {s.n_transformer}</dd>
62
62
  </div>
63
63
  <div>
64
- <dt>load</dt>
65
- <dd>{fmt.format(s.load_kw)} kW</dd>
64
+ <dt>Load</dt>
65
+ <dd class="mono">{fmt.format(s.load_kw)} kW</dd>
66
66
  </div>
67
67
  <div>
68
68
  <dt>Generation capacity</dt>
69
- <dd>{fmt.format(s.gen_kw)} kW</dd>
69
+ <dd class="mono">{fmt.format(s.gen_kw)} kW</dd>
70
70
  </div>
71
71
  <div>
72
- <dt>sources / loads / gens</dt>
73
- <dd>{s.n_source} / {s.n_load} / {s.n_generator}</dd>
72
+ <dt>Sources / loads / generators</dt>
73
+ <dd class="mono">{s.n_source} / {s.n_load} / {s.n_generator}</dd>
74
74
  </div>
75
75
  {#if s.n_ibr > 0 || s.n_shunt > 0 || (s.n_capacitor ?? 0) > 0}
76
76
  <div>
77
- <dt>IBRs / shunts / caps</dt>
78
- <dd>{s.n_ibr} / {s.n_shunt} / {s.n_capacitor ?? 0}</dd>
77
+ <dt>IBRs / shunts / capacitors</dt>
78
+ <dd class="mono">{s.n_ibr} / {s.n_shunt} / {s.n_capacitor ?? 0}</dd>
79
79
  </div>
80
80
  {/if}
81
+ <div>
82
+ <dt>File</dt>
83
+ <dd class="mono">{mc.fileName}</dd>
84
+ </div>
81
85
  </dl>
82
86
  </details>
83
87
 
84
- <p class="footnote mono">
85
- {mc.coordsKind === 'geographic'
86
- ? 'Geographic coordinates'
87
- : mc.coordsKind === 'planar'
88
- ? 'Drawing coordinates'
89
- : 'Generated diagram'}
90
- </p>
88
+ <dl>
89
+ <div>
90
+ <dt>Coordinates</dt>
91
+ <dd>
92
+ {mc.coordsKind === 'geographic'
93
+ ? 'Geographic'
94
+ : mc.coordsKind === 'planar'
95
+ ? 'Drawing'
96
+ : 'Generated diagram'}
97
+ </dd>
98
+ </div>
99
+ </dl>
91
100
  <div class="calculation-actions">
92
101
  {#if mc.solving}
93
- <span role="status">Calculating...</span>{#if mc.solveAbort}<button
102
+ <span role="status">Calculating&hellip;</span>{#if mc.solveAbort}<button
94
103
  onclick={() => mc.solveAbort?.abort()}>Cancel</button
95
104
  >{/if}
96
105
  {:else}
@@ -110,27 +119,27 @@
110
119
  notices.push({
111
120
  kind: 'warning',
112
121
  title: 'AC power flow unavailable',
113
- details: mc.mcPfReason ?? 'This case cannot run AC power flow in this browser'
122
+ details: mc.mcPfReason ?? 'AC power flow does not support this case'
114
123
  })}>Why unavailable</button
115
124
  >
116
125
  {/if}
117
126
  {/if}
118
127
  </div>
119
128
  {#if mc.result}<p class="footnote">Converged, {mc.result.iterations} iterations</p>
120
- <button class="reset mono" onclick={openResults}>View results in Studies</button>{/if}
129
+ <button class="reset" onclick={openResults}>View results in Studies</button>{/if}
121
130
  {/if}
122
131
 
123
132
  {#if mc.selectedBus}
124
133
  {@const b = mc.selectedBus}
125
134
  <hr />
126
- <h3 class="mono">bus {b.id}</h3>
135
+ <h3>Bus <span class="mono">{b.id}</span></h3>
127
136
  <div class="terminals">
128
137
  {#each b.terminals as t (t)}
129
138
  <span
130
139
  class="term"
131
140
  class:grounded={b.grounded.includes(t)}
132
141
  style={`--tc:${terminalColor(t)}`}
133
- title={b.grounded.includes(t) ? `terminal ${t} (grounded)` : `terminal ${t}`}
142
+ title={b.grounded.includes(t) ? `Terminal ${t}, grounded` : `Terminal ${t}`}
134
143
  >
135
144
  {t}{#if b.grounded.includes(t)}<span class="gnd" aria-hidden="true">&#9178;</span>{/if}
136
145
  </span>
@@ -145,7 +154,7 @@
145
154
  {/each}
146
155
  </div>
147
156
  {:else}
148
- <p class="footnote mono">no attachments on this bus</p>
157
+ <p class="footnote">No attachments</p>
149
158
  {/if}
150
159
  {@const loadBranches = mc.mcLoadBranches.filter((load) => load.bus === b.id)}
151
160
  {#if loadBranches.length > 0}
@@ -154,7 +163,7 @@
154
163
  {#each loadBranches as load (`${load.load}:${load.branch}`)}
155
164
  <div class="load-row">
156
165
  <span class="mono"
157
- >{load.load}{loadBranches.length > 1 ? ` · ${load.branch + 1}` : ''}</span
166
+ >{load.load}{loadBranches.length > 1 ? ` (${load.branch + 1})` : ''}</span
158
167
  >
159
168
  <label
160
169
  >P kW<input
@@ -200,7 +209,7 @@
200
209
  >
201
210
  </div>
202
211
  {/each}
203
- <p class="footnote">Changes re-solve automatically using the retained factorization.</p>
212
+ <p class="footnote">Changes solve automatically.</p>
204
213
  </div>
205
214
  {/if}
206
215
  {:else if mc.selectedEdge}
@@ -211,15 +220,19 @@
211
220
  {e.kind}
212
221
  {e.from}&#8201;&ndash;&#8201;{e.to}
213
222
  </h3>
214
- <dl class="mono">
223
+ <dl>
224
+ <div>
225
+ <dt>Phases</dt>
226
+ <dd class="mono">{e.n_phases}</dd>
227
+ </div>
215
228
  <div>
216
- <dt>phases</dt>
217
- <dd>{e.n_phases}&#966; , {e.conductors.length} conductors</dd>
229
+ <dt>Conductors</dt>
230
+ <dd class="mono">{e.conductors.length}</dd>
218
231
  </div>
219
232
  {#if e.kind === 'switch'}
220
233
  <div>
221
- <dt>state</dt>
222
- <dd>{e.closed ? 'closed' : 'open'}</dd>
234
+ <dt>State</dt>
235
+ <dd>{e.closed ? 'Closed' : 'Open'}</dd>
223
236
  </div>
224
237
  {/if}
225
238
  </dl>
@@ -232,8 +245,6 @@
232
245
  </span>
233
246
  {/each}
234
247
  </div>
235
- {:else if mc.placed}
236
- <p class="footnote mono">select a bus or a line to expand its detail</p>
237
248
  {/if}
238
249
 
239
250
  <div class="mc-legend mono">
@@ -251,8 +262,8 @@
251
262
  </span>
252
263
  </div>
253
264
 
254
- <button class="reset mono" onclick={() => app.requestFrame(mc.id)}>Fit case</button>
255
- <button class="reset mono" onclick={() => ctrl.removeMultiCase(mc)}>remove</button>
265
+ <button class="reset" onclick={() => app.requestFrame(mc.id)}>Fit case</button>
266
+ <button class="reset" onclick={() => ctrl.removeMultiCase(mc)}>Remove</button>
256
267
  {/if}
257
268
 
258
269
  <style>
@@ -309,7 +320,6 @@
309
320
  font-size: 12px;
310
321
  color: var(--text-secondary);
311
322
  letter-spacing: 0;
312
- text-transform: uppercase;
313
323
  }
314
324
 
315
325
  dl {
@@ -11,34 +11,32 @@
11
11
  {@const [cname, cregion] = splitName(app.active?.name ?? '')}
12
12
  {@const deltaObjective = stats.deltaObjective}
13
13
  <h2>{cname} <span class="region mono">{cregion}</span></h2>
14
- <dl class="mono">
14
+ <dl>
15
15
  <div>
16
- <dt>buses</dt>
17
- <dd>{stats.buses}</dd>
16
+ <dt>Buses</dt>
17
+ <dd class="mono">{stats.buses}</dd>
18
18
  </div>
19
19
  <div>
20
- <dt>lines</dt>
21
- <dd>{stats.branches}</dd>
20
+ <dt>Lines</dt>
21
+ <dd class="mono">{stats.branches}</dd>
22
22
  </div>
23
23
  {#if ctrl.activeFormulation !== 'acpf'}
24
24
  <div>
25
- <dt>binding lines</dt>
26
- <dd>{stats.binding ?? '...'}</dd>
25
+ <dt>Binding lines</dt>
26
+ <dd class="mono">{stats.binding ?? '…'}</dd>
27
27
  </div>
28
28
  <div>
29
29
  <dt>OPF objective</dt>
30
- <dd>
31
- {#if stats.objective === null}
32
- <span class="blink">solving...</span>
33
- {:else}
34
- {fmt.format(stats.objective)}
35
- {/if}
36
- </dd>
30
+ {#if stats.objective === null}
31
+ <dd class="blink">Solving&hellip;</dd>
32
+ {:else}
33
+ <dd class="mono">{fmt.format(stats.objective)}</dd>
34
+ {/if}
37
35
  </div>
38
36
  {#if ctrl.isPerturbed(ctrl.activeSolvable) && deltaObjective !== null}
39
37
  <div class="delta">
40
- <dt>vs base</dt>
41
- <dd>{signed(deltaObjective)}</dd>
38
+ <dt>Change from base</dt>
39
+ <dd class="mono">{signed(deltaObjective)}</dd>
42
40
  </div>
43
41
  {/if}
44
42
  {/if}
@@ -158,7 +158,7 @@
158
158
  <button
159
159
  class="title"
160
160
  aria-label={`Move ${panel.title} panel`}
161
- title="Click to expand. Drag or use arrow keys to move. Open panel options to dock."
161
+ title="Drag or use arrow keys to move"
162
162
  onpointerdown={(event) => start(event, 'move')}
163
163
  onkeydown={key}
164
164
  onclick={() => layout.activate(panel)}>{panel.title}</button
@@ -5,7 +5,7 @@
5
5
  </script>
6
6
 
7
7
  {#if app.placingId}
8
- <div class="placement-cue mono">click the map to place the topology layout</div>
8
+ <div class="placement-cue">Click the map to place the topology layout</div>
9
9
  {/if}
10
10
 
11
11
  <style>