@tellegen/svelte 0.2.0 → 0.4.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 (88) hide show
  1. package/README.md +269 -5
  2. package/dist/Sparkline.svelte +2 -2
  3. package/dist/TellegenMap.svelte +518 -202
  4. package/dist/TellegenMap.svelte.d.ts +45 -0
  5. package/dist/TellegenProvider.svelte +40 -4
  6. package/dist/TellegenShell.svelte +24 -46
  7. package/dist/TellegenViewer.svelte +12 -2
  8. package/dist/api.d.ts +18 -3
  9. package/dist/api.js +8 -2
  10. package/dist/basemap.d.ts +192 -0
  11. package/dist/basemap.js +259 -0
  12. package/dist/colors.d.ts +25 -7
  13. package/dist/colors.js +39 -13
  14. package/dist/components/AppFooter.svelte +3 -2
  15. package/dist/components/AppHeader.svelte +44 -19
  16. package/dist/components/BindingLines.svelte +4 -7
  17. package/dist/components/BusPicker.svelte +11 -5
  18. package/dist/components/ControlPanel.svelte +106 -343
  19. package/dist/components/DemandSlider.svelte +36 -29
  20. package/dist/components/DiagramCanvas.svelte +486 -0
  21. package/dist/components/DiagramCanvas.svelte.d.ts +13 -0
  22. package/dist/components/DisplayControls.svelte +8 -2
  23. package/dist/components/DropZone.svelte +1 -1
  24. package/dist/components/FormulationSelector.svelte +9 -4
  25. package/dist/components/LocalCaseDetails.svelte +61 -48
  26. package/dist/components/McResults.svelte +428 -0
  27. package/dist/components/McResults.svelte.d.ts +17 -0
  28. package/dist/components/ModelDetails.svelte +230 -0
  29. package/dist/components/ModelDetails.svelte.d.ts +7 -0
  30. package/dist/components/MulticonductorDetails.svelte +232 -83
  31. package/dist/components/NetworkStats.svelte +20 -18
  32. package/dist/components/NotificationCenter.svelte +245 -0
  33. package/dist/components/NotificationCenter.svelte.d.ts +3 -0
  34. package/dist/components/PanelCard.svelte +384 -0
  35. package/dist/components/PanelCard.svelte.d.ts +9 -0
  36. package/dist/components/PanelFrame.svelte +46 -0
  37. package/dist/components/PanelFrame.svelte.d.ts +16 -0
  38. package/dist/components/PanelHost.svelte +237 -0
  39. package/dist/components/PanelHost.svelte.d.ts +6 -0
  40. package/dist/components/PlacementCue.svelte +1 -1
  41. package/dist/components/PowerFlowDetails.svelte +99 -0
  42. package/dist/components/PowerFlowDetails.svelte.d.ts +3 -0
  43. package/dist/components/RatingSlider.svelte +19 -14
  44. package/dist/components/RestoreDefaultsButton.svelte +1 -1
  45. package/dist/components/SavedNetworkDetails.svelte +214 -0
  46. package/dist/components/SavedNetworkDetails.svelte.d.ts +3 -0
  47. package/dist/components/SensitivityReadout.svelte +26 -18
  48. package/dist/components/ShellEnvironment.svelte +51 -0
  49. package/dist/components/ShellEnvironment.svelte.d.ts +18 -0
  50. package/dist/components/SizeLegend.svelte +2 -2
  51. package/dist/components/SolveCard.svelte +48 -51
  52. package/dist/components/TopMovers.svelte +9 -4
  53. package/dist/components/TopMovers.svelte.d.ts +2 -17
  54. package/dist/components.d.ts +5 -0
  55. package/dist/components.js +5 -0
  56. package/dist/context.svelte.d.ts +8 -1
  57. package/dist/context.svelte.js +8 -3
  58. package/dist/controller.svelte.d.ts +117 -36
  59. package/dist/controller.svelte.js +1133 -253
  60. package/dist/diagram-labels.d.ts +9 -0
  61. package/dist/diagram-labels.js +40 -0
  62. package/dist/display.d.ts +3 -7
  63. package/dist/display.js +31 -27
  64. package/dist/format.d.ts +9 -0
  65. package/dist/format.js +17 -4
  66. package/dist/index.d.ts +15 -5
  67. package/dist/index.js +9 -2
  68. package/dist/map.d.ts +4 -2
  69. package/dist/map.js +2 -1
  70. package/dist/maplibre-worker.d.ts +5 -0
  71. package/dist/maplibre-worker.dev.d.ts +11 -0
  72. package/dist/maplibre-worker.dev.js +15 -0
  73. package/dist/maplibre-worker.js +8 -0
  74. package/dist/multiconductor.d.ts +11 -2
  75. package/dist/multiconductor.js +107 -14
  76. package/dist/notices.svelte.d.ts +29 -0
  77. package/dist/notices.svelte.js +86 -0
  78. package/dist/panels.svelte.d.ts +71 -0
  79. package/dist/panels.svelte.js +245 -0
  80. package/dist/slider.d.ts +12 -0
  81. package/dist/slider.js +31 -0
  82. package/dist/state.svelte.d.ts +107 -23
  83. package/dist/state.svelte.js +106 -20
  84. package/dist/styles.css +59 -6
  85. package/dist/synthetic-layout.js +1 -0
  86. package/package.json +29 -11
  87. package/dist/study-package.d.ts +0 -8
  88. package/dist/study-package.js +0 -17
