@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
package/README.md CHANGED
@@ -40,6 +40,7 @@ remain in the browser on this path.
40
40
  - `orgHref`
41
41
  - `orgLabel`
42
42
  - `showFooter`, default `true`
43
+ - `theme`, `light` (default) or `dark`
43
44
 
44
45
  Use `TellegenProvider` and `TellegenShell` when state should survive route changes:
45
46
 
@@ -54,16 +55,269 @@ Use `TellegenProvider` and `TellegenShell` when state should survive route chang
54
55
  </TellegenProvider>
55
56
  ```
56
57
 
58
+ ## Panel layout
59
+
60
+ The shell docks Network and Studies on the left, and Solver and Agent on the
61
+ right. The most recently opened panel gets more room in its dock; click another
62
+ heading to expand it. Drag a panel heading to detach it; use the corner handle to resize it.
63
+ Panel options dock it on either side, and **Reset layout** restores the default
64
+ positions. Focus a heading or resize handle and use arrow keys for keyboard
65
+ control; hold Shift for larger steps. Narrow windows show one active drawer.
66
+ Panel positions are stored in the browser, independently of case and Study data.
67
+
68
+ Custom panels can join the same layout inside `TellegenProvider`:
69
+
70
+ ```svelte
71
+ <TellegenProvider>
72
+ <TellegenShell />
73
+ <PanelFrame id="notes" title="Notes" side="right" order={30}>
74
+ <p>Case notes</p>
75
+ </PanelFrame>
76
+ </TellegenProvider>
77
+ ```
78
+
79
+ Import `PanelFrame` from `@tellegen/svelte`. Its optional `open` prop is bindable;
80
+ `width` sets its preferred width, and the `headerActions` snippet adds compact
81
+ controls beside its title.
82
+
83
+ ### Custom shells
84
+
85
+ `TellegenShell` is one arrangement of exported parts. A host can mount its own
86
+ inside `TellegenProvider`:
87
+
88
+ ```svelte
89
+ <!-- +layout.svelte -->
90
+ <script lang="ts">
91
+ import { TellegenProvider } from "@tellegen/svelte";
92
+ import "@tellegen/svelte/styles.css";
93
+ import MyShell from "./MyShell.svelte";
94
+ </script>
95
+
96
+ <TellegenProvider loadDefaultCases={false} apiBase={null} theme="dark">
97
+ <MyShell />
98
+ </TellegenProvider>
99
+ ```
100
+
101
+ ```svelte
102
+ <!-- MyShell.svelte: reads the controller from context, so it must be a child -->
103
+ <script lang="ts">
104
+ import { PanelFrame, getController } from "@tellegen/svelte";
105
+ import { TellegenMap } from "@tellegen/svelte/map";
106
+ import { NotificationCenter, PanelHost, ShellEnvironment } from "@tellegen/svelte/components";
107
+
108
+ const ctrl = getController();
109
+ </script>
110
+
111
+ <ShellEnvironment />
112
+ <TellegenMap
113
+ theme="dark"
114
+ projection="globe"
115
+ onbusclick={ctrl.selectBus}
116
+ onlocalbusclick={ctrl.selectLocalBus}
117
+ onmultibusclick={ctrl.selectMultiBus}
118
+ onplacecase={ctrl.placeCase}
119
+ onmapclick={ctrl.clearSelection}
120
+ />
121
+ <PanelHost showToolbar={false} />
122
+ <NotificationCenter />
123
+ <PanelFrame id="region" title="Region" side="left">...</PanelFrame>
124
+ ```
125
+
126
+ `ShellEnvironment` is renderless and keeps the compact-layout, pointer and
127
+ viewport state in step, as the default shell does. `PanelHost` reads
128
+ `--tellegen-dock-left`, `--tellegen-dock-right` and `--tellegen-dock-bottom`
129
+ from an ancestor for its dock insets (defaults 20px, 20px and 100px), so a
130
+ shell with its own rail or status bar moves the docks without restyling them.
131
+
132
+ When a panel closes, focus moves to its toolbar button. Without the toolbar,
133
+ focus goes back to the control that had focus when the panel opened. If there
134
+ is none, it goes to the panel's dock, a region named "Left panels" or "Right
135
+ panels". On narrow screens that region is the drawer, named "Panels".
136
+
137
+ A shell that shows `app.error` itself can also read `app.errorDetail`. When
138
+ it is set, `app.error` is a message written for people, and the detail holds
139
+ the technical text behind it. `NotificationCenter` shows that text in a
140
+ collapsed disclosure.
141
+
142
+ ## Basemap
143
+
144
+ CARTO's vector styles suit a network overlay best: quiet roads, flat land and
145
+ clear water. CARTO's basemap terms ask for an API key, which you can get from
146
+ [carto.com/basemaps/apikey](https://carto.com/basemaps/apikey). No account is
147
+ needed, and non-commercial use (personal projects, research, teaching,
148
+ non-profits) is free up to 5 million requests a month. The browser sends the
149
+ key with every style, tile, glyph and sprite request as `?key=`, so it is
150
+ public by design: restrict it to the hosts that serve your app in CARTO's key
151
+ dashboard, and use one key per project.
152
+
153
+ The default order depends on the key:
154
+
155
+ - **With `cartoApiKey`:** the map loads the CARTO style. That is
156
+ `positron-nolabels` for the light theme and `dark-matter-nolabels` for the
157
+ dark theme, or the labelled styles with `hideBasemapLabels={false}`. It
158
+ falls back to the matching [OpenFreeMap](https://openfreemap.org/) style.
159
+ - **Without a key:** the map loads the OpenFreeMap style (`positron` or
160
+ `dark`) and falls back to the keyless CARTO style.
161
+
162
+ The map switches to the fallback in three cases:
163
+
164
+ - the primary style cannot be fetched;
165
+ - its tiles answer 401, 403 or 429;
166
+ - tile fetches keep failing with nothing arriving.
167
+
168
+ Whenever an OpenFreeMap style renders, as primary or as fallback,
169
+ `openFreeMapEdits` tunes it towards CARTO's look. Road lines start at zoom 8.
170
+ In the dark theme, water and waterways take Dark Matter's colours and land
171
+ fills take one tone. The edits match layers by `source-layer`, not by id.
172
+
173
+ Label layers are hidden by default. The attribution control is always on and
174
+ shows the credits of the style on screen.
175
+
176
+ `TellegenMap` (also exported from `@tellegen/svelte/map`) accepts these props
177
+ besides its click callbacks:
178
+
179
+ - `mapStyle`: a style URL or inline `StyleSpecification`. Default: the CARTO
180
+ style for `theme` with a key, else the OpenFreeMap style.
181
+ - `fallbackMapStyle`: the style used when the primary fails; `null` disables
182
+ the switch. Default: whichever of the two is not the primary.
183
+ - `cartoApiKey`: the CARTO key. It is trimmed, and an empty, blank or
184
+ absent key means none.
185
+ - `theme`: `'light'` (default) or `'dark'`. Picks the default styles and the
186
+ overlay palette (`MAP_PALETTE`).
187
+ - `projection`: `'mercator'` (default) or `'globe'`. Re-applied after every
188
+ style load, so a basemap switch keeps it.
189
+ - `initialCamera`: `{ center, zoom, bearing, pitch }`, read once per mount.
190
+ - `hideBasemapLabels`: default `true`.
191
+ - `extraLayers`: deck.gl layers drawn after tellegen's own, in the same
192
+ overlay.
193
+ - `attribution`: `'compact'` (default) or `'full'`. There is no off switch.
194
+ - `onMapReady(map, overlay)`: called with the MapLibre map and the deck
195
+ overlay once they exist. It may return a cleanup, and it fires again after
196
+ a context-loss or basemap rebuild.
197
+ - `onBasemapFallback(reason)`: the switch to the fallback happened.
198
+ `reason.from` and `reason.to` name the two providers.
199
+ - `onBasemapError(reason)`: no basemap can be shown. The network layers
200
+ still draw. `reason.from` names the provider that failed last, and
201
+ `reason.to` is `null`.
202
+
203
+ `TellegenProvider` and `TellegenViewer` also take `cartoApiKey`, and pass it,
204
+ together with `theme`, to the default shell's map. A SvelteKit 3 host can
205
+ declare the key as an optional public build-time variable:
206
+
207
+ ```ts
208
+ // src/env.ts
209
+ import { defineEnvVars } from "@sveltejs/kit/env";
210
+
211
+ export const variables = defineEnvVars({
212
+ PUBLIC_CARTO_BASEMAP_KEY: { public: true, static: true, schema: (value) => value ?? "" }
213
+ });
214
+ ```
215
+
216
+ ```svelte
217
+ <script lang="ts">
218
+ import { TellegenProvider } from "@tellegen/svelte";
219
+ import { PUBLIC_CARTO_BASEMAP_KEY } from "$app/env/public";
220
+ </script>
221
+
222
+ <TellegenProvider cartoApiKey={PUBLIC_CARTO_BASEMAP_KEY}>...</TellegenProvider>
223
+ ```
224
+
225
+ On SvelteKit 2, import it from `$env/static/public` instead; that module
226
+ needs the variable defined at build time, even if empty.
227
+
228
+ Changing `mapStyle`, `fallbackMapStyle`, `theme`, `hideBasemapLabels`,
229
+ `cartoApiKey` or `attribution` rebuilds the map in place and keeps the camera.
230
+
231
+ The helpers behind the defaults are exported too:
232
+
233
+ - `OPENFREEMAP_STYLES` and `CARTO_STYLES`;
234
+ - `cartoStyleUrl(url, apiKey)`;
235
+ - `defaultBasemap(theme, { cartoApiKey })` and `resolveBasemap(props)`;
236
+ - `attachBasemapFallback(map, spec, handlers)`;
237
+ - `isOpenFreeMapStyle(style)` and `openFreeMapEdits(style, theme)`.
238
+
239
+ `defaultBasemap` and `resolveBasemap` also say who serves each style, so a
240
+ host can word its credits and fallback notice without reading URLs. Their
241
+ `primaryProvider` and `fallbackProvider` are `'carto'`, `'openfreemap'`, or
242
+ `'custom'` for a style the host passed, and `fallbackProvider` is `null` when
243
+ the fallback is off. `defaultBasemap` is typed `DefaultBasemap`: two style
244
+ URLs, one CARTO and one OpenFreeMap, so its providers are never `'custom'` and
245
+ its fallback is never `null`. The fallback reasons use the same names in
246
+ `from` and `to`.
247
+
248
+ A Content Security Policy must allow `https://tiles.openfreemap.org` and
249
+ `https://*.cartocdn.com` in `connect-src` and `img-src`. The style, vector
250
+ tiles, fonts and sprites are all fetched. The engine underneath also needs
251
+ `script-src 'wasm-unsafe-eval'` (never `'unsafe-eval'`) and `worker-src
252
+ 'self'`, since its workers are same-origin.
253
+
254
+ A host that passes its own deck.gl layers in `extraLayers`, or works with the
255
+ MapLibre map from `onMapReady`, must resolve one copy each of
256
+ `@deck.gl/core`, `@deck.gl/layers`, `@deck.gl/maplibre` and `maplibre-gl`.
257
+ When two deck.gl versions load, deck.gl throws "multiple versions detected".
258
+ If the host's ranges differ from this package's (`^9.4.0` for deck.gl,
259
+ `^6.15.0` for maplibre-gl), point both at one version with npm `overrides`
260
+ (or `resolutions` in pnpm and Yarn).
261
+
262
+ The map needs no Vite settings, in a build or in the dev server. MapLibre's
263
+ worker comes from a `?worker&url` import, which a production build bundles
264
+ with its shared chunk. Vite's dev server pre-bundles this package and cannot
265
+ pre-bundle that import, so the package's `./maplibre-worker` export has a
266
+ `development` condition that loads it at run time, outside the pre-bundle.
267
+ That export is internal to `TellegenMap`. A host that excluded
268
+ `@tellegen/svelte` from `optimizeDeps` to work around this can drop the
269
+ exclusion.
270
+
271
+ ## Dark theme
272
+
273
+ `<TellegenProvider theme="dark">` (or `<TellegenViewer theme="dark">`) sets
274
+ `data-theme="dark"` on the document root while mounted, which switches every
275
+ token in `styles.css`. The default shell passes the theme to its map, so the
276
+ basemap and the overlay palette follow. A custom shell passes
277
+ `theme="dark"` to `TellegenMap` itself. A server-rendered host can put the
278
+ attribute in its template to avoid a light first paint. Text tokens keep at
279
+ least 7:1 against the panel on either ground.
280
+
281
+ ## Opening a module from memory
282
+
283
+ A host that produces a balanced powerio module itself opens it without a file:
284
+
285
+ ```ts
286
+ import { getController } from "@tellegen/svelte";
287
+
288
+ const ctrl = getController();
289
+ const local = await ctrl.openBalancedModule({ name: "synthetic grid", moduleJson });
290
+ // or, with the payload the engine's ingestCase already returned for it:
291
+ await ctrl.openBalancedModule({ name, payload, frame: false });
292
+ // swap it for a new result in place
293
+ await ctrl.replaceLocalCase(local, { name, payload });
294
+ ```
295
+
296
+ Options: `geoLayer` (a `.geo.json` document, for a module without
297
+ coordinates; a module that carries `Bus.location` needs none),
298
+ `formulation`, `activate` (default `true`; `false` returns the prepared case
299
+ without adding it) and `frame` (default `true`). `replaceLocalCase` keeps the
300
+ case's slot in the header, its formulation and display mode, and the selected
301
+ bus when the new case has it. With `loadDefaultCases={false}` the controller
302
+ fetches nothing on `load()`; with `apiBase={null}` it has no API client at all
303
+ and every server path reports compute off.
304
+
57
305
  ## Engine Reexports
