@tellegen/svelte 0.4.1 → 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 (35) hide show
  1. package/README.md +38 -10
  2. package/dist/Sparkline.svelte +4 -4
  3. package/dist/TellegenMap.svelte +41 -46
  4. package/dist/TellegenProvider.svelte +29 -13
  5. package/dist/components/AppFooter.svelte +4 -4
  6. package/dist/components/AppHeader.svelte +15 -19
  7. package/dist/components/BindingLines.svelte +10 -9
  8. package/dist/components/BusPicker.svelte +5 -6
  9. package/dist/components/ControlPanel.svelte +12 -9
  10. package/dist/components/ControlPanel.svelte.d.ts +6 -1
  11. package/dist/components/DemandSlider.svelte +26 -29
  12. package/dist/components/DisplayControls.svelte +5 -24
  13. package/dist/components/DropZone.svelte +7 -3
  14. package/dist/components/DropZone.svelte.d.ts +6 -17
  15. package/dist/components/FormulationSelector.svelte +2 -7
  16. package/dist/components/LocalCaseDetails.svelte +126 -126
  17. package/dist/components/LocalCaseDetails.svelte.d.ts +6 -1
  18. package/dist/components/MulticonductorDetails.svelte +48 -38
  19. package/dist/components/NetworkStats.svelte +14 -16
  20. package/dist/components/PanelCard.svelte +1 -1
  21. package/dist/components/PlacementCue.svelte +1 -1
  22. package/dist/components/RatingSlider.svelte +22 -23
  23. package/dist/components/RestoreDefaultsButton.svelte +2 -3
  24. package/dist/components/SensitivityReadout.svelte +9 -27
  25. package/dist/components/SolveCard.svelte +69 -35
  26. package/dist/components/TopMovers.svelte +5 -5
  27. package/dist/context.svelte.d.ts +6 -2
  28. package/dist/context.svelte.js +4 -2
  29. package/dist/controller.svelte.d.ts +7 -0
  30. package/dist/controller.svelte.js +33 -6
  31. package/dist/display.js +8 -9
  32. package/dist/format.d.ts +1 -9
  33. package/dist/format.js +0 -13
  34. package/dist/styles.css +3 -0
  35. package/package.json +10 -6
