@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,73 @@
1
+ <script lang="ts">
2
+ import { getController } from '../context.svelte.js';
3
+
4
+ const ctrl = getController();
5
+ </script>
6
+
7
+ <footer class="mono">
8
+ <a href="/credits/">credits</a>
9
+ <i class="sep"></i>
10
+ <a href="/privacy/">privacy</a>
11
+ {#if ctrl.showFileDropUi}
12
+ <i class="sep filedrop-ui"></i>
13
+ <span class="drophint filedrop-ui"
14
+ ><span class="arrow">&#8675;</span> drop a case or geographic file anywhere</span
15
+ >
16
+ {/if}
17
+ </footer>
18
+
19
+ <style>
20
+ footer {
21
+ position: absolute;
22
+ bottom: 0;
23
+ left: 0;
24
+ right: 0;
25
+ z-index: 10;
26
+ display: flex;
27
+ align-items: center;
28
+ padding: 8px 20px;
29
+ font-size: 10.5px;
30
+ color: var(--text-secondary);
31
+ background: linear-gradient(rgba(236, 233, 226, 0), rgba(236, 233, 226, 0.9));
32
+ animation: rise 0.5s 0.24s ease-out both;
33
+ pointer-events: none;
34
+ }
35
+
36
+ footer a {
37
+ pointer-events: auto;
38
+ color: var(--text-secondary);
39
+ }
40
+
41
+ footer a:hover {
42
+ color: var(--text-accent);
43
+ }
44
+
45
+ .drophint {
46
+ color: var(--text-tertiary);
47
+ }
48
+
49
+ .drophint .arrow {
50
+ color: var(--text-accent);
51
+ }
52
+
53
+ @media (max-width: 760px) {
54
+ footer {
55
+ padding: 7px 10px;
56
+ overflow-x: auto;
57
+ font-size: 9.5px;
58
+ white-space: nowrap;
59
+ pointer-events: auto;
60
+ scrollbar-width: none;
61
+ }
62
+
63
+ footer::-webkit-scrollbar {
64
+ display: none;
65
+ }
66
+ }
67
+
68
+ @media (prefers-reduced-motion: reduce) {
69
+ footer {
70
+ animation: none;
71
+ }
72
+ }
73
+ </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 AppFooter: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type AppFooter = InstanceType<typeof AppFooter>;
18
+ export default AppFooter;
19
+ //# sourceMappingURL=AppFooter.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppFooter.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AppFooter.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,SAAS;;kBAA+E,CAAC;AAC7E,KAAK,SAAS,GAAG,YAAY,CAAC,OAAO,SAAS,CAAC,CAAC;AAClD,eAAe,SAAS,CAAC"}
@@ -0,0 +1,405 @@
1
+ <script lang="ts">
2
+ import { getAppState, getController, getUiConfig } from '../context.svelte.js';
3
+ import { splitName } from '../format.js';
4
+
5
+ const app = getAppState();
6
+ const ctrl = getController();
7
+ const config = getUiConfig();
8
+
9
+ let fileInput = $state.raw<HTMLInputElement | undefined>(undefined);
10
+ </script>
11
+
12
+ <header>
13
+ <div class="brand">
14
+ <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true">
15
+ <path d="M4 18 L12 6 L20 18" stroke="#b25e00" stroke-width="1.6" fill="none" />
16
+ <circle cx="4" cy="18" r="2.4" fill="#b25e00" />
17
+ <circle cx="12" cy="6" r="2.4" fill="#20242b" />
18
+ <circle cx="20" cy="18" r="2.4" fill="#b25e00" />
19
+ </svg>
20
+ <h1>tellegen</h1>
21
+ </div>
22
+ <nav class="cases" aria-label="networks">
23
+ {#each app.cases as c (c.id)}
24
+ {@const [cname, cregion] = splitName(c.name)}
25
+ <div class="case-chip" class:active={app.activeCaseId === c.id}>
26
+ <button class="case-activate" onclick={() => ctrl.activateCase(c.id)}>
27
+ <span class="cname"
28
+ >{cname}{#if c.perturbed}<i class="mark" title="demand perturbed"></i>{/if}</span
29
+ >
30
+ <span class="cregion mono">{cregion}</span>
31
+ </button>
32
+ <button
33
+ class="case-remove mono"
34
+ aria-label="remove {c.name} from this browser"
35
+ title="remove {c.name} from this browser"
36
+ onclick={(e) => ctrl.removeBackendCase(c, e)}>&#10005;</button
37
+ >
38
+ </div>
39
+ {/each}
40
+ {#each app.localCases as c (c.id)}
41
+ <div class="case-chip local" class:active={app.activeLocalId === c.id}>
42
+ <button class="case-activate" onclick={() => ctrl.activateLocal(c)}>
43
+ <span class="cname">{c.label}</span>
44
+ <span class="cregion mono">local</span>
45
+ </button>
46
+ <button
47
+ class="case-remove mono"
48
+ aria-label="remove {c.label}"
49
+ title="remove {c.label}"
50
+ onclick={(e) => ctrl.removeLocalCase(c, e)}>&#10005;</button
51
+ >
52
+ </div>
53
+ {/each}
54
+ {#each app.multiCases as c (c.id)}
55
+ <div class="case-chip local" class:active={app.activeMultiId === c.id}>
56
+ <button class="case-activate" onclick={() => ctrl.activateMulti(c)}>
57
+ <span class="cname">{c.label}</span>
58
+ <span class="cregion mono">multiconductor</span>
59
+ </button>
60
+ <button
61
+ class="case-remove mono"
62
+ aria-label="remove {c.label}"
63
+ title="remove {c.label}"
64
+ onclick={(e) => ctrl.removeMultiCase(c, e)}>&#10005;</button
65
+ >
66
+ </div>
67
+ {/each}
68
+ {#if ctrl.showFileDropUi}
69
+ <button
70
+ class="ghost filedrop-ui"
71
+ title="parsed in your browser; the file never uploads"
72
+ onclick={() => fileInput?.click()}
73
+ >
74
+ <span class="cname"><span class="arrow">&#8675;</span>drop a case file</span>
75
+ <span class="cregion mono">case + geo files</span>
76
+ </button>
77
+ {/if}
78
+ </nav>
79
+ <span class="kicker mono">
80
+ <a href={config.orgHref} target="_blank" rel="noreferrer">{config.orgLabel}</a>
81
+ <i class="sep"></i>
82
+ <a href={config.docsHref} target="_blank" rel="noreferrer">docs</a>
83
+ </span>
84
+ <input
85
+ type="file"
86
+ accept=".m,.raw,.aux,.dss,.pwd,.csv,.json,.geojson"
87
+ multiple
88
+ hidden
89
+ bind:this={fileInput}
90
+ onchange={(e) => {
91
+ const input = e.currentTarget;
92
+ if (input.files) ctrl.ingestFiles(Array.from(input.files));
93
+ input.value = '';
94
+ }}
95
+ />
96
+ </header>
97
+
98
+ <style>
99
+ header {
100
+ position: absolute;
101
+ top: 0;
102
+ left: 0;
103
+ right: 0;
104
+ z-index: 10;
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: space-between;
108
+ gap: 16px;
109
+ padding: 10px 20px;
110
+ background: linear-gradient(rgba(236, 233, 226, 0.95), rgba(236, 233, 226, 0));
111
+ animation: drop 0.5s ease-out both;
112
+ }
113
+
114
+ .brand {
115
+ flex: 0 0 auto;
116
+ display: flex;
117
+ align-items: center;
118
+ gap: 10px;
119
+ }
120
+
121
+ h1 {
122
+ margin: 0;
123
+ font-size: 22px;
124
+ font-weight: 600;
125
+ letter-spacing: 0;
126
+ }
127
+
128
+ .cases {
129
+ flex: 1 1 auto;
130
+ min-width: 0;
131
+ display: flex;
132
+ gap: 6px;
133
+ justify-content: center;
134
+ overflow-x: auto;
135
+ scrollbar-width: none;
136
+ }
137
+
138
+ .cases::-webkit-scrollbar {
139
+ display: none;
140
+ }
141
+
142
+ .cases > button,
143
+ .case-chip {
144
+ display: flex;
145
+ align-items: flex-start;
146
+ gap: 1px;
147
+ padding: 5px 12px 4px;
148
+ background: var(--surface-chip);
149
+ border: 1px solid var(--line);
150
+ border-radius: 3px;
151
+ cursor: pointer;
152
+ font-family: var(--font-display);
153
+ color: var(--ink);
154
+ transition: border-color 0.15s ease;
155
+ }
156
+
157
+ .cases > button {
158
+ flex-direction: column;
159
+ }
160
+
161
+ .case-chip {
162
+ align-items: stretch;
163
+ flex: 0 0 auto;
164
+ width: max-content;
165
+ max-width: 156px;
166
+ gap: 0;
167
+ padding: 0;
168
+ overflow: visible;
169
+ position: relative;
170
+ }
171
+
172
+ .case-chip button {
173
+ font-family: var(--font-display);
174
+ color: inherit;
175
+ cursor: pointer;
176
+ }
177
+
178
+ .case-activate {
179
+ display: flex;
180
+ flex-direction: column;
181
+ align-items: flex-start;
182
+ gap: 1px;
183
+ min-width: 0;
184
+ width: 100%;
185
+ padding: 5px 20px 4px 10px;
186
+ background: transparent;
187
+ border: 0;
188
+ }
189
+
190
+ .case-remove {
191
+ position: absolute;
192
+ top: 1px;
193
+ right: 1px;
194
+ display: grid;
195
+ place-items: center;
196
+ width: 18px;
197
+ height: 18px;
198
+ padding: 0;
199
+ background: transparent;
200
+ border: 0;
201
+ color: var(--text-tertiary);
202
+ font-size: 9px;
203
+ line-height: 1;
204
+ }
205
+
206
+ /* The visible button stays small; this pads the actual tap target out to
207
+ at least 44px. .case-chip is overflow: visible so this isn't clipped. */
208
+ .case-remove::before {
209
+ content: '';
210
+ position: absolute;
211
+ inset: -13px;
212
+ }
213
+
214
+ .case-remove:hover,
215
+ .case-remove:focus-visible {
216
+ background: var(--accent-soft);
217
+ border-top-right-radius: 3px;
218
+ color: var(--red);
219
+ }
220
+
221
+ .cases > button:hover,
222
+ .case-chip:hover {
223
+ border-color: var(--accent);
224
+ }
225
+
226
+ .case-chip.active {
227
+ background: var(--panel);
228
+ border-color: var(--accent);
229
+ box-shadow: inset 0 -2px 0 var(--accent);
230
+ }
231
+
232
+ .cname {
233
+ font-size: 12.5px;
234
+ font-weight: 600;
235
+ line-height: 1.2;
236
+ display: inline-flex;
237
+ align-items: center;
238
+ gap: 5px;
239
+ }
240
+
241
+ .cname .mark {
242
+ width: 5px;
243
+ height: 5px;
244
+ background: var(--accent-bright);
245
+ transform: rotate(45deg);
246
+ }
247
+
248
+ .cregion {
249
+ font-size: 9.5px;
250
+ color: var(--text-secondary);
251
+ letter-spacing: 0;
252
+ text-transform: uppercase;
253
+ }
254
+
255
+ .case-chip .cregion {
256
+ white-space: nowrap;
257
+ }
258
+
259
+ .case-chip .cname,
260
+ .case-chip .cregion {
261
+ max-width: 100%;
262
+ overflow: hidden;
263
+ text-overflow: ellipsis;
264
+ }
265
+
266
+ /* Local case chips: dashed border + graphite text, topology only. */
267
+ .case-chip.local {
268
+ border-style: dashed;
269
+ color: var(--text-secondary);
270
+ }
271
+
272
+ .case-chip.local.active {
273
+ background: var(--panel);
274
+ border-color: var(--accent);
275
+ box-shadow: inset 0 -2px 0 var(--accent);
276
+ }
277
+
278
+ /* Ghost chip: standing invitation to drop or pick a case file. */
279
+ .cases > button.ghost {
280
+ background: rgba(252, 251, 247, 0.36);
281
+ border: 1px dashed rgba(178, 94, 0, 0.55);
282
+ color: var(--text-secondary);
283
+ box-shadow: inset 0 0 0 1px rgba(212, 116, 34, 0.08);
284
+ }
285
+
286
+ .cases > button.ghost .cregion {
287
+ white-space: nowrap;
288
+ }
289
+
290
+ .cases > button.ghost:hover {
291
+ border-color: var(--accent);
292
+ background: var(--accent-soft);
293
+ }
294
+
295
+ .cases > button.ghost:hover,
296
+ .cases > button.ghost:hover .cregion {
297
+ color: var(--text-accent);
298
+ }
299
+
300
+ .kicker {
301
+ flex: 0 0 auto;
302
+ display: flex;
303
+ align-items: center;
304
+ font-size: 11px;
305
+ text-transform: uppercase;
306
+ letter-spacing: 0;
307
+ color: var(--text-secondary);
308
+ white-space: nowrap;
309
+ }
310
+
311
+ .kicker a {
312
+ color: var(--text-secondary);
313
+ text-decoration: none;
314
+ }
315
+
316
+ .kicker a:hover {
317
+ color: var(--text-accent);
318
+ }
319
+
320
+ @media (max-width: 760px) {
321
+ header {
322
+ align-items: flex-start;
323
+ flex-wrap: wrap;
324
+ gap: 8px;
325
+ padding: 8px 10px 12px;
326
+ background: linear-gradient(rgba(236, 233, 226, 0.97), rgba(236, 233, 226, 0.72));
327
+ }
328
+
329
+ .brand {
330
+ gap: 8px;
331
+ }
332
+
333
+ h1 {
334
+ font-size: 20px;
335
+ }
336
+
337
+ .kicker {
338
+ margin-left: auto;
339
+ font-size: 9.5px;
340
+ letter-spacing: 0;
341
+ line-height: 2;
342
+ }
343
+
344
+ .cases {
345
+ order: 3;
346
+ width: 100%;
347
+ overflow-x: auto;
348
+ padding-bottom: 2px;
349
+ scrollbar-width: none;
350
+ scroll-padding: 10px;
351
+ scroll-snap-type: x proximity;
352
+ -webkit-overflow-scrolling: touch;
353
+ }
354
+
355
+ .cases::-webkit-scrollbar {
356
+ display: none;
357
+ }
358
+
359
+ .cases > button,
360
+ .case-chip {
361
+ flex: 0 0 auto;
362
+ max-width: 150px;
363
+ min-height: 40px;
364
+ scroll-snap-align: start;
365
+ }
366
+
367
+ .cases > button {
368
+ padding: 7px 10px 6px;
369
+ }
370
+
371
+ .case-activate {
372
+ padding: 7px 24px 6px 10px;
373
+ }
374
+
375
+ .case-remove {
376
+ width: 22px;
377
+ height: 22px;
378
+ }
379
+
380
+ .cname,
381
+ .cregion {
382
+ max-width: 100%;
383
+ white-space: nowrap;
384
+ overflow: hidden;
385
+ text-overflow: ellipsis;
386
+ }
387
+ }
388
+
389
+ @media (max-width: 420px) {
390
+ .kicker {
391
+ display: none;
392
+ }
393
+
394
+ .cases > button,
395
+ .case-chip {
396
+ max-width: 132px;
397
+ }
398
+ }
399
+
400
+ @media (prefers-reduced-motion: reduce) {
401
+ header {
402
+ animation: none;
403
+ }
404
+ }
405
+ </style>
@@ -0,0 +1,4 @@
1
+ declare const AppHeader: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type AppHeader = ReturnType<typeof AppHeader>;
3
+ export default AppHeader;
4
+ //# sourceMappingURL=AppHeader.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppHeader.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/AppHeader.svelte.ts"],"names":[],"mappings":"AA+EA,QAAA,MAAM,SAAS,2DAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -0,0 +1,101 @@
1
+ <script lang="ts">
2
+ import { getAppState, getController } from '../context.svelte.js';
3
+
4
+ const app = getAppState();
5
+ const ctrl = getController();
6
+
7
+ // The active case's binding branches (loading at the thermal limit), joined to
8
+ // the network for identity. Renders nothing when none bind. Each row is a
9
+ // button: the keyboard path for branch selection, mirroring what clicking the
10
+ // line on the map does.
11
+ const bindingLines = $derived.by(() => {
12
+ const c = ctrl.activeSolvable;
13
+ if (!c?.network || !c.solution) return [];
14
+ const byId = new Map(c.network.branches.map((b) => [b.id, b]));
15
+ return c.solution.flows
16
+ .filter((f) => f.loading >= 0.999)
17
+ .flatMap((f) => {
18
+ const branch = byId.get(f.branch);
19
+ return branch ? [{ branch, loading: f.loading }] : [];
20
+ });
21
+ });
22
+
23
+ function select(branchId: number) {
24
+ const c = ctrl.activeSolvable;
25
+ if (!c) return;
26
+ if (ctrl.isBackendCase(c)) ctrl.selectBranch(c.id, branchId, { focus: true });
27
+ else ctrl.selectLocalBranch(c.id, branchId, { focus: true });
28
+ }
29
+ </script>
30
+
31
+ {#if bindingLines.length > 0}
32
+ <div class="binding-lines">
33
+ <p
34
+ class="mono dim head"
35
+ title="lines loaded to their thermal rating; select one to see &part;LMP/&part;rating"
36
+ >
37
+ binding lines
38
+ </p>
39
+ <ul class="mono">
40
+ {#each bindingLines as { branch, loading } (branch.id)}
41
+ <li>
42
+ <button
43
+ class="mono"
44
+ class:selected={app.selectedBranch === branch.id}
45
+ aria-pressed={app.selectedBranch === branch.id}
46
+ onclick={() => select(branch.id)}
47
+ >
48
+ line {branch.from}&#8201;&ndash;&#8201;{branch.to} &middot; {Math.round(
49
+ loading * 100
50
+ )}%
51
+ </button>
52
+ </li>
53
+ {/each}
54
+ </ul>
55
+ </div>
56
+ {/if}
57
+
58
+ <style>
59
+ .binding-lines {
60
+ margin-top: 10px;
61
+ }
62
+
63
+ .head {
64
+ font-size: 10.5px;
65
+ margin: 0 0 4px;
66
+ }
67
+
68
+ ul {
69
+ margin: 0;
70
+ padding: 0;
71
+ list-style: none;
72
+ max-height: 118px;
73
+ overflow-y: auto;
74
+ }
75
+
76
+ li button {
77
+ width: 100%;
78
+ padding: 3px 6px 3px 8px;
79
+ text-align: left;
80
+ font-size: 12px;
81
+ color: var(--ink);
82
+ background: none;
83
+ border: 0;
84
+ border-top: 1px solid var(--line);
85
+ border-radius: 0;
86
+ cursor: pointer;
87
+ }
88
+
89
+ li button:hover {
90
+ background: var(--accent-soft);
91
+ }
92
+
93
+ li button.selected {
94
+ color: var(--text-accent);
95
+ background:
96
+ linear-gradient(90deg, rgba(177, 104, 27, 0.16), rgba(177, 104, 27, 0.05)),
97
+ var(--accent-soft);
98
+ box-shadow: inset 3px 0 0 var(--accent);
99
+ font-weight: 600;
100
+ }
101
+ </style>
@@ -0,0 +1,4 @@
1
+ declare const BindingLines: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type BindingLines = ReturnType<typeof BindingLines>;
3
+ export default BindingLines;
4
+ //# sourceMappingURL=BindingLines.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BindingLines.svelte.d.ts","sourceRoot":"","sources":["../../src/lib/components/BindingLines.svelte.ts"],"names":[],"mappings":"AA0DA,QAAA,MAAM,YAAY,2DAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}