58
306
 
59
307
  The package reexports the browser engine helpers, including `ingestCase`,
60
- `classifyJson`, and `ingestJsonDrop`, for custom drop surfaces. In 0.2 these
61
- APIs use byte input: pass `Uint8Array` to `ingestCase`, await
308
+ `classifyJson`, and `ingestJsonDrop`, for custom file imports. These APIs use
309
+ byte input: pass `Uint8Array` to `ingestCase`, await
62
310
  `classifyJson(bytes)` and read its `{ kind, format }` result, and use
63
311
  `ingestJsonDrop(bytes)` to classify and parse in one call. `isStudyPackageText`
64
- was removed; test for `kind === "balanced-package"` instead. `JsonDropKind` now
65
- includes `transmission`, `distribution`, `ambiguous`, and `unknown`; BMOPF and
66
- PMD are distribution `format` values rather than separate kinds.
312
+ was removed; stored PowerIO documents report `kind === "module"`.
313
+ `JsonDropKind` also includes `transmission`, `distribution`, `ambiguous`, and
314
+ `unknown`; BMOPF and PMD are distribution `format` values rather than separate
315
+ kinds. Every solvable ingest payload carries the retained module in
316
+ `module_json`; the viewer uses that generation-2 IR for Study construction and
317
+ geographic edits without keeping a second serialized network.
318
+
319
+ Drawing layers use a plain canvas with their own x/y coordinates. Saved Studies
320
+ retain the drawing center and scale separately from the geographic map camera.
67
321
 
