@texturehq/edges 2.4.5 → 2.4.7

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.
@@ -1226,6 +1226,19 @@ interface BaseLayerSpec {
1226
1226
  color?: string;
1227
1227
  /** Optional grouping for organizing layers in the control */
1228
1228
  group?: string;
1229
+ /**
1230
+ * Mutually-exclusive sub-modes, rendered as a dropdown beneath the layer's
1231
+ * checkbox. `selectedId` is a single value rather than a set so "two modes
1232
+ * at once" is unrepresentable, not merely discouraged. Layers without this
1233
+ * render as a plain checkbox.
1234
+ */
1235
+ variants?: {
1236
+ options: ReadonlyArray<{
1237
+ id: string;
1238
+ label: string;
1239
+ }>;
1240
+ selectedId: string;
1241
+ };
1229
1242
  };
1230
1243
  /** Event handlers for user interactions */
1231
1244
  events?: {
@@ -1563,6 +1576,11 @@ interface LayersControl extends BaseControl {
1563
1576
  * Callback to clear all layer selections (hide all layers)
1564
1577
  */
1565
1578
  onClearAll?: () => void;
1579
+ /**
1580
+ * Callback when a layer's selected variant changes. Only fires for layers
1581
+ * declaring `legend.variants`.
1582
+ */
1583
+ onLayerVariantChange?: (layerId: string, variantId: string) => void;
1566
1584
  }
1567
1585
  /**
1568
1586
  * Search control configuration
@@ -2099,6 +2117,71 @@ declare const TextLink: ({ href, children, className, external, title, variant,
2099
2117
  /** Grouping for an entity. Lets surfaces section grid elements and devices
2100
2118
  * apart from core platform objects. */
2101
2119
  type EntityCategory = "platform" | "grid" | "device";
2120
+ /**
2121
+ * The base page composition an entity renders with. Thirty in-scope entities
2122
+ * share these five compositions — the per-entity variation is which blocks
2123
+ * appear (driven by this config), not per-entity page code.
2124
+ *
2125
+ * - `device` — Battery, EV Charger, Inverter, Thermostat, EV, Water Heater.
2126
+ * Leads with `entity.now` (live state + headline metrics).
2127
+ * - `platform` — Site, Customer. No `Now` block.
2128
+ * - `grid-energized` — energized grid equipment (Transformer, Recloser, Meter…).
2129
+ * - `grid-line` — lines & structures (overhead/underground spans, support structures).
2130
+ *
2131
+ * Matches the design's coverage matrix (DESIGN-REFERENCE.md §2). Every entry in
2132
+ * `ENTITY_CONFIG` carries an archetype, even the 12 that have no detail page —
2133
+ * the archetype is the composition it *would* use; `detailPage` gates whether a
2134
+ * page is actually reachable.
2135
+ */
2136
+ type EntityArchetype = "device" | "platform" | "grid-energized" | "grid-line";
2137
+ /**
2138
+ * A serializable `FieldFormat`. `entity.now` and every block that consumes a
2139
+ * config must be JSON-serializable (the core block contract forbids functions
2140
+ * in props), so the function-carrying `custom` format variant is excluded here
2141
+ * at the type level — a metric can never smuggle a `formatter` into the registry.
2142
+ */
2143
+ type SerializableFieldFormat = Exclude<FieldFormat, {
2144
+ type: "custom";
2145
+ }>;
2146
+ /**
2147
+ * One headline metric shown by `entity.now`. A serializable declaration only —
2148
+ * `path` is a dot-path into the resolved entity data, `label` is a translation
2149
+ * key, `format` says how to render the value, and `icon` is a registry key
2150
+ * (never a component). No functions: the host resolves `path`/`format` at render.
2151
+ *
2152
+ * The shape is the serializable subset of `device-config`'s `FieldConfig`
2153
+ * (`id`/`label`/`path`/`format`/`icon`) — `showWhen`/`resolver`/`custom`
2154
+ * formatters are intentionally dropped.
2155
+ */
2156
+ interface HeadlineMetric {
2157
+ /** Stable identifier for the metric (unique within an entity). */
2158
+ id: string;
2159
+ /** Translation key for the display label. */
2160
+ label: string;
2161
+ /** Dot-path into the resolved entity data (e.g. "state.gridPower"). */
2162
+ path: string;
2163
+ /** How to render the value. Serializable formats only. */
2164
+ format: SerializableFieldFormat;
2165
+ /** Optional icon registry key. */
2166
+ icon?: IconName;
2167
+ }
2168
+ /**
2169
+ * A serializable rule for deriving a display state from resolved entity data.
2170
+ * Mirrors `device-config`'s `StateRule` but drops the optional `resolver`
2171
+ * function so the registry stays JSON-serializable. `mapping` maps a raw field
2172
+ * value to a display-state token; `fallback` is used when the field is
2173
+ * missing/unmapped.
2174
+ */
2175
+ interface EntityStateRule {
2176
+ /** Dot-path to the source field in the resolved entity data. */
2177
+ fieldPath: string;
2178
+ /** Raw-value → display-state-token map. */
2179
+ mapping: Record<string, string>;
2180
+ /** State token to use when the field is missing or unmapped. */
2181
+ fallback: string;
2182
+ /** Optional translation key for the state's display label. */
2183
+ label?: string;
2184
+ }
2102
2185
  interface EntityConfig {
2103
2186
  /** Phosphor icon name. The canonical (resting / default) glyph. For switchable
2104
2187
  * elements this mirrors `states.closed.icon` (or the base glyph when the
@@ -2127,6 +2210,40 @@ interface EntityConfig {
2127
2210
  open: EntityStateVariant;
2128
2211
  closed: EntityStateVariant;
2129
2212
  };
2213
+ /**
2214
+ * The base page composition this entity renders with. Required on all 42
2215
+ * entries — the archetype is the composition an entity *would* use even when
2216
+ * `detailPage` is false.
2217
+ */
2218
+ archetype: EntityArchetype;
2219
+ /**
2220
+ * Whether this entity has a reachable detail page. 30 of 42 are `true`; the
2221
+ * registry is the single answer to "does this entity have a page?".
2222
+ */
2223
+ detailPage: boolean;
2224
+ /**
2225
+ * `entity.now` field config. Present only for entities that show a `Now`
2226
+ * block (devices with a `device-config` source, plus Meter). Absent means
2227
+ * `entity.now` declines to render — grid elements omit this until the design's
2228
+ * (currently "For review") metric proposal is approved, and Site/Customer
2229
+ * omit it because their composition has no `Now` block.
2230
+ */
2231
+ fields?: {
2232
+ /**
2233
+ * The 2–4 headline metrics `entity.now` shows. The cell count adapts across
2234
+ * that range; the bound is enforced by the conformance test, not just docs.
2235
+ */
2236
+ headlineMetrics: HeadlineMetric[];
2237
+ };
2238
+ /**
2239
+ * State rules for `entity.now`'s status chips. `primaryState` drives the
2240
+ * pulsing primary chip; `secondaryState` (battery/inverter: charge + grid)
2241
+ * renders a second chip with no pulse.
2242
+ */
2243
+ stateRules?: {
2244
+ primaryState: EntityStateRule;
2245
+ secondaryState?: EntityStateRule;
2246
+ };
2130
2247
  }
2131
2248
  /** One open/closed state's presentation: an optional glyph override and/or a
2132
2249
  * status color. Omitting `icon` means "use the entity's base icon, recolored". */
@@ -2186,6 +2303,8 @@ declare const ENTITY_CONFIG: {
2186
2303
  readonly app: {
2187
2304
  readonly icon: "AppWindow";
2188
2305
  readonly category: "platform";
2306
+ readonly archetype: "platform";
2307
+ readonly detailPage: false;
2189
2308
  readonly label: {
2190
2309
  readonly singular: "App";
2191
2310
  readonly plural: "Apps";
@@ -2195,6 +2314,8 @@ declare const ENTITY_CONFIG: {
2195
2314
  readonly site: {
2196
2315
  readonly icon: "MapPin";
2197
2316
  readonly category: "platform";
2317
+ readonly archetype: "platform";
2318
+ readonly detailPage: true;
2198
2319
  readonly label: {
2199
2320
  readonly singular: "Site";
2200
2321
  readonly plural: "Sites";
@@ -2204,6 +2325,8 @@ declare const ENTITY_CONFIG: {
2204
2325
  readonly device: {
2205
2326
  readonly icon: "ShareNetwork";
2206
2327
  readonly category: "platform";
2328
+ readonly archetype: "platform";
2329
+ readonly detailPage: false;
2207
2330
  readonly label: {
2208
2331
  readonly singular: "Device";
2209
2332
  readonly plural: "Devices";
@@ -2213,6 +2336,8 @@ declare const ENTITY_CONFIG: {
2213
2336
  readonly customer: {
2214
2337
  readonly icon: "Users";
2215
2338
  readonly category: "platform";
2339
+ readonly archetype: "platform";
2340
+ readonly detailPage: true;
2216
2341
  readonly label: {
2217
2342
  readonly singular: "Customer";
2218
2343
  readonly plural: "Customers";
@@ -2222,6 +2347,8 @@ declare const ENTITY_CONFIG: {
2222
2347
  readonly contact: {
2223
2348
  readonly icon: "Users";
2224
2349
  readonly category: "platform";
2350
+ readonly archetype: "platform";
2351
+ readonly detailPage: false;
2225
2352
  readonly label: {
2226
2353
  readonly singular: "Contact";
2227
2354
  readonly plural: "Contacts";
@@ -2231,6 +2358,8 @@ declare const ENTITY_CONFIG: {
2231
2358
  readonly program: {
2232
2359
  readonly icon: "ClipboardText";
2233
2360
  readonly category: "platform";
2361
+ readonly archetype: "platform";
2362
+ readonly detailPage: false;
2234
2363
  readonly label: {
2235
2364
  readonly singular: "Program";
2236
2365
  readonly plural: "Programs";
@@ -2240,6 +2369,8 @@ declare const ENTITY_CONFIG: {
2240
2369
  readonly workflow: {
2241
2370
  readonly icon: "FlowArrow";
2242
2371
  readonly category: "platform";
2372
+ readonly archetype: "platform";
2373
+ readonly detailPage: false;
2243
2374
  readonly label: {
2244
2375
  readonly singular: "Workflow";
2245
2376
  readonly plural: "Workflows";
@@ -2249,6 +2380,8 @@ declare const ENTITY_CONFIG: {
2249
2380
  readonly collection: {
2250
2381
  readonly icon: "Stack";
2251
2382
  readonly category: "platform";
2383
+ readonly archetype: "platform";
2384
+ readonly detailPage: false;
2252
2385
  readonly label: {
2253
2386
  readonly singular: "Collection";
2254
2387
  readonly plural: "Collections";
@@ -2258,6 +2391,8 @@ declare const ENTITY_CONFIG: {
2258
2391
  readonly board: {
2259
2392
  readonly icon: "SquaresFour";
2260
2393
  readonly category: "platform";
2394
+ readonly archetype: "platform";
2395
+ readonly detailPage: false;
2261
2396
  readonly label: {
2262
2397
  readonly singular: "Board";
2263
2398
  readonly plural: "Boards";
@@ -2267,6 +2402,8 @@ declare const ENTITY_CONFIG: {
2267
2402
  readonly report: {
2268
2403
  readonly icon: "ChartLine";
2269
2404
  readonly category: "platform";
2405
+ readonly archetype: "platform";
2406
+ readonly detailPage: false;
2270
2407
  readonly label: {
2271
2408
  readonly singular: "Report";
2272
2409
  readonly plural: "Reports";
@@ -2276,6 +2413,8 @@ declare const ENTITY_CONFIG: {
2276
2413
  readonly event: {
2277
2414
  readonly icon: "ClockCounterClockwise";
2278
2415
  readonly category: "platform";
2416
+ readonly archetype: "platform";
2417
+ readonly detailPage: false;
2279
2418
  readonly label: {
2280
2419
  readonly singular: "Event";
2281
2420
  readonly plural: "Events";
@@ -2285,6 +2424,8 @@ declare const ENTITY_CONFIG: {
2285
2424
  readonly alert: {
2286
2425
  readonly icon: "Warning";
2287
2426
  readonly category: "platform";
2427
+ readonly archetype: "platform";
2428
+ readonly detailPage: false;
2288
2429
  readonly label: {
2289
2430
  readonly singular: "Alert";
2290
2431
  readonly plural: "Alerts";
@@ -2294,6 +2435,8 @@ declare const ENTITY_CONFIG: {
2294
2435
  readonly monitor: {
2295
2436
  readonly icon: "Pulse";
2296
2437
  readonly category: "platform";
2438
+ readonly archetype: "platform";
2439
+ readonly detailPage: false;
2297
2440
  readonly label: {
2298
2441
  readonly singular: "Monitor";
2299
2442
  readonly plural: "Monitors";
@@ -2303,6 +2446,8 @@ declare const ENTITY_CONFIG: {
2303
2446
  readonly command: {
2304
2447
  readonly icon: "Play";
2305
2448
  readonly category: "platform";
2449
+ readonly archetype: "platform";
2450
+ readonly detailPage: false;
2306
2451
  readonly label: {
2307
2452
  readonly singular: "Command";
2308
2453
  readonly plural: "Commands";
@@ -2312,6 +2457,8 @@ declare const ENTITY_CONFIG: {
2312
2457
  readonly source: {
2313
2458
  readonly icon: "TextureSource";
2314
2459
  readonly category: "grid";
2460
+ readonly archetype: "grid-energized";
2461
+ readonly detailPage: true;
2315
2462
  readonly label: {
2316
2463
  readonly singular: "Source";
2317
2464
  readonly plural: "Sources";
@@ -2321,6 +2468,8 @@ declare const ENTITY_CONFIG: {
2321
2468
  readonly substation: {
2322
2469
  readonly icon: "TextureSource";
2323
2470
  readonly category: "grid";
2471
+ readonly archetype: "grid-energized";
2472
+ readonly detailPage: true;
2324
2473
  readonly label: {
2325
2474
  readonly singular: "Substation";
2326
2475
  readonly plural: "Substations";
@@ -2330,6 +2479,8 @@ declare const ENTITY_CONFIG: {
2330
2479
  readonly capacitor: {
2331
2480
  readonly icon: "TextureCapacitor";
2332
2481
  readonly category: "grid";
2482
+ readonly archetype: "grid-energized";
2483
+ readonly detailPage: true;
2333
2484
  readonly label: {
2334
2485
  readonly singular: "Capacitor";
2335
2486
  readonly plural: "Capacitors";
@@ -2339,6 +2490,8 @@ declare const ENTITY_CONFIG: {
2339
2490
  readonly switch: {
2340
2491
  readonly icon: "TextureSwitchClosed";
2341
2492
  readonly category: "grid";
2493
+ readonly archetype: "grid-energized";
2494
+ readonly detailPage: true;
2342
2495
  readonly label: {
2343
2496
  readonly singular: "Switch";
2344
2497
  readonly plural: "Switches";
@@ -2358,6 +2511,8 @@ declare const ENTITY_CONFIG: {
2358
2511
  readonly sectionalizer: {
2359
2512
  readonly icon: "TextureSectionalizerClosed";
2360
2513
  readonly category: "grid";
2514
+ readonly archetype: "grid-energized";
2515
+ readonly detailPage: true;
2361
2516
  readonly label: {
2362
2517
  readonly singular: "Sectionalizer";
2363
2518
  readonly plural: "Sectionalizers";
@@ -2377,6 +2532,8 @@ declare const ENTITY_CONFIG: {
2377
2532
  readonly relay: {
2378
2533
  readonly icon: "Cpu";
2379
2534
  readonly category: "grid";
2535
+ readonly archetype: "grid-energized";
2536
+ readonly detailPage: true;
2380
2537
  readonly label: {
2381
2538
  readonly singular: "Relay";
2382
2539
  readonly plural: "Relays";
@@ -2386,6 +2543,8 @@ declare const ENTITY_CONFIG: {
2386
2543
  readonly transformer: {
2387
2544
  readonly icon: "TextureTransformer";
2388
2545
  readonly category: "grid";
2546
+ readonly archetype: "grid-energized";
2547
+ readonly detailPage: true;
2389
2548
  readonly label: {
2390
2549
  readonly singular: "Transformer";
2391
2550
  readonly plural: "Transformers";
@@ -2395,15 +2554,42 @@ declare const ENTITY_CONFIG: {
2395
2554
  readonly meter: {
2396
2555
  readonly icon: "Gauge";
2397
2556
  readonly category: "grid";
2557
+ readonly archetype: "grid-energized";
2558
+ readonly detailPage: true;
2398
2559
  readonly label: {
2399
2560
  readonly singular: "Meter";
2400
2561
  readonly plural: "Meters";
2401
2562
  };
2402
2563
  readonly description: "Revenue meter at the point of consumption";
2564
+ readonly fields: {
2565
+ readonly headlineMetrics: [{
2566
+ readonly id: "power";
2567
+ readonly label: "device.meter.fields.power";
2568
+ readonly path: "state.phaseARealPower";
2569
+ readonly format: {
2570
+ readonly type: "power";
2571
+ readonly unit: "kW";
2572
+ readonly decimals: 2;
2573
+ };
2574
+ readonly icon: "Pulse";
2575
+ }, {
2576
+ readonly id: "consumption";
2577
+ readonly label: "device.meter.fields.consumption";
2578
+ readonly path: "state.latestKwhUsage";
2579
+ readonly format: {
2580
+ readonly type: "energy";
2581
+ readonly sourceUnit: "kWh";
2582
+ readonly decimals: 2;
2583
+ };
2584
+ readonly icon: "Lightning";
2585
+ }];
2586
+ };
2403
2587
  };
2404
2588
  readonly consumer: {
2405
2589
  readonly icon: "Gauge";
2406
2590
  readonly category: "grid";
2591
+ readonly archetype: "grid-energized";
2592
+ readonly detailPage: true;
2407
2593
  readonly label: {
2408
2594
  readonly singular: "Consumer";
2409
2595
  readonly plural: "Consumers";
@@ -2413,6 +2599,8 @@ declare const ENTITY_CONFIG: {
2413
2599
  readonly singlePhaseOverheadLine: {
2414
2600
  readonly icon: "TextureSinglePhaseOverheadLine";
2415
2601
  readonly category: "grid";
2602
+ readonly archetype: "grid-line";
2603
+ readonly detailPage: true;
2416
2604
  readonly label: {
2417
2605
  readonly singular: "Single-Phase Overhead Line";
2418
2606
  readonly plural: "Single-Phase Overhead Lines";
@@ -2422,6 +2610,8 @@ declare const ENTITY_CONFIG: {
2422
2610
  readonly twoPhaseOverheadLine: {
2423
2611
  readonly icon: "TextureTwoPhaseOverheadLine";
2424
2612
  readonly category: "grid";
2613
+ readonly archetype: "grid-line";
2614
+ readonly detailPage: true;
2425
2615
  readonly label: {
2426
2616
  readonly singular: "Two-Phase Overhead Line";
2427
2617
  readonly plural: "Two-Phase Overhead Lines";
@@ -2431,6 +2621,8 @@ declare const ENTITY_CONFIG: {
2431
2621
  readonly threePhaseOverheadLine: {
2432
2622
  readonly icon: "TextureThreePhaseOverheadLine";
2433
2623
  readonly category: "grid";
2624
+ readonly archetype: "grid-line";
2625
+ readonly detailPage: true;
2434
2626
  readonly label: {
2435
2627
  readonly singular: "Three-Phase Overhead Line";
2436
2628
  readonly plural: "Three-Phase Overhead Lines";
@@ -2440,6 +2632,8 @@ declare const ENTITY_CONFIG: {
2440
2632
  readonly singlePhaseUndergroundLine: {
2441
2633
  readonly icon: "TextureSinglePhaseUndergroundLine";
2442
2634
  readonly category: "grid";
2635
+ readonly archetype: "grid-line";
2636
+ readonly detailPage: true;
2443
2637
  readonly label: {
2444
2638
  readonly singular: "Single-Phase Underground Line";
2445
2639
  readonly plural: "Single-Phase Underground Lines";
@@ -2449,6 +2643,8 @@ declare const ENTITY_CONFIG: {
2449
2643
  readonly twoPhaseUndergroundLine: {
2450
2644
  readonly icon: "TextureTwoPhaseUndergroundLine";
2451
2645
  readonly category: "grid";
2646
+ readonly archetype: "grid-line";
2647
+ readonly detailPage: true;
2452
2648
  readonly label: {
2453
2649
  readonly singular: "Two-Phase Underground Line";
2454
2650
  readonly plural: "Two-Phase Underground Lines";
@@ -2458,6 +2654,8 @@ declare const ENTITY_CONFIG: {
2458
2654
  readonly threePhaseUndergroundLine: {
2459
2655
  readonly icon: "TextureThreePhaseUndergroundLine";
2460
2656
  readonly category: "grid";
2657
+ readonly archetype: "grid-line";
2658
+ readonly detailPage: true;
2461
2659
  readonly label: {
2462
2660
  readonly singular: "Three-Phase Underground Line";
2463
2661
  readonly plural: "Three-Phase Underground Lines";
@@ -2467,6 +2665,8 @@ declare const ENTITY_CONFIG: {
2467
2665
  readonly motorSwitch: {
2468
2666
  readonly icon: "Diamond";
2469
2667
  readonly category: "grid";
2668
+ readonly archetype: "grid-energized";
2669
+ readonly detailPage: true;
2470
2670
  readonly label: {
2471
2671
  readonly singular: "Motor Switch";
2472
2672
  readonly plural: "Motor Switches";
@@ -2476,6 +2676,8 @@ declare const ENTITY_CONFIG: {
2476
2676
  readonly recloser: {
2477
2677
  readonly icon: "TextureRecloser";
2478
2678
  readonly category: "grid";
2679
+ readonly archetype: "grid-energized";
2680
+ readonly detailPage: true;
2479
2681
  readonly label: {
2480
2682
  readonly singular: "Recloser";
2481
2683
  readonly plural: "Reclosers";
@@ -2493,6 +2695,8 @@ declare const ENTITY_CONFIG: {
2493
2695
  readonly fuse: {
2494
2696
  readonly icon: "TextureFuse";
2495
2697
  readonly category: "grid";
2698
+ readonly archetype: "grid-energized";
2699
+ readonly detailPage: true;
2496
2700
  readonly label: {
2497
2701
  readonly singular: "Fuse";
2498
2702
  readonly plural: "Fuses";
@@ -2502,6 +2706,8 @@ declare const ENTITY_CONFIG: {
2502
2706
  readonly breaker: {
2503
2707
  readonly icon: "TextureBreaker";
2504
2708
  readonly category: "grid";
2709
+ readonly archetype: "grid-energized";
2710
+ readonly detailPage: true;
2505
2711
  readonly label: {
2506
2712
  readonly singular: "Breaker";
2507
2713
  readonly plural: "Breakers";
@@ -2519,6 +2725,8 @@ declare const ENTITY_CONFIG: {
2519
2725
  readonly generator: {
2520
2726
  readonly icon: "Wind";
2521
2727
  readonly category: "grid";
2728
+ readonly archetype: "grid-energized";
2729
+ readonly detailPage: true;
2522
2730
  readonly label: {
2523
2731
  readonly singular: "Generator";
2524
2732
  readonly plural: "Generators";
@@ -2528,6 +2736,8 @@ declare const ENTITY_CONFIG: {
2528
2736
  readonly regulator: {
2529
2737
  readonly icon: "TextureRegulator";
2530
2738
  readonly category: "grid";
2739
+ readonly archetype: "grid-energized";
2740
+ readonly detailPage: true;
2531
2741
  readonly label: {
2532
2742
  readonly singular: "Regulator";
2533
2743
  readonly plural: "Regulators";
@@ -2537,6 +2747,8 @@ declare const ENTITY_CONFIG: {
2537
2747
  readonly supportStructure: {
2538
2748
  readonly icon: "TextureSupportStructure";
2539
2749
  readonly category: "grid";
2750
+ readonly archetype: "grid-line";
2751
+ readonly detailPage: true;
2540
2752
  readonly label: {
2541
2753
  readonly singular: "Support Structure";
2542
2754
  readonly plural: "Support Structures";
@@ -2546,51 +2758,256 @@ declare const ENTITY_CONFIG: {
2546
2758
  readonly battery: {
2547
2759
  readonly icon: "BatteryHigh";
2548
2760
  readonly category: "device";
2761
+ readonly archetype: "device";
2762
+ readonly detailPage: true;
2549
2763
  readonly label: {
2550
2764
  readonly singular: "Battery";
2551
2765
  readonly plural: "Batteries";
2552
2766
  };
2553
2767
  readonly description: "Behind-the-meter battery or storage system";
2768
+ readonly fields: {
2769
+ readonly headlineMetrics: [{
2770
+ readonly id: "power";
2771
+ readonly label: "device.battery.fields.power";
2772
+ readonly path: "state.gridPower";
2773
+ readonly format: {
2774
+ readonly type: "power";
2775
+ readonly decimals: 1;
2776
+ };
2777
+ readonly icon: "Lightning";
2778
+ }, {
2779
+ readonly id: "soc";
2780
+ readonly label: "device.battery.fields.soc";
2781
+ readonly path: "state.chargePercentage";
2782
+ readonly format: {
2783
+ readonly type: "percentage";
2784
+ readonly decimals: 0;
2785
+ };
2786
+ readonly icon: "BatteryCharging";
2787
+ }];
2788
+ };
2789
+ readonly stateRules: {
2790
+ readonly primaryState: {
2791
+ readonly label: "device.battery.fields.status";
2792
+ readonly fieldPath: "state.chargingState";
2793
+ readonly mapping: {
2794
+ readonly charging: "charging";
2795
+ readonly discharging: "discharging";
2796
+ readonly idle: "idle";
2797
+ readonly unknown: "unknown";
2798
+ };
2799
+ readonly fallback: "unknown";
2800
+ };
2801
+ readonly secondaryState: {
2802
+ readonly label: "device.battery.fields.gridState";
2803
+ readonly fieldPath: "state.gridStatus";
2804
+ readonly mapping: {
2805
+ readonly importing: "importing";
2806
+ readonly exporting: "exporting";
2807
+ readonly idle: "importing";
2808
+ readonly unknown: "importing";
2809
+ };
2810
+ readonly fallback: "importing";
2811
+ };
2812
+ };
2554
2813
  };
2555
2814
  readonly evCharger: {
2556
2815
  readonly icon: "ChargingStation";
2557
2816
  readonly category: "device";
2817
+ readonly archetype: "device";
2818
+ readonly detailPage: true;
2558
2819
  readonly label: {
2559
2820
  readonly singular: "EV Charger";
2560
2821
  readonly plural: "EV Chargers";
2561
2822
  };
2562
2823
  readonly description: "Electric-vehicle charging station or port";
2824
+ readonly fields: {
2825
+ readonly headlineMetrics: [{
2826
+ readonly id: "power";
2827
+ readonly label: "device.evCharger.fields.power";
2828
+ readonly path: "state.chargerWattage";
2829
+ readonly format: {
2830
+ readonly type: "power";
2831
+ readonly decimals: 1;
2832
+ };
2833
+ readonly icon: "Lightning";
2834
+ }, {
2835
+ readonly id: "isPluggedIn";
2836
+ readonly label: "device.evCharger.fields.isPluggedIn";
2837
+ readonly path: "state.isPluggedIn";
2838
+ readonly format: {
2839
+ readonly type: "boolean";
2840
+ readonly trueText: "Yes";
2841
+ readonly falseText: "No";
2842
+ };
2843
+ readonly icon: "Plug";
2844
+ }];
2845
+ };
2846
+ readonly stateRules: {
2847
+ readonly primaryState: {
2848
+ readonly label: "device.evCharger.fields.status";
2849
+ readonly fieldPath: "state.chargingState";
2850
+ readonly mapping: {
2851
+ readonly charging: "charging";
2852
+ readonly discharging: "discharging";
2853
+ readonly idle: "idle";
2854
+ readonly unknown: "unknown";
2855
+ };
2856
+ readonly fallback: "unknown";
2857
+ };
2858
+ };
2563
2859
  };
2564
2860
  readonly inverter: {
2565
2861
  readonly icon: "Sun";
2566
2862
  readonly category: "device";
2863
+ readonly archetype: "device";
2864
+ readonly detailPage: true;
2567
2865
  readonly label: {
2568
2866
  readonly singular: "Solar / Inverter";
2569
2867
  readonly plural: "Solar / Inverters";
2570
2868
  };
2571
2869
  readonly description: "Solar PV inverter and its connected array";
2870
+ readonly fields: {
2871
+ readonly headlineMetrics: [{
2872
+ readonly id: "power";
2873
+ readonly label: "device.inverter.fields.power";
2874
+ readonly path: "state.power";
2875
+ readonly format: {
2876
+ readonly type: "power";
2877
+ readonly decimals: 0;
2878
+ };
2879
+ readonly icon: "Lightning";
2880
+ }, {
2881
+ readonly id: "gridPower";
2882
+ readonly label: "device.inverter.fields.gridPower";
2883
+ readonly path: "state.gridPower";
2884
+ readonly format: {
2885
+ readonly type: "power";
2886
+ readonly decimals: 0;
2887
+ };
2888
+ readonly icon: "GridFour";
2889
+ }];
2890
+ };
2891
+ readonly stateRules: {
2892
+ readonly primaryState: {
2893
+ readonly label: "device.inverter.fields.gridStatus";
2894
+ readonly fieldPath: "state.gridStatus";
2895
+ readonly mapping: {
2896
+ readonly exporting: "exporting";
2897
+ readonly importing: "importing";
2898
+ readonly idle: "idle";
2899
+ };
2900
+ readonly fallback: "unknown";
2901
+ };
2902
+ };
2572
2903
  };
2573
2904
  readonly thermostat: {
2574
2905
  readonly icon: "Thermometer";
2575
2906
  readonly category: "device";
2907
+ readonly archetype: "device";
2908
+ readonly detailPage: true;
2576
2909
  readonly label: {
2577
2910
  readonly singular: "Thermostat";
2578
2911
  readonly plural: "Thermostats";
2579
2912
  };
2580
2913
  readonly description: "Smart thermostat or connected HVAC control";
2914
+ readonly fields: {
2915
+ readonly headlineMetrics: [{
2916
+ readonly id: "ambientTemperature";
2917
+ readonly label: "device.thermostat.fields.ambientTemperature";
2918
+ readonly path: "state.ambientTemperature";
2919
+ readonly format: {
2920
+ readonly type: "temperature";
2921
+ readonly decimals: 0;
2922
+ };
2923
+ readonly icon: "Thermometer";
2924
+ }, {
2925
+ readonly id: "heatTarget";
2926
+ readonly label: "device.thermostat.fields.heatTarget";
2927
+ readonly path: "state.heatTarget";
2928
+ readonly format: {
2929
+ readonly type: "temperature";
2930
+ readonly decimals: 0;
2931
+ };
2932
+ readonly icon: "Flame";
2933
+ }, {
2934
+ readonly id: "coolTarget";
2935
+ readonly label: "device.thermostat.fields.coolTarget";
2936
+ readonly path: "state.coolTarget";
2937
+ readonly format: {
2938
+ readonly type: "temperature";
2939
+ readonly decimals: 0;
2940
+ };
2941
+ readonly icon: "Snowflake";
2942
+ }];
2943
+ };
2944
+ readonly stateRules: {
2945
+ readonly primaryState: {
2946
+ readonly label: "device.thermostat.fields.operatingMode";
2947
+ readonly fieldPath: "state.operatingMode";
2948
+ readonly mapping: {
2949
+ readonly heat: "heat";
2950
+ readonly cool: "cool";
2951
+ readonly auto: "auto";
2952
+ readonly off: "off";
2953
+ readonly eco: "eco";
2954
+ readonly unknown: "unknown";
2955
+ };
2956
+ readonly fallback: "unknown";
2957
+ };
2958
+ };
2581
2959
  };
2582
2960
  readonly vehicle: {
2583
2961
  readonly icon: "Car";
2584
2962
  readonly category: "device";
2963
+ readonly archetype: "device";
2964
+ readonly detailPage: true;
2585
2965
  readonly label: {
2586
2966
  readonly singular: "EV";
2587
2967
  readonly plural: "EVs";
2588
2968
  };
2589
2969
  readonly description: "Electric vehicle managed as a flexible load";
2970
+ readonly fields: {
2971
+ readonly headlineMetrics: [{
2972
+ readonly id: "chargePercentage";
2973
+ readonly label: "device.vehicle.fields.chargePercentage";
2974
+ readonly path: "state.chargePercentage";
2975
+ readonly format: {
2976
+ readonly type: "percentage";
2977
+ readonly decimals: 0;
2978
+ };
2979
+ readonly icon: "BatteryCharging";
2980
+ }, {
2981
+ readonly id: "range";
2982
+ readonly label: "device.vehicle.fields.range";
2983
+ readonly path: "state.range";
2984
+ readonly format: {
2985
+ readonly type: "distance";
2986
+ readonly unit: "mi";
2987
+ readonly decimals: 0;
2988
+ };
2989
+ readonly icon: "GasPump";
2990
+ }];
2991
+ };
2992
+ readonly stateRules: {
2993
+ readonly primaryState: {
2994
+ readonly label: "device.vehicle.fields.status";
2995
+ readonly fieldPath: "state.chargingState";
2996
+ readonly mapping: {
2997
+ readonly charging: "charging";
2998
+ readonly discharging: "discharging";
2999
+ readonly idle: "idle";
3000
+ readonly unknown: "unknown";
3001
+ };
3002
+ readonly fallback: "unknown";
3003
+ };
3004
+ };
2590
3005
  };
2591
3006
  readonly waterHeater: {
2592
3007
  readonly icon: "Drop";
2593
3008
  readonly category: "device";
3009
+ readonly archetype: "device";
3010
+ readonly detailPage: true;
2594
3011
  readonly label: {
2595
3012
  readonly singular: "Water Heater";
2596
3013
  readonly plural: "Water Heaters";
@@ -2645,6 +3062,48 @@ declare function getEntityLabel(entityType: EntityType, plural?: boolean): strin
2645
3062
  * @returns The entity's category
2646
3063
  */
2647
3064
  declare function getEntityCategory(entityType: EntityType): EntityCategory;
3065
+ /**
3066
+ * Inclusive bounds on `fields.headlineMetrics`. The design's `entity.now`
3067
+ * acceptance criteria say the cell count adapts across this range. Exported so
3068
+ * the conformance test enforces the bound rather than documenting it.
3069
+ */
3070
+ declare const HEADLINE_METRIC_BOUNDS: {
3071
+ readonly min: 2;
3072
+ readonly max: 4;
3073
+ };
3074
+ /**
3075
+ * Resolve the base page composition for an entity. This is what lets the 30
3076
+ * in-scope entities share five compositions instead of 30 pages of page code.
3077
+ *
3078
+ * @param entityType - The entity type key
3079
+ * @returns The entity's archetype
3080
+ * @throws If `entityType` is not a known entity (never returns `undefined`, so
3081
+ * a typo surfaces as a loud error rather than a silently missing composition).
3082
+ *
3083
+ * @example
3084
+ * ```tsx
3085
+ * archetypeFor("battery"); // "device"
3086
+ * archetypeFor("transformer"); // "grid-energized"
3087
+ * archetypeFor("nope"); // throws
3088
+ * ```
3089
+ */
3090
+ declare function archetypeFor(entityType: EntityType): EntityArchetype;
3091
+ /**
3092
+ * Whether an entity has a reachable detail page. The registry is the single
3093
+ * answer to "does this entity have a page?".
3094
+ *
3095
+ * @param entityType - The entity type key
3096
+ */
3097
+ declare function entityHasDetailPage(entityType: EntityType): boolean;
3098
+ /**
3099
+ * Whether `entity.now` renders a metric grid for this entity. True iff the
3100
+ * entity declares at least one headline metric. Grid elements (no metric
3101
+ * registry yet), Site, Customer and Water Heater return `false` — `entity.now`
3102
+ * declines to render its cells rather than crashing or rendering empty.
3103
+ *
3104
+ * @param entityType - The entity type key
3105
+ */
3106
+ declare function entityShowsNow(entityType: EntityType): boolean;
2648
3107
 
2649
3108
  /**
2650
3109
  * Resolves a CSS variable to its computed color value.
@@ -2705,4 +3164,4 @@ declare const getContrastingTextColor: (backgroundColor: string) => string;
2705
3164
  */
2706
3165
  declare const mapValuesToCategoricalColors: (values: (string | number)[]) => Record<string | number, string>;
2707
3166
 
2708
- export { type DistanceFormat as $, type ActionItem as A, type BadgeProps as B, type ChartMargin as C, getContrastingTextColor as D, ENTITY_CONFIG as E, getDefaultChartColor as F, getDefaultColors as G, Heading as H, type InteractiveMapProps as I, getEntityConfig as J, getEntityIcon as K, Loader as L, type MapPoint as M, getEntityLabel as N, getResolvedColor as O, getThemeCategoricalColors as P, getYFormatSettings as Q, isLightColor as R, type SegmentOption as S, TextLink as T, type FieldValue as U, type BooleanFormat as V, type FormattedValue as W, type FieldFormat as X, type YFormatSettings as Y, type CurrentFormat as Z, type DateFormat as _, type ActionMenuProps as a, type VoltageUnit as a$, type EnergyUnit as a0, type EnergyFormat as a1, type CurrencyFormat as a2, type NumberFormat as a3, type PhoneFormat as a4, type PowerFormat as a5, type FormatterFunction as a6, type ResistanceFormat as a7, type TemperatureFormat as a8, type TemperatureUnitString as a9, ENTITY_CATEGORY_CONFIG as aA, type EntityCategory as aB, type EntityCategoryConfig as aC, GRID_STATE_COLORS as aD, type GridStateColor as aE, InteractiveMap as aF, type InteractiveMapHandle as aG, type LayerFeature as aH, type LayerStyle as aI, MAP_TYPES as aJ, type MapType as aK, Meter as aL, type MetricFormat as aM, type PercentageFormat as aN, type PowerUnit as aO, type RenderType as aP, type ResistanceUnit as aQ, SegmentedControl as aR, StackNav as aS, type StackNavGroup as aT, type StackNavItem as aU, type StackNavLinkComponentProps as aV, type StackNavProps as aW, type StackNavTheme as aX, StaticMap as aY, type TextTransform as aZ, type TextTruncatePosition as a_, type TemperatureUnit as aa, type TextFormat as ab, type VoltageFormat as ac, type DeviceState as ad, type GridState as ae, type ComponentFormatter as af, type LayerSpec as ag, type CustomPinsSpec as ah, type GeoJsonLayerSpec as ai, type RasterLayerSpec as aj, type VectorLayerSpec as ak, type ClusteredVectorLayerSpec as al, ActionMenu as am, AppShell as an, Avatar as ao, Badge as ap, type BaseFormat as aq, ChartContext as ar, CodeEditor as as, type ColorSpec as at, type ComponentFormatOptions as au, type CurrentUnit as av, type CustomFormat as aw, DEFAULT_MAP_TYPE as ax, type DateFormatStyle as ay, type DistanceUnit as az, type AppShellProps as b, type ZoomStops as b0, activeDeviceStates as b1, baselineFromPoint as b2, deviceStateLabels as b3, deviceStateMetricFormats as b4, formatComponentValue as b5, getDeviceStateLabel as b6, getEntityCategory as b7, getGridStateLabel as b8, gridStateLabels as b9, isActiveState as ba, mapValuesToCategoricalColors as bb, useChartContext as bc, useComponentFormatter as bd, type AvatarProps as c, type BaseDataPoint as d, type CodeEditorProps as e, type CodeLanguage as f, type CodeTheme as g, type EntityConfig as h, type EntityType as i, Logo as j, type MeterProps as k, type SegmentedControlProps as l, SideNav as m, type SideNavItem as n, type SideNavProps as o, type StaticMapProps as p, type TooltipData as q, type TooltipSeries as r, TopNav as s, type TopNavProps as t, type YFormatType as u, clearColorCache as v, createCategoryColorMap as w, createXScale as x, createYScale as y, defaultMargin as z };
3167
+ export { getYFormatSettings as $, type ActionItem as A, type BadgeProps as B, type ChartMargin as C, archetypeFor as D, ENTITY_CONFIG as E, clearColorCache as F, createCategoryColorMap as G, HEADLINE_METRIC_BOUNDS as H, type InteractiveMapProps as I, createXScale as J, createYScale as K, Loader as L, type MapPoint as M, defaultMargin as N, entityHasDetailPage as O, entityShowsNow as P, getContrastingTextColor as Q, getDefaultChartColor as R, type SegmentOption as S, TextLink as T, getDefaultColors as U, getEntityConfig as V, getEntityIcon as W, getEntityLabel as X, type YFormatSettings as Y, getResolvedColor as Z, getThemeCategoricalColors as _, type ActionMenuProps as a, type StackNavGroup as a$, isLightColor as a0, type FieldValue as a1, type BooleanFormat as a2, type FormattedValue as a3, type FieldFormat as a4, type CurrentFormat as a5, type DateFormat as a6, type DistanceFormat as a7, type EnergyUnit as a8, type EnergyFormat as a9, CodeEditor as aA, type ColorSpec as aB, type ComponentFormatOptions as aC, type CurrentUnit as aD, type CustomFormat as aE, DEFAULT_MAP_TYPE as aF, type DateFormatStyle as aG, type DistanceUnit as aH, ENTITY_CATEGORY_CONFIG as aI, type EntityCategory as aJ, type EntityCategoryConfig as aK, GRID_STATE_COLORS as aL, type GridStateColor as aM, InteractiveMap as aN, type InteractiveMapHandle as aO, type LayerFeature as aP, type LayerStyle as aQ, MAP_TYPES as aR, type MapType as aS, Meter as aT, type MetricFormat as aU, type PercentageFormat as aV, type PowerUnit as aW, type RenderType as aX, type ResistanceUnit as aY, SegmentedControl as aZ, StackNav as a_, type CurrencyFormat as aa, type NumberFormat as ab, type PhoneFormat as ac, type PowerFormat as ad, type FormatterFunction as ae, type ResistanceFormat as af, type TemperatureFormat as ag, type TemperatureUnitString as ah, type TemperatureUnit as ai, type TextFormat as aj, type VoltageFormat as ak, type DeviceState as al, type GridState as am, type ComponentFormatter as an, type LayerSpec as ao, type CustomPinsSpec as ap, type GeoJsonLayerSpec as aq, type RasterLayerSpec as ar, type VectorLayerSpec as as, type ClusteredVectorLayerSpec as at, ActionMenu as au, AppShell as av, Avatar as aw, Badge as ax, type BaseFormat as ay, ChartContext as az, type AppShellProps as b, type StackNavItem as b0, type StackNavLinkComponentProps as b1, type StackNavProps as b2, type StackNavTheme as b3, StaticMap as b4, type TextTransform as b5, type TextTruncatePosition as b6, type VoltageUnit as b7, type ZoomStops as b8, activeDeviceStates as b9, baselineFromPoint as ba, deviceStateLabels as bb, deviceStateMetricFormats as bc, formatComponentValue as bd, getDeviceStateLabel as be, getEntityCategory as bf, getGridStateLabel as bg, gridStateLabels as bh, isActiveState as bi, mapValuesToCategoricalColors as bj, useChartContext as bk, useComponentFormatter as bl, type AvatarProps as c, type BaseDataPoint as d, type CodeEditorProps as e, type CodeLanguage as f, type CodeTheme as g, type EntityArchetype as h, type EntityConfig as i, type EntityStateRule as j, type EntityType as k, Heading as l, type HeadlineMetric as m, Logo as n, type MeterProps as o, type SegmentedControlProps as p, type SerializableFieldFormat as q, SideNav as r, type SideNavItem as s, type SideNavProps as t, type StaticMapProps as u, type TooltipData as v, type TooltipSeries as w, TopNav as x, type TopNavProps as y, type YFormatType as z };