@@ -5,19 +5,24 @@
5
5
 
6
6
  const app = getAppState();
7
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.";
8
+ const reportedUnits = $derived(
9
+ (ctrl.selectedSensitivity?.units ?? '(objective units/MW)/MW').replaceAll(
10
+ 'objective_unit',
11
+ 'objective units'
12
+ )
13
+ );
14
+ const busUnitTitle = $derived(
15
+ `LMP response to active demand, in ${reportedUnits}. The response is small while the active set stays unchanged.`
16
+ );
17
+ const branchUnitTitle = $derived(
18
+ `LMP response to the thermal rating, in ${reportedUnits}. The column is zero when the limit is inactive.`
19
+ );
15
20
 
16
- // Branch mode: the selection is a line and the column is ∂LMP/∂rating; the
21
+ // Branch mode: the selection is a line and the column is price/rating; the
17
22
  // legend and preview pipeline are identical (both columns are bus-keyed).
18
23
  const branchMode = $derived(app.selectedBranch !== null);
19
24
  const unitTitle = $derived(branchMode ? branchUnitTitle : busUnitTitle);
20
- const units = $derived(branchMode ? '($/MWh)/MVA' : '($/MWh)/MW');
25
+ const previewUnits = $derived(app.previewPrices?.units ?? 'objective units/MW');
21
26
 