68
322
  ## Release
69
323
 
@@ -83,3 +337,13 @@ release. The package ships only `dist`, the README, the MIT license text, and
83
337
  package metadata. The packed smoke test installs the generated tarballs into a
84
338
  temporary Svelte consumer and builds it so missing exports, styles, or wasm
85
339
  assets fail before publish.
340
+
341
+ ## Citing
342
+
343
+ If you use this package in published work, please cite the tellegen paper:
344
+ Samuel Talkington, Frederik Geth, Qian Zhang, Le Xie, and Skyler Liu. 2026.
345
+ *Interactive Power Flow in the Browser*. The Inaugural ACM Conference on Digital
346
+ Transformation (ACM DXConf 2026), Ann Arbor, MI, USA.
347
+ [doi:10.1145/3847238.3849038](https://doi.org/10.1145/3847238.3849038). The
348
+ [repository README](https://github.com/eigenergy/tellegen#citing) has the BibTeX
349
+ entry.
@@ -42,7 +42,7 @@
42
42
  <text
43
43
  class="axis-label y-label"
44
44
  x={-(T + PLOT_H / 2)}
45
- y="8"
45
+ y="11"
46
46
  text-anchor="middle"
47
47
  transform="rotate(-90)"
48
48
  >
@@ -70,7 +70,7 @@
70
70
  .axis-label {
71
71
  fill: var(--text-tertiary);
72
72
  font-family: var(--font-mono);
73
- font-size: 9px;
73
+ font-size: 12px;
74
74
  letter-spacing: 0;
75
75
  }
76
76