@@ -83,6 +83,7 @@ export declare class Controller {
83
83
  localSeq: number;
84
84
  loading: Promise<void> | null;
85
85
  private readonly dropBatchGate;
86
+ disposed: boolean;
86
87
  nearbyRangeAnchor: DemandRangeAnchor | null;
87
88
  hiddenDefaults: Set<string>;
88
89
  casesLoaded: boolean;
@@ -129,6 +130,12 @@ export declare class Controller {
129
130
  maybeStartLocalSolve(id: string): void;
130
131
  getStudy(c: SolvableCase, studyInputJson: string): Promise<BrowserStudy | null>;
131
132
  browserSensitivity(c: SolvableCase, studyInputJson: string, target: SensTarget): Promise<SensitivityColumn | null>;
133
+ /** Release every engine handle this controller holds: each case's browser
134
+ * Study, including one still being built, and each multiconductor
135
+ * session, with their pending edits and in-flight requests.
136
+ * `TellegenProvider` calls it when it unmounts; the controller builds no
137
+ * Study and keeps no new session afterwards. */
138
+ dispose(): void;
132
139
  disposeStudy(c: SolvableCase): void;
133
140
  acceptSensitivity(c: SolvableCase, col: SensitivityColumn | null, target: SensTarget | null, sensitivitySeq?: number): void;
134
141
  finishSolve(c: SolvableCase, seq: number, target: SensTarget | null): void;
@@ -19,10 +19,9 @@ function localHasBus(c, busId) {
19
19
  }
20
20
  /** Terminal copy when nothing can solve. Two facts decide the wording: whether
21
21
  * this browser's engine failure is known permanent, and that the server
22
- * declines compute. Server compute exists in the codebase but ships disabled;
23
- * interest routes to email until it is offered. */
24
- const WASM_REQUIRED_NOTICE = 'this browser cannot run the WebAssembly engine that solves locally, and server side compute is disabled in this demo. it may be offered later; to express interest, email Samuel Talkington at talks@umich.edu';
25
- const COMPUTE_OFF_NOTICE = 'solving in this browser did not complete, and server side compute is disabled in this demo. retry to run the browser engine again, or email Samuel Talkington at talks@umich.edu to express interest in server compute';
22
+ * declines compute. */
23
+ const WASM_REQUIRED_NOTICE = 'This browser cannot run the WebAssembly engine, and server compute is disabled';
24
+ const COMPUTE_OFF_NOTICE = 'The browser solve did not complete, and server compute is disabled. Retry to run it again';
26
25
  /** Upper bound on waiting for a camera fly-to before the sensitivity solve
27
26
  * starts anyway: a missing map or a cue that never appears must not stall the
28
27
  * selection. */
@@ -107,6 +106,8 @@ export class Controller {
107
106
  // File reads and worker messages retain whole buffers. Admit one validated
108
107
  // batch at a time so repeated drops cannot multiply the aggregate byte cap.
109
108
  dropBatchGate = new DropBatchGate();
109
+ // Set by dispose(). Not $state; nothing renders it.
110
+ disposed = false;
110
111
  nearbyRangeAnchor = $state(null);
111
112
  // Default cases the user has closed; drives the restore affordance. Seeded in load().
112
113
  hiddenDefaults = $state(new Set());
@@ -268,6 +269,9 @@ export class Controller {
268
269
  // under that formulation. Returns null when the sens module can't load; the caller then
269
270
  // falls back to the server, surfacing solveFallbackReason.
270
271
  async getStudy(c, studyInputJson) {
272
+ // A disposed controller builds nothing that would outlive it.
273
+ if (this.disposed)
274
+ return null;
271
275
  const latched = this.studyUnavailable.get(c);
272
276
  if (latched) {
273
277
  c.solveFallbackReason ??= latched;
@@ -347,6 +351,28 @@ export class Controller {
347
351
  const study = await this.getStudy(c, studyInputJson);
348
352
  return study ? study.sensitivity(c.id, this.caseDeltas(c), this.caseRatings(c), target) : null;
349
353
  }
354
+ /** Release every engine handle this controller holds: each case's browser
355
+ * Study, including one still being built, and each multiconductor
356
+ * session, with their pending edits and in-flight requests.
357
+ * `TellegenProvider` calls it when it unmounts; the controller builds no
358
+ * Study and keeps no new session afterwards. */
359
+ dispose() {
360
+ if (this.disposed)
361
+ return;
362
+ this.disposed = true;
363
+ this.abort?.abort();
364
+ this.abort = null;
365
+ for (const c of [...this.app.cases, ...this.app.localCases])
366
+ this.disposeStudy(c);
367
+ for (const c of this.app.multiCases) {
368
+ c.solveAbort?.abort();
369
+ if (c.mcEditTimer !== null)
370
+ clearTimeout(c.mcEditTimer);
371
+ c.mcEditTimer = null;
372
+ c.mcSession?.free();
373
+ c.mcSession = null;
374
+ }
375
+ }
350
376
  // Release a case's Study (if any) when the case is removed.
351
377
  disposeStudy(c) {
352
378
  const cached = this.caseStudies.get(c);
@@ -2144,7 +2170,7 @@ export class Controller {
2144
2170
  }
2145
2171
  /** Compute terminal quantities only for the retained input and current case revision. */
2146
2172
  solveMultiCase = async (c, options = {}, signal) => {
2147
- if (signal?.aborted)
2173
+ if (signal?.aborted || this.disposed)
2148
2174
  throw new DOMException('Calculation cancelled', 'AbortError');
2149
2175
  if (c.solving)
2150
2176
  throw new Error('A calculation is already running for this case');
@@ -2187,7 +2213,8 @@ export class Controller {
2187
2213
  snapshot?.result ?? (await this.mcTransport.solveMcModule(input, options, abort.signal));
2188
2214
  result = summarizeMcPfResult(fullResult);
2189
2215
  }
2190
- if (abort.signal.aborted)
2216
+ // dispose() aborts too; a session that arrives after it is freed below.
2217
+ if (abort.signal.aborted || this.disposed)
2191
2218
  throw new DOMException('Calculation cancelled', 'AbortError');
2192
2219
  if (seq !== c.solveSeq ||
2193
2220
  (retainedAtStart ? retainedAtStart !== c.mcSession : input !== c.moduleJson) ||
package/dist/display.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { demandGradient, priceGradient } from './colors.js';
2
- import { priceCopy } from './format.js';
3
2
  import { CaseState } from './state.svelte.js';
4
3
  /** Variables present in the current result. Power flow has no LMP values. */
5
4
  export function displayMetaFor(c) {
@@ -11,7 +10,9 @@ export function displayMetaFor(c) {
11
10
  mode: 'price',
12
11
  label: 'LMP',
13
12
  unit: 'objective units/MW',
14
- copy: priceCopy(c.formulation),
13
+ copy: c.formulation === 'socwr'
14
+ ? 'Locational marginal price, SOCWR relaxation'
15
+ : 'Locational marginal price',
15
16
  gradient: priceGradient
16
17
  });
17
18
  }
@@ -20,18 +21,16 @@ export function displayMetaFor(c) {
20
21
  mode: 'voltage',
21
22
  label: '|V|',
22
23
  unit: 'pu',
23
- copy: c.formulation === 'socwr'
24
- ? 'SOCWR voltage magnitude from the current relaxed solution.'
25
- : 'Bus voltage magnitude from the current power-flow result.',
24
+ copy: c.formulation === 'socwr' ? 'Voltage magnitude, SOCWR relaxation' : 'Voltage magnitude',
26
25
  gradient: priceGradient
27
26
  });
28
27
  }
29
28
  if (c.solution.va.length > 0) {
30
29
  options.push({
31
30
  mode: 'angle',
32
- label: 'angle',
31
+ label: 'Angle',
33
32
  unit: 'rad',
34
- copy: 'Bus voltage angle from the current result.',
33
+ copy: 'Voltage angle',
35
34
  gradient: priceGradient
36
35
  });
37
36
  }
@@ -39,9 +38,9 @@ export function displayMetaFor(c) {
39
38
  if (c.network?.buses.length) {
40
39
  options.push({
41
40
  mode: 'demand',
42
- label: 'demand',
41
+ label: 'Demand',
43
42
  unit: 'MW',
44
- copy: 'Bus demand, including committed changes, on a square-root scale.',
43
+ copy: 'Demand, square-root scale',
45
44
  gradient: demandGradient
46
45
  });
47
46
  }
package/dist/format.d.ts CHANGED
@@ -1,19 +1,11 @@
1
- import type { SolveIteration } from './api.js';
2
1
  import type { RGBA } from './colors.js';
3
- import type { DisplayMode, SolveBackend } from './state.svelte.js';
2
+ import type { DisplayMode } from './state.svelte.js';
4
3
  import { type Formulation } from '@tellegen/engine';
5
4
  export { errorText } from '@tellegen/engine';
6
5
  /** The short menu label for a formulation tag (e.g. `acopf` -> `AC OPF`). */
7
6
  export declare function formulationLabel(id: Formulation): string;
8
7
  export declare function formulationHint(id: Formulation): string;
9
- export declare function priceCopy(id: Formulation): string;
10
8
  export declare function splitName(name: string): [string, string];
11
- /** Structural shape `solveMetaLabel` needs, satisfied by `CaseState` and `LocalCase`. */
12
- type SolveMeta = {
13
- iterations?: SolveIteration[];
14
- solveBackend: SolveBackend | null;
15
- };
16
- export declare function solveMetaLabel(c: SolveMeta): string;
17
9
  export declare const INFEASIBLE_DISPATCH = "No feasible dispatch: demand cannot be served within line and generator limits";
18
10
  /** The message for a local case whose browser solve failed, and the
19
11
  * technical detail to disclose behind it. An infeasible DC OPF means the
package/dist/format.js CHANGED
@@ -9,23 +9,10 @@ export function formulationLabel(id) {
9
9
  export function formulationHint(id) {
10
10
  return FORMULATIONS.find((f) => f.id === id)?.hint ?? id;
11
11
  }
12
- export function priceCopy(id) {
13
- const definition = 'Locational marginal price (LMP): the change in the OPF objective for an additional MW at a bus, in objective units/MW.';
14
- return id === 'socwr'
15
- ? `${definition} Prices come from the SOCWR relaxation. Select a bus to see their sensitivity.`
16
- : `${definition} Select a bus to see its demand sensitivity.`;
17
- }
18
12
  export function splitName(name) {
19
13
  const m = name.match(/^(.*?)\s*\((.*)\)$/);
20
14
  return m ? [m[1], m[2]] : [name, ''];
21
15
  }
22
- export function solveMetaLabel(c) {
23
- if ((c.iterations ?? []).length > 1)
24
- return `${c.iterations?.length} iterations`;
25
- if (c.solveBackend === 'clarabel-wasm-server-sensitivity')
26
- return 'server price sensitivity';
27
- return c.solveBackend === 'rust-server' ? 'server solve' : 'browser solve';
28
- }
29
16
  export const INFEASIBLE_DISPATCH = 'No feasible dispatch: demand cannot be served within line and generator limits';
30
17
  /** The message for a local case whose browser solve failed, and the
31
18
  * technical detail to disclose behind it. An infeasible DC OPF means the
package/dist/styles.css CHANGED
@@ -298,6 +298,7 @@ a {
298
298
  before the component split. Scoping this would raise its specificity past that rule. */
299
299
  .mode > button {
300
300
  margin-left: auto;
301
+ font-family: inherit;
301
302
  font-size: 12.5px;
302
303
  padding: 2px 7px;
303
304
  background: none;
@@ -313,6 +314,7 @@ a {
313
314
  }
314
315
 
315
316
  .display-mode {
317
+ flex-wrap: wrap;
316
318
  gap: 8px;
317
319
  }
318
320
 
@@ -387,6 +389,7 @@ a {
387
389
 
388
390
  .reset {
389
391
  margin-top: 10px;
392
+ font-family: inherit;
390
393
  font-size: 12.5px;
391
394
  padding: 3px 10px;
392
395
  background: none;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellegen/svelte",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Svelte components for tellegen power system maps, local case files, and browser solves.",
5
5
  "keywords": [
6
6
  "svelte",
@@ -67,18 +67,22 @@
67
67
  "pack:dry-run": "npm pack --dry-run --cache ../../node_modules/.cache/npm-pack"
68
68
  },
69
69
  "dependencies": {
70
+ "@tellegen/engine": "^0.4.1"
71
+ },
72
+ "peerDependencies": {
70
73
  "@deck.gl/core": "^9.4.0",
71
74
  "@deck.gl/layers": "^9.4.0",
72
75
  "@deck.gl/maplibre": "^9.4.0",
73
- "@tellegen/engine": "^0.4.0",
74
- "maplibre-gl": "^6.15.0"
75
- },
76
- "peerDependencies": {
76
+ "maplibre-gl": "^6.15.0",
77
77
  "svelte": ">=5.30 <6"
78
78
  },
79
79
  "devDependencies": {
80
- "@sveltejs/package": "^2.3.0",
80
+ "@deck.gl/core": "^9.4.0",
81
+ "@deck.gl/layers": "^9.4.0",
82
+ "@deck.gl/maplibre": "^9.4.0",
83
+ "@sveltejs/package": "^3.0.1",
81
84
  "@sveltejs/vite-plugin-svelte": "^7.3.1",
85
+ "maplibre-gl": "^6.15.0",
82
86
  "svelte": "^5.57.2",
83
87
  "svelte-check": "^4.7.7",
84
88
  "typescript": "^6.0.3",