22
27
  const previewStep = $derived(
23
28
  branchMode
@@ -54,33 +59,36 @@
54
59
  {#if ctrl.sensSummary?.flat}
55
60
  <div class="legend flat" style:background={ctrl.flatSensBackground}></div>
56
61
  <div class="legend-labels mono single">
57
- <span>uniform {signedExp(ctrl.sensSummary.mean * previewStep)} $/MWh</span>
62
+ <span
63
+ >uniform {signedExp(ctrl.sensSummary.mean * previewStep)}
64
+ {previewUnits}</span
65
+ >
58
66
  </div>
59
67
  {:else if ctrl.previewScale}
60
68
  <!-- The bounds are fixed for the whole drag (column scale × full slider
61
69
  deflection), so the ramp ends label the colors: full green/purple is
62
- the predicted ΔLMP at full deflection, and intensity grows with the
70
+ the predicted LMP change at full deflection, and intensity grows with the
63
71
  step instead of renormalizing every frame. -->
64
72
  <div class="legend" style:background={sensGradient}></div>
65
73
  <div class="legend-labels mono">
66
74
  <span>&minus;{ctrl.previewScale.toExponential(1)}</span>
67
- <span>&Delta;LMP $/MWh</span>
75
+ <span>&Delta; LMP {previewUnits}</span>
68
76
  <span>+{ctrl.previewScale.toExponential(1)}</span>
69
77
  </div>
70
78
  {/if}
71
79
  {:else}
72
80
  <p class="dim small sensitivity-copy" title={unitTitle}>
73
81
  {#if branchMode}
74
- LMP response per MVA of rating on line {branchLabel}.
82
+ LMP response to the rating on line {branchLabel}.
75
83
  {:else}
76
- LMP response per MW of demand at bus {app.selectedBus}.
84
+ LMP response to demand at bus {app.selectedBus}.
77
85
  {/if}
78
86
  <span class="hint-dot mono" title={unitTitle} aria-label={unitTitle}>i</span>
79
87
  </p>
80
88
  {#if ctrl.sensSummary?.flat}
81
89
  <div class="legend flat" style:background={ctrl.flatSensBackground}></div>
82
90
  <div class="legend-labels mono single">
83
- <span>uniform {signedExp(ctrl.sensSummary.mean)} {units}</span>
91
+ <span>uniform {signedExp(ctrl.sensSummary.mean)} {reportedUnits}</span>
84
92
  </div>
85
93
  {:else if ctrl.sensSummary}
86
94
  <div class="legend" style:background={sensGradient}></div>
@@ -93,7 +101,7 @@
93
101
  <div class="legend" style:background="var(--line)" style:opacity="0.4"></div>
94
102
  <div class="legend-labels mono single">
95
103
  <span class="blink">
96
- computing {#if branchMode}&part;LMP/&part;fmax{:else}&part;LMP/&part;d{/if}&hellip;
104
+ computing {#if branchMode}&part;value/&part;fmax{:else}&part;value/&part;d{/if}&hellip;
97
105
  </span>
98
106
  </div>
99
107
  {/if}
@@ -103,7 +111,7 @@
103
111
  <style>
104
112
  .chip {
105
113
  font-family: var(--font-mono);
106
- font-size: 11px;
114
+ font-size: 13px;
107
115
  padding: 2px 8px;
108
116
  border: 1px solid var(--accent);
109
117
  color: var(--text-accent);
@@ -121,7 +129,7 @@
121
129
  }
122
130
 
123
131
  .sensitivity-copy {
124
- font-size: 11.5px;
132
+ font-size: 13.5px;
125
133
  line-height: 1.35;
126
134
  white-space: nowrap;
127
135
  }
@@ -0,0 +1,51 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { PANEL_COMPACT_QUERY } from '../panels.svelte.js';
4
+ import { getAppState, getController } from '../context.svelte.js';
5
+
6
+ // Renderless. Keeps the shared app state in step with the environment: the
7
+ // pointer that gates the file drop affordance, the compact-layout media
8
+ // query, and the viewport height panel and map framing both read. The
9
+ // default shell renders it; a custom shell on `PanelHost` must too.
10
+
11
+ // Gates the drag-and-drop affordance only. The upload button in the header
12
+ // is always rendered: a file picker works on every device, and the plain
13
+ // `hover`/`pointer` queries resolve false on a touchscreen laptop that also
14
+ // has a mouse, which hid the whole file flow there (#110). `any-*` match
15
+ // when at least one attached input can hover and point precisely.
16
+ const FILE_DROP_QUERY = '(any-hover: hover) and (any-pointer: fine)';
17
+
18
+ const app = getAppState();
19
+ const ctrl = getController();
20
+
21
+ onMount(() => {
22
+ const query = window.matchMedia(FILE_DROP_QUERY);
23
+ const syncFileDropUi = () => {
24
+ ctrl.showFileDropUi = query.matches;
25
+ if (!ctrl.showFileDropUi) app.dragOver = false;
26
+ };
27
+ syncFileDropUi();
28
+ query.addEventListener('change', syncFileDropUi);
29
+
30
+ const compact = window.matchMedia(PANEL_COMPACT_QUERY);
31
+ const syncCompact = () => {
32
+ app.compactLayout = compact.matches;
33
+ if (!compact.matches) app.sheetInset = 0;
34
+ };
35
+ syncCompact();
36
+ compact.addEventListener('change', syncCompact);
37
+
38
+ // Shared viewport measurements keep panel and map framing dimensions aligned.
39
+ const syncViewport = () => (app.viewportHeight = window.innerHeight);
40
+ syncViewport();
41
+ window.addEventListener('resize', syncViewport);
42
+ window.addEventListener('orientationchange', syncViewport);
43
+
44
+ return () => {
45
+ query.removeEventListener('change', syncFileDropUi);
46
+ compact.removeEventListener('change', syncCompact);
47
+ window.removeEventListener('resize', syncViewport);
48
+ window.removeEventListener('orientationchange', syncViewport);
49
+ };
50
+ });
51
+ </script>
@@ -0,0 +1,18 @@
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 ShellEnvironment: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type ShellEnvironment = InstanceType<typeof ShellEnvironment>;
18
+ export default ShellEnvironment;
@@ -19,7 +19,7 @@
19
19
  display: flex;
20
20
  align-items: center;
21
21
  gap: 12px;
22
- font-size: 10px;
22
+ font-size: 12px;
23
23
  color: var(--text-secondary);
24
24
  }
25
25
 
@@ -38,7 +38,7 @@
38
38
 
39
39
  .caption {
40
40
  margin-left: auto;
41
- font-size: 9.5px;
41
+ font-size: 12px;
42
42
  }
43
43
 
44
44
  @media (max-width: 760px) {
@@ -1,56 +1,63 @@
1
1
  <script lang="ts">
2
- import { getController } from '../context.svelte.js';
2
+ import { getAppState, getController } from '../context.svelte.js';
3
3
  import { formulationLabel, solveMetaLabel } from '../format.js';
4
4
  import Sparkline from '../Sparkline.svelte';
5
+ import PanelFrame from './PanelFrame.svelte';
6
+ let open = $state(true);
5
7
 
6
8
  const ctrl = getController();
9
+ const app = getAppState();
10
+ const saved = $derived(app.studyView);
7
11
  const active = $derived(ctrl.activeSolvable);
8
- const iterations = $derived(active?.iterations ?? []);
12
+ const iterations = $derived(
13
+ saved
14
+ ? Array.isArray(saved.solution?.iterations)
15
+ ? saved.solution.iterations
16
+ : []
17
+ : (active?.iterations ?? [])
18
+ );
9
19
  </script>
10
20
 
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>
21
+ {#if saved || (active && (active.solving || active.solveMs != null))}
22
+ <PanelFrame id="solver" title="Solver" side="right" order={10} width={320} bind:open>
23
+ <div class="solvecard">
24
+ <div class="solve-label mono">
25
+ {saved ? 'Saved result' : active?.formulation === 'acpf' ? 'Power flow' : 'OPF solve'}
26
+ </div>
27
+ {#if iterations.length > 1}
28
+ {#key iterations}
29
+ <Sparkline {iterations} />
30
+ {/key}
31
+ {/if}
32
+ {#if saved}
33
+ <div class="solve-meta mono dim">
34
+ <span class="solve-formulation">{formulationLabel(saved.formulation)}</span>
35
+ <span>{saved.solution?.status ?? 'Not solved'}</span>
36
+ <span>Saved state: {saved.label}</span>
37
+ </div>
38
+ {:else if active}
39
+ <div class="solve-meta mono dim">
40
+ <span class="solve-formulation">{formulationLabel(active.formulation)}</span>
41
+ <span>{solveMetaLabel(active)}</span>
42
+ {#if active.solveMs != null}<span>{active.solveMs} ms</span>{/if}
43
+ </div>
44
+ {#if active.solveBackend === 'rust-server' && active.solveFallbackReason}
45
+ <p class="fallback-reason mono dim" title={active.solveFallbackReason}>
46
+ fallback: {active.solveFallbackReason}
47
+ </p>
48
+ {/if}
49
+ {/if}
15
50
  </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>
51
+ </PanelFrame>
32
52
  {/if}
33
53
 
34
54
  <style>
55
+ .solve-label {
56
+ font-size: 13px;
57
+ margin-bottom: 8px;
58
+ }
35
59
  .solvecard {
36
- position: absolute;
37
- top: 76px;
38
- right: 20px;
39
- z-index: 10;
40
- width: 300px;
41
60
  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
61
  }
55
62
 
56
63
  .solve-meta {
@@ -58,7 +65,7 @@
58
65
  align-items: center;
59
66
  flex-wrap: wrap;
60
67
  gap: 10px 14px;
61
- font-size: 10px;
68
+ font-size: 12px;
62
69
  margin-top: 6px;
63
70
  }
64
71
 
@@ -68,18 +75,8 @@
68
75
 
69
76
  .fallback-reason {
70
77
  margin: 6px 0 0;
71
- font-size: 10px;
78
+ font-size: 12px;
72
79
  line-height: 1.35;
73
80
  overflow-wrap: anywhere;
74
81
  }
75
-
76
- @media (max-width: 760px) {
77
- .solvecard {
78
- /* just clear of the header's case tabs */
79
- top: 108px;
80
- left: auto;
81
- right: 10px;
82
- width: min(300px, calc(100% - 20px));
83
- }
84
- }
85
82
  </style>
@@ -3,15 +3,20 @@
3
3
  import { signedExp } from '../format.js';
4
4
 
5
5
  const ctrl = getController();
6
- const unitTitle =
7
- 'LMP is measured in $/MWh and demand is perturbed in MW, so dLMP/dd has units ($/MWh)/MW.';
6
+ const units = $derived(
7
+ (ctrl.selectedSensitivity?.units ?? '(objective units/MW)/MW').replaceAll(
8
+ 'objective_unit',
9
+ 'objective units'
10
+ )
11
+ );
12
+ const unitTitle = $derived(`LMP response per MW of demand, in ${units}.`);
8
13
  </script>
9
14
 
10
15
  <div class="movers-block">
11
16
  {#if !ctrl.previewing && ctrl.topMovers.length > 0}
12
17
  <table class="mono">
13
18
  <caption class="mono dim" title={unitTitle}>
14
- largest &Delta;LMP per MW demand <span class="unit">($/MWh)/MW</span>
19
+ largest LMP response <span class="unit">{units}</span>
15
20
  </caption>
16
21
  <tbody>
17
22
  {#each ctrl.topMovers as mover (mover.bus)}
@@ -41,7 +46,7 @@
41
46
 
42
47
  caption {
43
48
  text-align: left;
44
- font-size: 10.5px;
49
+ font-size: 12.5px;
45
50
  margin-bottom: 4px;
46
51
  }
47
52
 
@@ -1,18 +1,3 @@
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 TopMovers: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type TopMovers = InstanceType<typeof TopMovers>;
1
+ declare const TopMovers: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type TopMovers = ReturnType<typeof TopMovers>;
18
3
  export default TopMovers;
@@ -1,4 +1,8 @@
1
+ export { default as ModelDetails } from './components/ModelDetails.svelte';
1
2
  export { default as AppFooter } from './components/AppFooter.svelte';
3
+ export { default as NotificationCenter } from './components/NotificationCenter.svelte';
4
+ export { default as PanelHost } from './components/PanelHost.svelte';
5
+ export { default as ShellEnvironment } from './components/ShellEnvironment.svelte';
2
6
  export { default as AppHeader } from './components/AppHeader.svelte';
3
7
  export { default as BusPicker } from './components/BusPicker.svelte';
4
8
  export { default as ControlPanel } from './components/ControlPanel.svelte';
@@ -16,3 +20,4 @@ export { default as SizeLegend } from './components/SizeLegend.svelte';
16
20
  export { default as SolveCard } from './components/SolveCard.svelte';
17
21
  export { default as TopMovers } from './components/TopMovers.svelte';
18
22
  export { default as Sparkline } from './Sparkline.svelte';
23
+ export { default as McResults } from './components/McResults.svelte';
@@ -1,4 +1,8 @@
1
+ export { default as ModelDetails } from './components/ModelDetails.svelte';
1
2
  export { default as AppFooter } from './components/AppFooter.svelte';
3
+ export { default as NotificationCenter } from './components/NotificationCenter.svelte';
4
+ export { default as PanelHost } from './components/PanelHost.svelte';
5
+ export { default as ShellEnvironment } from './components/ShellEnvironment.svelte';
2
6
  export { default as AppHeader } from './components/AppHeader.svelte';
3
7
  export { default as BusPicker } from './components/BusPicker.svelte';
4
8
  export { default as ControlPanel } from './components/ControlPanel.svelte';
@@ -16,3 +20,4 @@ export { default as SizeLegend } from './components/SizeLegend.svelte';
16
20
  export { default as SolveCard } from './components/SolveCard.svelte';
17
21
  export { default as TopMovers } from './components/TopMovers.svelte';
18
22
  export { default as Sparkline } from './Sparkline.svelte';
23
+ export { default as McResults } from './components/McResults.svelte';
@@ -1,12 +1,19 @@
1
1
  import type { AppState } from './state.svelte.js';
2
2
  import type { Controller } from './controller.svelte.js';
3
3
  export interface TellegenUiConfig {
4
- apiBase: string;
4
+ /** Backend base path; `null` for a host with no backend at all. */
5
+ apiBase: string | null;
5
6
  loadDefaultCases: boolean;
6
7
  docsHref: string;
7
8
  orgHref: string;
8
9
  orgLabel: string;
9
10
  showFooter: boolean;
11
+ /** `'dark'` sets `data-theme="dark"` on the document root while the
12
+ * provider is mounted, which switches the `styles.css` tokens. */
13
+ theme: 'light' | 'dark';
14
+ /** CARTO basemap key for the default shell's map, trimmed; empty for
15
+ * none. See `TellegenMap`'s `cartoApiKey`. */
16
+ cartoApiKey: string;
10
17
  }
11
18
  export type TellegenUiOptions = Partial<TellegenUiConfig>;
12
19
  export declare const DEFAULT_TELLEGEN_UI_CONFIG: TellegenUiConfig;
@@ -1,4 +1,5 @@
1
1
  import { getContext, setContext } from 'svelte';
2
+ import { normalizeCartoApiKey } from './basemap.js';
2
3
  const APP = Symbol('tellegen.app');
3
4
  const CTRL = Symbol('tellegen.controller');
4
5
  const CONFIG = Symbol('tellegen.config');
@@ -8,16 +9,20 @@ export const DEFAULT_TELLEGEN_UI_CONFIG = {
8
9
  docsHref: 'https://eigenergy.github.io/tellegen/',
9
10
  orgHref: 'https://github.com/eigenergy',
10
11
  orgLabel: 'eigenergy group @ michigan ece',
11
- showFooter: true
12
+ showFooter: true,
13
+ theme: 'light',
14
+ cartoApiKey: ''
12
15
  };
13
16
  export function resolveTellegenUiConfig(options = {}) {
14
17
  return {
15
- apiBase: options.apiBase ?? DEFAULT_TELLEGEN_UI_CONFIG.apiBase,
18
+ apiBase: options.apiBase === undefined ? DEFAULT_TELLEGEN_UI_CONFIG.apiBase : options.apiBase,
16
19
  loadDefaultCases: options.loadDefaultCases ?? DEFAULT_TELLEGEN_UI_CONFIG.loadDefaultCases,
17
20
  docsHref: options.docsHref ?? DEFAULT_TELLEGEN_UI_CONFIG.docsHref,
18
21
  orgHref: options.orgHref ?? DEFAULT_TELLEGEN_UI_CONFIG.orgHref,
19
22
  orgLabel: options.orgLabel ?? DEFAULT_TELLEGEN_UI_CONFIG.orgLabel,
20
- showFooter: options.showFooter ?? DEFAULT_TELLEGEN_UI_CONFIG.showFooter
23
+ showFooter: options.showFooter ?? DEFAULT_TELLEGEN_UI_CONFIG.showFooter,
24
+ theme: options.theme ?? DEFAULT_TELLEGEN_UI_CONFIG.theme,
25
+ cartoApiKey: normalizeCartoApiKey(options.cartoApiKey) ?? DEFAULT_TELLEGEN_UI_CONFIG.cartoApiKey
21
26
  };
22
27
  }
23
28
  export const setAppState = (a) => setContext(APP, a);