@fusion.dev/helix 0.181.2 → 0.182.1

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.
@@ -10173,6 +10173,62 @@
10173
10173
  "module": "src/bar-chart/root/bar-chart-root.ts"
10174
10174
  }
10175
10175
  },
10176
+ {
10177
+ "description": "The color of a bar whose cell says `data-tone=\"accent\"`.",
10178
+ "name": "--bar-chart-tone-accent",
10179
+ "inheritedFrom": {
10180
+ "name": "HelixHeadlessBarChart",
10181
+ "module": "src/bar-chart/root/bar-chart-root.ts"
10182
+ }
10183
+ },
10184
+ {
10185
+ "description": "The color of a bar whose cell says `data-tone=\"danger\"`.",
10186
+ "name": "--bar-chart-tone-danger",
10187
+ "inheritedFrom": {
10188
+ "name": "HelixHeadlessBarChart",
10189
+ "module": "src/bar-chart/root/bar-chart-root.ts"
10190
+ }
10191
+ },
10192
+ {
10193
+ "description": "The color of a bar whose cell says `data-tone=\"info\"`.",
10194
+ "name": "--bar-chart-tone-info",
10195
+ "inheritedFrom": {
10196
+ "name": "HelixHeadlessBarChart",
10197
+ "module": "src/bar-chart/root/bar-chart-root.ts"
10198
+ }
10199
+ },
10200
+ {
10201
+ "description": "The color of a bar whose cell says `data-tone=\"muted\"`.",
10202
+ "name": "--bar-chart-tone-muted",
10203
+ "inheritedFrom": {
10204
+ "name": "HelixHeadlessBarChart",
10205
+ "module": "src/bar-chart/root/bar-chart-root.ts"
10206
+ }
10207
+ },
10208
+ {
10209
+ "description": "The color of a bar whose cell says `data-tone=\"primary\"`. A tone with no property of its own falls back to the bar's series color, and the same holds for a name you define.",
10210
+ "name": "--bar-chart-tone-primary",
10211
+ "inheritedFrom": {
10212
+ "name": "HelixHeadlessBarChart",
10213
+ "module": "src/bar-chart/root/bar-chart-root.ts"
10214
+ }
10215
+ },
10216
+ {
10217
+ "description": "The color of a bar whose cell says `data-tone=\"success\"`.",
10218
+ "name": "--bar-chart-tone-success",
10219
+ "inheritedFrom": {
10220
+ "name": "HelixHeadlessBarChart",
10221
+ "module": "src/bar-chart/root/bar-chart-root.ts"
10222
+ }
10223
+ },
10224
+ {
10225
+ "description": "The color of a bar whose cell says `data-tone=\"warning\"`.",
10226
+ "name": "--bar-chart-tone-warning",
10227
+ "inheritedFrom": {
10228
+ "name": "HelixHeadlessBarChart",
10229
+ "module": "src/bar-chart/root/bar-chart-root.ts"
10230
+ }
10231
+ },
10176
10232
  {
10177
10233
  "description": "The gradient for a bar, named per bar so the styled tier can use it as the `fill`. It is always drawn, and used only when `wash=\"gradient\"` is set.",
10178
10234
  "name": "--chart-bar-wash",
@@ -10880,7 +10936,7 @@
10880
10936
  {
10881
10937
  "name": "bar",
10882
10938
  "type": {
10883
- "text": "(\n value: number,\n index: number,\n depth: number,\n tone: number,\n scale: (value: number) => number,\n from?: number,\n end?: StackEnd,\n ) => TemplateResult"
10939
+ "text": "(\n value: number,\n index: number,\n depth: number,\n tone: number,\n scale: (value: number) => number,\n from?: number,\n end?: StackEnd,\n named?: string,\n ) => TemplateResult"
10884
10940
  }
10885
10941
  }
10886
10942
  ],
@@ -11687,7 +11743,7 @@
11687
11743
  "part": "Root",
11688
11744
  "examples": [
11689
11745
  {
11690
- "description": "Revenue by quarter, drawn from a table whose cells show text and hold the plotted number in `data-value`.",
11746
+ "description": "Revenue by quarter, drawn from a table whose cells show text and hold the plotted number in `data-value`, then signups by channel stacked into one column a quarter, and a net figure that goes below zero.",
11691
11747
  "slug": "preview"
11692
11748
  },
11693
11749
  {
@@ -11783,13 +11839,21 @@
11783
11839
  }
11784
11840
  },
11785
11841
  {
11786
- "description": "Under `stacked`, the segment at a column's far end: the last positive segment above zero and the last negative one below it. Its `data-end` is `positive` or `negative`. A blank or a zero segment is never the end. Set together with `bar`.",
11842
+ "description": "The bar at a column's far end, the end away from zero, which is the end to round. A column standing alone is its own far end. Under `stacked`, the last positive segment above zero and the last negative one below it. Its `data-end` is `positive` or `negative`. A blank or a zero is never the end. Set together with `bar`.",
11787
11843
  "name": "bar-end",
11788
11844
  "inheritedFrom": {
11789
11845
  "name": "HelixHeadlessBarChart",
11790
11846
  "module": "src/bar-chart/root/bar-chart-root.ts"
11791
11847
  }
11792
11848
  },
11849
+ {
11850
+ "description": "A far end below zero, whose far end is its bottom, or its inline start when the bars run across. Set together with `bar-end`.",
11851
+ "name": "bar-end-negative",
11852
+ "inheritedFrom": {
11853
+ "name": "HelixHeadlessBarChart",
11854
+ "module": "src/bar-chart/root/bar-chart-root.ts"
11855
+ }
11856
+ },
11793
11857
  {
11794
11858
  "description": "The far stop of a bar's gradient, where one is used.",
11795
11859
  "name": "bar-wash-end",
@@ -197448,6 +197512,24 @@
197448
197512
  "default": "0.5",
197449
197513
  "description": "The tightest any form turns, as a share of the box's height: the radius of\nits sharpest curve.\n\nHalf the height. A bend's apex a few dozen pixels round kinked the strands\nthat ran along its inside, and read as a fold in the light where every form\nis meant to be one sweep. `path.pure.spec.ts` measures it on every form."
197450
197514
  },
197515
+ {
197516
+ "kind": "variable",
197517
+ "name": "SPREAD",
197518
+ "type": {
197519
+ "text": "number"
197520
+ },
197521
+ "default": "1.35",
197522
+ "description": "The ribbon's width over each form's own at a `spread` of `1`, which the\nrenderer multiplies in. Here so the path can know how far its strands reach."
197523
+ },
197524
+ {
197525
+ "kind": "variable",
197526
+ "name": "SHAPE",
197527
+ "type": {
197528
+ "text": "PathShape"
197529
+ },
197530
+ "default": "{ lean: 0.1, period: 1.4, sweep: 0.4, swing: 0.6 }",
197531
+ "description": "The shape a page draws unless it asks for another: calm, the ribbon\nswinging six tenths of the most its turns allow over S curves 2.4 screens\nlong, resting a tenth of the width right of the middle. Compared down the\nwhole landing page against wider, quicker, and leaning shapes, this one\nread as one long current rather than a ribbon zigzagging between cards."
197532
+ },
197451
197533
  {
197452
197534
  "kind": "function",
197453
197535
  "name": "boxPath",
@@ -197531,6 +197613,14 @@
197531
197613
  "text": "number"
197532
197614
  },
197533
197615
  "description": "The page's height, in CSS pixels."
197616
+ },
197617
+ {
197618
+ "name": "shape",
197619
+ "default": "SHAPE",
197620
+ "type": {
197621
+ "text": "PathShape"
197622
+ },
197623
+ "description": "The ribbon's shape down the page."
197534
197624
  }
197535
197625
  ],
197536
197626
  "description": "The ribbon laid down a whole page, which is what an element with `travel`\nshows.\n\nEach stop places its form at its target, in page order. Between two stops\nthe ribbon fills the gap with soft S-curves in `fill`'s place, except\nbetween two waves less than a screen and a half apart, where it turns off\nscreen so each wave reads alone.\nWithout stops, `fill` runs the length of the page: a bend opens the page and\na ribbon follows it, waves cross every screen and a third, and a ribbon\nmeanders the whole way. After a last wave the ribbon does not come back, so\nthe page ends on the wave."
@@ -197577,6 +197667,32 @@
197577
197667
  ],
197578
197668
  "description": "One coordinate of a uniform Catmull-Rom segment, the same one the shader runs."
197579
197669
  },
197670
+ {
197671
+ "kind": "function",
197672
+ "name": "arcLengths",
197673
+ "return": {
197674
+ "type": {
197675
+ "text": "Float32Array"
197676
+ }
197677
+ },
197678
+ "parameters": [
197679
+ {
197680
+ "name": "points",
197681
+ "type": {
197682
+ "text": "readonly PathPoint[]"
197683
+ },
197684
+ "description": "The path."
197685
+ },
197686
+ {
197687
+ "name": "width",
197688
+ "type": {
197689
+ "text": "number"
197690
+ },
197691
+ "description": "The box's width, in CSS pixels, which `x` is a share of."
197692
+ }
197693
+ ],
197694
+ "description": "How far along the drawn centerline each point is, in CSS pixels, for the\nshader to set every frequency by.\n\n**Arc length, which only grows.** The shader used the sum of a point's `y`\nand its `x` in pixels, which grows down a page and across a wave heading\nright, and runs backward wherever the path heads left faster than it heads\ndown: on a bend's upper arm, and across a wave sweeping left. There the\nstrands' twist and waves ran in reverse, and every strand turned a corner\nat the same place.\n\nSixteen samples a segment of the same spline the shader draws."
197695
+ },
197580
197696
  {
197581
197697
  "kind": "function",
197582
197698
  "name": "lookup",
@@ -197643,6 +197759,22 @@
197643
197759
  "module": "src/strands/path.ts"
197644
197760
  }
197645
197761
  },
197762
+ {
197763
+ "kind": "js",
197764
+ "name": "SPREAD",
197765
+ "declaration": {
197766
+ "name": "SPREAD",
197767
+ "module": "src/strands/path.ts"
197768
+ }
197769
+ },
197770
+ {
197771
+ "kind": "js",
197772
+ "name": "SHAPE",
197773
+ "declaration": {
197774
+ "name": "SHAPE",
197775
+ "module": "src/strands/path.ts"
197776
+ }
197777
+ },
197646
197778
  {
197647
197779
  "kind": "js",
197648
197780
  "name": "boxPath",
@@ -197667,6 +197799,14 @@
197667
197799
  "module": "src/strands/path.ts"
197668
197800
  }
197669
197801
  },
197802
+ {
197803
+ "kind": "js",
197804
+ "name": "arcLengths",
197805
+ "declaration": {
197806
+ "name": "arcLengths",
197807
+ "module": "src/strands/path.ts"
197808
+ }
197809
+ },
197670
197810
  {
197671
197811
  "kind": "js",
197672
197812
  "name": "lookup",
@@ -198133,6 +198273,14 @@
198133
198273
  }
198134
198274
  ],
198135
198275
  "exports": [
198276
+ {
198277
+ "kind": "js",
198278
+ "name": "MAX_QUIET",
198279
+ "declaration": {
198280
+ "name": "MAX_QUIET",
198281
+ "module": "src/strands/renderer.ts"
198282
+ }
198283
+ },
198136
198284
  {
198137
198285
  "kind": "js",
198138
198286
  "name": "MAX_STRANDS",
@@ -198201,7 +198349,7 @@
198201
198349
  "declarations": [
198202
198350
  {
198203
198351
  "kind": "class",
198204
- "description": "Hundreds of thin strands are drawn as one ribbon, and the light they add\ntogether is bloomed, so where they converge the ribbon burns hot. The ribbon\nmoves on its own all the time: it sways and breathes, its strands wave and\nroll over one another, and light runs along them.\n\nIts palette comes from the tokens. The strands are `--helix-color-primary`,\na few are `--helix-color-accent` for depth, and the page they light is\n`--helix-color-background`. On a light page the light becomes ink, because\nlight added to near-white cannot be seen.\n\n**Three forms, set with `variant`.** `bend` turns bright strands through one tight point, with its\narms opening to the right. `ribbon`, the default, is a broad, soft ribbon in\nlong curves. `waves` is sharp strands crossing the full width.\n\n**In its own box, by default.** The element fills the box it is given, the\nway an image does, and `at` places the bend's apex or the ribbon's axis across\nit. Content placed inside sits over the ribbon, in the `content` part.\n\n**Down the whole page, with `travel`.** The element fixes itself behind the\npage, on `--helix-layer-beneath`, and lays one ribbon from the top of the page to the\nbottom, so scrolling travels along it. Put `helix-strands-stop` elements\ninside to pin a form to a section: each names an element with `for` and says\nwhich form the ribbon takes there. Between stops the ribbon fills the gap in\nits own `variant`, and between two waves less than a screen and a half\napart it turns off screen so each wave reads alone. Set `z-index` on the\nelement to move it.\n\nThe ribbon stops moving in these cases:\n- `prefers-reduced-motion` is set. The element shows one still frame, and in\n `travel` still follows the scroll, without easing.\n- The element is off screen or in a background tab.\n\nIt shows a gradient from the same tokens in these cases, and matches\n`:state(fallback)` and sends `helix-strands-fallback` with the reason:\n- The renderer draws on the CPU, such as a headless browser or a machine with\n no usable GPU.\n- There is no WebGL, or the browser took the drawing context back.\n\nStrands is styled only and has behavior, as `Aura` does: its behavior is\ndrawing to a canvas, so there is no structure for a headless version to hold.",
198352
+ "description": "Hundreds of thin strands are drawn as one ribbon, and the light they add\ntogether is bloomed, so where they converge the ribbon burns hot. The ribbon\nmoves on its own all the time: it sways and breathes, its strands wave and\nroll over one another, and light runs along them.\n\nIts palette comes from the tokens. The strands are `--helix-color-primary`,\na few are `--helix-color-accent` for depth, and the page they light is\n`--helix-color-background`. On a light page the light becomes ink, because\nlight added to near-white cannot be seen.\n\n**Four forms, set with `variant`.** `bend` turns bright strands through one\ntight point, with its arms opening to the right. `ribbon`, the default, is a\nbroad, soft ribbon in long curves. `sweep` enters at a top corner, the one\n`at` is nearer, and crosses in one diagonal to the far side. `waves` is sharp\nstrands crossing the full width.\n\n**Quiet behind words.** `quiet` names elements, such as a hero's words, that\nthe light dims behind to a faint glow, so a bright ribbon can cross the page\nunder them and they stay crisp.\n\n**In its own box, by default.** The element fills the box it is given, the\nway an image does, and `at` places the bend's apex or the ribbon's axis across\nit. Content placed inside sits over the ribbon, in the `content` part.\n\n**Down the whole page, with `travel`.** The element fixes itself behind the\npage, on `--helix-layer-beneath`, and lays one ribbon from the top of the page to the\nbottom, so scrolling travels along it. Put `helix-strands-stop` elements\ninside to pin a form to a section: each names an element with `for` and says\nwhich form the ribbon takes there. Between stops the ribbon fills the gap in\nits own `variant`, and between two waves less than a screen and a half\napart it turns off screen so each wave reads alone. Set `z-index` on the\nelement to move it.\n\nThe ribbon stops moving in these cases:\n- `prefers-reduced-motion` is set. The element shows one still frame, and in\n `travel` still follows the scroll, without easing.\n- The element is off screen or in a background tab.\n\nIt shows a gradient from the same tokens in these cases, and matches\n`:state(fallback)` and sends `helix-strands-fallback` with the reason:\n- The renderer draws on the CPU, such as a headless browser or a machine with\n no usable GPU.\n- There is no WebGL, or the browser took the drawing context back.\n\nStrands is styled only and has behavior, as `Aura` does: its behavior is\ndrawing to a canvas, so there is no structure for a headless version to hold.",
198205
198353
  "name": "HelixStrands",
198206
198354
  "cssProperties": [
198207
198355
  {
@@ -198294,7 +198442,7 @@
198294
198442
  "text": "number"
198295
198443
  },
198296
198444
  "default": "1",
198297
- "description": "How differently each strand moves, from `0`, the whole ribbon rolling and\nwaving as one, to `1`, each strand at its own pace and angle, crossing\nover the others. Clamped to that range.",
198445
+ "description": "How differently the strands move across the ribbon, from `0`, the whole\nribbon rolling and waving as one, to `1`, its edges flowing at their own\npace past its middle. Neighbors always move together, so no strand leaves\nits lane. Clamped to that range.",
198298
198446
  "attribute": "drift"
198299
198447
  },
198300
198448
  {
@@ -198317,6 +198465,15 @@
198317
198465
  "description": "Overall brightness, as a multiplier on the light.",
198318
198466
  "attribute": "gain"
198319
198467
  },
198468
+ {
198469
+ "kind": "field",
198470
+ "name": "quiet",
198471
+ "type": {
198472
+ "text": "string | undefined"
198473
+ },
198474
+ "description": "The ids of elements the light dims behind, separated by spaces, such as\na hero's words. Inside each, the ribbon keeps a faint glow, so words over\nit stay crisp and nothing busy runs under them, and it eases back to full\na short way past each edge. Up to four; ids that name nothing are skipped.",
198475
+ "attribute": "quiet"
198476
+ },
198320
198477
  {
198321
198478
  "kind": "field",
198322
198479
  "name": "softness",
@@ -198362,7 +198519,7 @@
198362
198519
  "kind": "field",
198363
198520
  "name": "variant",
198364
198521
  "type": {
198365
- "text": "'bend' | 'ribbon' | 'waves' | undefined"
198522
+ "text": "'bend' | 'ribbon' | 'sweep' | 'waves' | undefined"
198366
198523
  },
198367
198524
  "description": "The form the ribbon takes. When it is not set, the form is `ribbon`. In\n`travel`, the form of every stretch no stop claims.",
198368
198525
  "attribute": "variant"
@@ -198520,6 +198677,21 @@
198520
198677
  "privacy": "private",
198521
198678
  "default": "0"
198522
198679
  },
198680
+ {
198681
+ "kind": "field",
198682
+ "name": "lengths",
198683
+ "privacy": "private",
198684
+ "default": "new Float32Array(MAX_POINTS)",
198685
+ "description": "How far along the path each point is, in pixels, four to a slot as the shader reads them."
198686
+ },
198687
+ {
198688
+ "kind": "field",
198689
+ "name": "quietRects",
198690
+ "privacy": "private",
198691
+ "readonly": true,
198692
+ "default": "new Float32Array(MAX_QUIET * 4)",
198693
+ "description": "The rects the light dims behind, in the path's pixels, from `quiet`."
198694
+ },
198523
198695
  {
198524
198696
  "kind": "field",
198525
198697
  "name": "table",
@@ -198717,6 +198889,17 @@
198717
198889
  },
198718
198890
  "description": "Lays the path out again: inside the box, or down the page from the stops.\n\nCheap, a few dozen points and a table of a few hundred numbers, so it runs\non any change that could move the path rather than working out which did."
198719
198891
  },
198892
+ {
198893
+ "kind": "method",
198894
+ "name": "measureQuiet",
198895
+ "privacy": "private",
198896
+ "return": {
198897
+ "type": {
198898
+ "text": "void"
198899
+ }
198900
+ },
198901
+ "description": "The rects of the elements `quiet` names: on the page in `travel`, where\nthe path is laid in page pixels, and inside the box otherwise."
198902
+ },
198720
198903
  {
198721
198904
  "kind": "method",
198722
198905
  "name": "placedStops",
@@ -199241,7 +199424,7 @@
199241
199424
  "text": "number"
199242
199425
  },
199243
199426
  "default": "1",
199244
- "description": "How differently each strand moves, from `0`, the whole ribbon rolling and\nwaving as one, to `1`, each strand at its own pace and angle, crossing\nover the others. Clamped to that range.",
199427
+ "description": "How differently the strands move across the ribbon, from `0`, the whole\nribbon rolling and waving as one, to `1`, its edges flowing at their own\npace past its middle. Neighbors always move together, so no strand leaves\nits lane. Clamped to that range.",
199245
199428
  "fieldName": "drift"
199246
199429
  },
199247
199430
  {
@@ -199262,6 +199445,14 @@
199262
199445
  "description": "Overall brightness, as a multiplier on the light.",
199263
199446
  "fieldName": "gain"
199264
199447
  },
199448
+ {
199449
+ "name": "quiet",
199450
+ "type": {
199451
+ "text": "string | undefined"
199452
+ },
199453
+ "description": "The ids of elements the light dims behind, separated by spaces, such as\na hero's words. Inside each, the ribbon keeps a faint glow, so words over\nit stay crisp and nothing busy runs under them, and it eases back to full\na short way past each edge. Up to four; ids that name nothing are skipped.",
199454
+ "fieldName": "quiet"
199455
+ },
199265
199456
  {
199266
199457
  "name": "softness",
199267
199458
  "type": {
@@ -199301,7 +199492,7 @@
199301
199492
  {
199302
199493
  "name": "variant",
199303
199494
  "type": {
199304
- "text": "'bend' | 'ribbon' | 'waves' | undefined"
199495
+ "text": "'bend' | 'ribbon' | 'sweep' | 'waves' | undefined"
199305
199496
  },
199306
199497
  "description": "The form the ribbon takes. When it is not set, the form is `ribbon`. In\n`travel`, the form of every stretch no stop claims.",
199307
199498
  "fieldName": "variant"
@@ -199401,22 +199592,31 @@
199401
199592
  "kind": "javascript-module",
199402
199593
  "path": "src/strands/shader.ts",
199403
199594
  "declarations": [
199595
+ {
199596
+ "kind": "variable",
199597
+ "name": "MAX_QUIET",
199598
+ "type": {
199599
+ "text": "number"
199600
+ },
199601
+ "default": "4",
199602
+ "description": "The most elements a ribbon dims behind, through the root's \\`quiet\\`."
199603
+ },
199404
199604
  {
199405
199605
  "kind": "variable",
199406
199606
  "name": "LINE_VERTEX",
199407
- "default": "`#version 300 es layout(location = 0) in vec2 aVert; layout(location = 1) in float aId; uniform float uDpr; uniform float uSoftness; uniform float uContrast; uniform vec3 uGlow; uniform vec3 uTint; uniform vec3 uPeak; out float vSide; out vec3 vColor; ${PATH} void main() { float id = aId; float r5 = h1(id + 7.77); float r6 = h1(id + 9.19); float r7 = h1(id + 11.3); float t = mix(uT.x, uT.y, aVert.x); vec3 info; vec3 unused; vec3 a = strandAt(id, t, info); vec3 b = strandAt(id, t + (uT.y - uT.x) / 700.0, unused); vec2 tg = b.xy - a.xy; tg = length(tg) > 1e-4 ? normalize(tg) : vec2(0.0, 1.0); vec2 nrm = vec2(-tg.y, tg.x); float soft = smoothstep(0.06, 0.28, info.y) * step(1.0 - uSoftness, r6); /* Nearer is thicker, and the back of the ribbon falls out of focus: wider at the same energy, so it reads as haze behind the sharp strands. */ float base = mix(0.35, 1.3, r7 * r7) * uDpr * (1.0 + (0.3 + 0.5 * uDepth) * a.z); float focus = uDepth * smoothstep(-0.1, -0.9, a.z) * 10.0 * uDpr; float w = max(base + soft * mix(8.0, 36.0, r6) * uDpr + focus, 1.0); float drawW = w + 2.5 * uDpr; vec2 res = uView * uDpr; vec2 px = a.xy * uDpr + nrm * aVert.y * drawW * 0.5; gl_Position = vec4(px.x / res.x * 2.0 - 1.0, 1.0 - px.y / res.y * 2.0, 0.0, 1.0); vSide = aVert.y * drawW / w; float shimmer = 0.5 + 0.5 * sin(info.z * 0.044 - uTime * (2.0 + 2.0 * r5) + r6 * 6.283); shimmer *= shimmer; /* Contrast spreads the strands' brightness: a few burn and most fall back. The mean is held, so the ribbon as a whole keeps its strength. */ float spread = mix(1.4, 4.5, uContrast); float least = mix(0.25, 0.04, uContrast); float bright = mix(least, 1.0, pow(r5, spread)) * 0.5625 / (least + (1.0 - least) / (spread + 1.0)); float energy = min(base, w) / w; float fall = 0.3 + 0.5 * uDepth; float depth = (1.0 - fall * (1.0 - a.z) * 0.5) / (1.0 - fall * 0.5); float rest = mix(0.35, 0.15, uContrast); vec3 col = mix(uGlow, uTint, step(0.83, r6) * 0.9); col = mix(col, uPeak, step(0.93, r7) * 0.6); vColor = col * energy * info.x * (rest + (1.0 - rest) * shimmer) * bright * depth; } `",
199607
+ "default": "`#version 300 es layout(location = 0) in vec2 aVert; layout(location = 1) in float aId; uniform float uDpr; uniform float uSoftness; uniform float uContrast; uniform vec3 uGlow; uniform vec3 uTint; uniform vec3 uPeak; out float vSide; out float vSigma; out vec3 vColor; ${PATH} void main() { float id = aId; float r5 = h1(id + 7.77); float r6 = h1(id + 9.19); float r7 = h1(id + 11.3); float t = mix(uT.x, uT.y, aVert.x); vec3 info; vec3 unused; vec3 a = strandAt(id, t, info); vec3 b = strandAt(id, t + (uT.y - uT.x) / 700.0, unused); vec2 tg = b.xy - a.xy; tg = length(tg) > 1e-4 ? normalize(tg) : vec2(0.0, 1.0); vec2 nrm = vec2(-tg.y, tg.x); float soft = smoothstep(0.06, 0.28, info.y) * step(1.0 - uSoftness, r6); /* Nearer is thicker, and the back of the ribbon falls out of focus: wider at the same energy, so it reads as haze behind the sharp strands. */ float base = mix(0.35, 1.3, r7 * r7) * uDpr * (1.0 + (0.3 + 0.5 * uDepth) * a.z); float focus = uDepth * smoothstep(-0.1, -0.9, a.z) * 4.0 * uDpr; float w = max(base + soft * mix(8.0, 36.0, r6) * uDpr + focus, 1.0); /* **A profile no narrower than a pixel can carry.** The strand's light falls off across it as a Gaussian a quarter of its width wide, which for a strand a pixel wide is a quarter of a pixel: narrower than the pixels sampling it, so a thin strand broke into a row of dots wherever it ran between pixel centers. Its spread is held to at least 0.7 of a device pixel, and its peak comes down by the same factor, so it carries the same light and reads as one continuous thread. The quad covers three spreads either side. */ float designed = w * 0.228; float sigma = max(designed, 0.7); float drawW = max(w, sigma * 6.0) + 2.5 * uDpr; vec2 res = uView * uDpr; vec2 px = a.xy * uDpr + nrm * aVert.y * drawW * 0.5; gl_Position = vec4(px.x / res.x * 2.0 - 1.0, 1.0 - px.y / res.y * 2.0, 0.0, 1.0); vSide = aVert.y * drawW * 0.5; vSigma = sigma; /* **Light travels along the strand slowly and long.** A pulse every 140 pixels broke each strand into dashes; one every 1,800 or so is light moving down a fiber. */ float shimmer = 0.5 + 0.5 * sin(info.z * 0.0035 - uTime * (0.6 + 0.4 * r5) + r6 * 6.283); /* Contrast spreads the strands' brightness: a few burn and most fall back. The mean is held, so the ribbon as a whole keeps its strength. */ float spread = mix(1.4, 4.5, uContrast); float least = mix(0.35, 0.14, uContrast); float bright = mix(least, 1.0, pow(r5, spread)) * 0.5625 / (least + (1.0 - least) / (spread + 1.0)); float energy = min(base, w) / w; float fall = 0.3 + 0.5 * uDepth; float depth = (1.0 - fall * (1.0 - a.z) * 0.5) / (1.0 - fall * 0.5); float rest = mix(0.35, 0.15, uContrast); vec3 col = mix(uGlow, uTint, step(0.83, r6) * 0.9); col = mix(col, uPeak, step(0.93, r7) * 0.6); vColor = col * energy * info.x * (rest + (1.0 - rest) * shimmer) * bright * depth * (designed / sigma) * quietAt(a.xy); } `",
199408
199608
  "description": "Each strand as a ribbon of quads, wide enough to anti-alias and to blur."
199409
199609
  },
199410
199610
  {
199411
199611
  "kind": "variable",
199412
199612
  "name": "LINE_FRAGMENT",
199413
- "default": "`#version 300 es precision highp float; in float vSide; in vec3 vColor; uniform float uScale; out vec4 outColor; void main() { outColor = vec4(vColor * exp(-vSide * vSide * 2.4) * uScale, 1.0); } `",
199613
+ "default": "`#version 300 es precision highp float; in float vSide; in float vSigma; in vec3 vColor; uniform float uScale; out vec4 outColor; void main() { float d = vSide / vSigma; outColor = vec4(vColor * exp(-0.5 * d * d) * uScale, 1.0); } `",
199414
199614
  "description": "A soft profile across the ribbon, added into the buffer."
199415
199615
  },
199416
199616
  {
199417
199617
  "kind": "variable",
199418
199618
  "name": "DUST_VERTEX",
199419
- "default": "`#version 300 es uniform float uDpr; uniform float uStrands; uniform vec3 uGlow; uniform vec3 uPeak; out vec3 vColor; ${PATH} void main() { float id = float(gl_VertexID); float sid = floor(h1(id + 0.5) * uStrands); float r = h1(id + 2.3); float r2 = h1(id + 4.1); float t = mix(uT.x, uT.y, fract(r + uTime * 0.003 * (0.3 + r2))); vec3 info; vec3 a = strandAt(sid, t, info); vec2 off = (vec2(h1(id + 6.1), h1(id + 8.3)) - 0.5) * (24.0 + (150.0 + info.y * uView.x) * h1(id + 9.7)); vec2 res = uView * uDpr; vec2 px = (a.xy + off) * uDpr; gl_Position = vec4(px.x / res.x * 2.0 - 1.0, 1.0 - px.y / res.y * 2.0, 0.0, 1.0); gl_PointSize = mix(1.2, 3.4, r2 * r2) * uDpr; float tw = 0.35 + 0.65 * (0.5 + 0.5 * sin(uTime * (0.5 + 1.8 * r) + r2 * 40.0)); /* By the root of the ribbon's brightness, so specks show along a dim ribbon as well as round a bright bend. */ vColor = mix(uGlow, uPeak, 0.55) * tw * mix(0.2, 0.9, r) * sqrt(info.x) * 0.8; } `",
199619
+ "default": "`#version 300 es uniform float uDpr; uniform float uStrands; uniform vec3 uGlow; uniform vec3 uPeak; out vec3 vColor; ${PATH} void main() { float id = float(gl_VertexID); float sid = floor(h1(id + 0.5) * uStrands); float r = h1(id + 2.3); float r2 = h1(id + 4.1); float t = mix(uT.x, uT.y, fract(r + uTime * 0.003 * (0.3 + r2))); vec3 info; vec3 a = strandAt(sid, t, info); vec2 off = (vec2(h1(id + 6.1), h1(id + 8.3)) - 0.5) * (24.0 + (150.0 + info.y * uView.x) * h1(id + 9.7)); vec2 res = uView * uDpr; vec2 px = (a.xy + off) * uDpr; gl_Position = vec4(px.x / res.x * 2.0 - 1.0, 1.0 - px.y / res.y * 2.0, 0.0, 1.0); gl_PointSize = mix(1.2, 3.4, r2 * r2) * uDpr; float tw = 0.35 + 0.65 * (0.5 + 0.5 * sin(uTime * (0.5 + 1.8 * r) + r2 * 40.0)); /* By the root of the ribbon's brightness, so specks show along a dim ribbon as well as round a bright bend. */ vColor = mix(uGlow, uPeak, 0.55) * tw * mix(0.2, 0.9, r) * sqrt(info.x) * 0.8 * quietAt(a.xy + off); } `",
199420
199620
  "description": "Specks drifting along the strands, thickest where the ribbon is bright."
199421
199621
  },
199422
199622
  {
@@ -199450,6 +199650,14 @@
199450
199650
  }
199451
199651
  ],
199452
199652
  "exports": [
199653
+ {
199654
+ "kind": "js",
199655
+ "name": "MAX_QUIET",
199656
+ "declaration": {
199657
+ "name": "MAX_QUIET",
199658
+ "module": "src/strands/shader.ts"
199659
+ }
199660
+ },
199453
199661
  {
199454
199662
  "kind": "js",
199455
199663
  "name": "LINE_VERTEX",
@@ -199542,7 +199750,7 @@
199542
199750
  "declarations": [
199543
199751
  {
199544
199752
  "kind": "class",
199545
- "description": "Placed inside a `helix-strands` with `travel`, a stop names an element with\n`for` and says which form the ribbon takes when the page reaches it: a `bend`\nturning through it, a `ribbon` passing through `at`, or `waves` crossing at\nits top edge. The stops are read in page order, whatever order they are\nwritten in, and a stop naming no element is skipped.\n\nA stop draws nothing itself and is never shown.\n\n**Every property is reflected**, because the parent watches its stops'\nattributes to lay the ribbon out again: a change made from script reaches the\npage as a change made in markup does. None has a default, so a stop writes\nnothing it was not given.",
199753
+ "description": "Placed inside a `helix-strands` with `travel`, a stop names an element with\n`for` and says which form the ribbon takes when the page reaches it: a `bend`\nturning through it, a `ribbon` passing through `at`, a `sweep` crossing in one\ndiagonal from the page's top corner on the side `at` is nearer to the\nelement's far top corner, or `waves` crossing at its top edge. The stops are\nread in page order, whatever order they are written in, and a stop naming no\nelement is skipped.\n\nA stop draws nothing itself and is never shown.\n\n**Every property is reflected**, because the parent watches its stops'\nattributes to lay the ribbon out again: a change made from script reaches the\npage as a change made in markup does. None has a default, so a stop writes\nnothing it was not given.",
199546
199754
  "name": "HelixStrandsStop",
199547
199755
  "members": [
199548
199756
  {
@@ -199569,7 +199777,7 @@
199569
199777
  "kind": "field",
199570
199778
  "name": "variant",
199571
199779
  "type": {
199572
- "text": "'bend' | 'ribbon' | 'waves' | undefined"
199780
+ "text": "'bend' | 'ribbon' | 'sweep' | 'waves' | undefined"
199573
199781
  },
199574
199782
  "description": "The form the ribbon takes at the element. When it is not set, the form is `ribbon`.",
199575
199783
  "attribute": "variant",
@@ -199935,7 +200143,7 @@
199935
200143
  {
199936
200144
  "name": "variant",
199937
200145
  "type": {
199938
- "text": "'bend' | 'ribbon' | 'waves' | undefined"
200146
+ "text": "'bend' | 'ribbon' | 'sweep' | 'waves' | undefined"
199939
200147
  },
199940
200148
  "description": "The form the ribbon takes at the element. When it is not set, the form is `ribbon`.",
199941
200149
  "fieldName": "variant"
@@ -251371,6 +251579,6 @@
251371
251579
  ],
251372
251580
  "helix": {
251373
251581
  "package": "@fusion.dev/helix",
251374
- "version": "0.181.2"
251582
+ "version": "0.182.1"
251375
251583
  }
251376
251584
  }
@@ -1,6 +1,6 @@
1
1
  import { createSheet } from '@fusion.dev/helix-core';
2
- export const styles = createSheet(`:host {display: grid;position: relative;gap: var(--bar-chart-gap, var(--helix-space-2xs));}[part="plot"] {position: relative;block-size: var(--bar-chart-block-size, 12em);}[part="plot"] svg {block-size: 100%;inline-size: 100%;overflow: visible;}[part="baseline"] {stroke: var(--helix-color-border);stroke-width: var(--helix-border-thin);vector-effect: non-scaling-stroke;}[part~="bar"] {--_bar-chart-corners: var(--bar-chart-bar-radius, 2px) var(--bar-chart-bar-radius, 2px) 0 0;--_bar-chart-base: 0 0 calc(var(--bar-chart-bar-stroke, var(--helix-border-thick)) / 2) 0;clip-path: inset(var(--_bar-chart-foot, 0) round var(--_bar-chart-corners));fill: var(--_bar-chart-series);}:host([orientation="horizontal"]) [part~="bar"] {--_bar-chart-corners: 0 var(--bar-chart-bar-radius, 2px) var(--bar-chart-bar-radius, 2px) 0;--_bar-chart-base: 0 0 0 calc(var(--bar-chart-bar-stroke, var(--helix-border-thick)) / 2);}:host([stacked]) [part~="bar"]:not([part~="bar-end"]) {--_bar-chart-corners: 0;}:host([stacked]) [part~="bar-end"][data-end="negative"] {--_bar-chart-corners: 0 0 var(--bar-chart-bar-radius, 2px) var(--bar-chart-bar-radius, 2px);}:host([stacked][orientation="horizontal"]) [part~="bar-end"][data-end="negative"] {--_bar-chart-corners: var(--bar-chart-bar-radius, 2px) 0 0 var(--bar-chart-bar-radius, 2px);}[part="labels"] {display: grid;grid-auto-columns: 1fr;grid-auto-flow: column;}[part="label"] {color: var(--helix-color-foreground-muted);font-size: var(--helix-text-xs);overflow: hidden;text-align: center;text-overflow: ellipsis;white-space: nowrap;}:host([orientation="horizontal"]) {grid-template-columns: auto 1fr;}:host([orientation="horizontal"]) [part="labels"] {grid-column: 1;grid-row: 1;}:host([orientation="horizontal"]) [part="plot"] {grid-column: 2;grid-row: 1;}:host([orientation="horizontal"]) [part="labels"] {grid-auto-columns: auto;grid-auto-flow: row;grid-auto-rows: 1fr;}:host([orientation="horizontal"]) [part="label"] {align-self: center;text-align: end;}[part~="bar"],
3
- [part="line"] {--_bar-chart-series: var(--chart-series);}[part="plot"]:has([data-active]) [part~="group"]:not([data-active]) {opacity: 0.45;}[part~="group"] {transition: opacity var(--helix-duration-fast) var(--helix-easing-decelerate);}[part~="bar"] {transition:
2
+ export const styles = createSheet(`:host {display: grid;position: relative;gap: var(--bar-chart-gap, var(--helix-space-2xs));}[part="plot"] {position: relative;block-size: var(--bar-chart-block-size, 12em);}[part="plot"] svg {block-size: 100%;inline-size: 100%;overflow: visible;}[part="baseline"] {stroke: var(--helix-color-border);stroke-width: var(--helix-border-thin);vector-effect: non-scaling-stroke;}[part~="bar"] {--_bar-chart-corners: var(--bar-chart-bar-radius, 2px) var(--bar-chart-bar-radius, 2px) 0 0;--_bar-chart-base: 0 0 calc(var(--bar-chart-bar-stroke, var(--helix-border-thick)) / 2) 0;clip-path: inset(var(--_bar-chart-foot, 0) round var(--_bar-chart-corners));fill: var(--_bar-chart-series);}:host([orientation="horizontal"]) [part~="bar"] {--_bar-chart-corners: 0 var(--bar-chart-bar-radius, 2px) var(--bar-chart-bar-radius, 2px) 0;--_bar-chart-base: 0 0 0 calc(var(--bar-chart-bar-stroke, var(--helix-border-thick)) / 2);}:host([stacked]) [part~="bar"]:not([part~="bar-end"]) {--_bar-chart-corners: 0;}[part~="bar-end-negative"] {--_bar-chart-corners: 0 0 var(--bar-chart-bar-radius, 2px) var(--bar-chart-bar-radius, 2px);}:host([orientation="horizontal"]) [part~="bar-end-negative"] {--_bar-chart-corners: var(--bar-chart-bar-radius, 2px) 0 0 var(--bar-chart-bar-radius, 2px);}[part="labels"] {display: grid;grid-auto-columns: 1fr;grid-auto-flow: column;}[part="label"] {color: var(--helix-color-foreground-muted);font-size: var(--helix-text-xs);overflow: hidden;text-align: center;text-overflow: ellipsis;white-space: nowrap;}:host([orientation="horizontal"]) {grid-template-columns: auto 1fr;}:host([orientation="horizontal"]) [part="labels"] {grid-column: 1;grid-row: 1;}:host([orientation="horizontal"]) [part="plot"] {grid-column: 2;grid-row: 1;}:host([orientation="horizontal"]) [part="labels"] {grid-auto-columns: auto;grid-auto-flow: row;grid-auto-rows: 1fr;align-self: start;block-size: var(--bar-chart-block-size, 12em);}:host([orientation="horizontal"]) [part="label"] {align-self: center;text-align: end;}[part~="bar"],
3
+ [part="line"] {--_bar-chart-series: var(--chart-series);}:host {--bar-chart-tone-accent: var(--helix-color-accent);--bar-chart-tone-danger: var(--helix-color-danger);--bar-chart-tone-info: var(--helix-color-info);--bar-chart-tone-muted: var(--helix-color-foreground-subtle);--bar-chart-tone-primary: var(--helix-color-primary);--bar-chart-tone-success: var(--helix-color-success);--bar-chart-tone-warning: var(--helix-color-warning);}[part="plot"]:has([data-active]) [part~="group"]:not([data-active]) {opacity: 0.45;}[part~="group"] {transition: opacity var(--helix-duration-fast) var(--helix-easing-decelerate);}[part~="bar"] {transition:
4
4
  height var(--helix-duration-normal) var(--helix-easing-decelerate),
5
5
  width var(--helix-duration-normal) var(--helix-easing-decelerate),
6
6
  x var(--helix-duration-normal) var(--helix-easing-decelerate),
@@ -7,7 +7,7 @@
7
7
  * screen are all arithmetic.
8
8
  */
9
9
  /** The three forms the ribbon takes. */
10
- export type StrandsVariant = 'bend' | 'ribbon' | 'waves';
10
+ export type StrandsVariant = 'bend' | 'ribbon' | 'sweep' | 'waves';
11
11
  /**
12
12
  * One point on the centerline: `x` as a share of the width, `y` in CSS pixels,
13
13
  * the ribbon's width as a share of the width, and its brightness from `0` to
@@ -27,6 +27,10 @@ export interface PlacedStop {
27
27
  height: number;
28
28
  /** The target's top, in page pixels. */
29
29
  top: number;
30
+ /** The target's left edge, as a share of the width, where it is known. */
31
+ left?: number | undefined;
32
+ /** The target's right edge, as a share of the width, where it is known. */
33
+ right?: number | undefined;
30
34
  variant: StrandsVariant;
31
35
  }
32
36
  /**
@@ -47,6 +51,33 @@ export declare const MAX_POINTS = 96;
47
51
  * is meant to be one sweep. `path.pure.spec.ts` measures it on every form.
48
52
  */
49
53
  export declare const TURN = 0.5;
54
+ /**
55
+ * The ribbon's width over each form's own at a `spread` of `1`, which the
56
+ * renderer multiplies in. Here so the path can know how far its strands reach.
57
+ */
58
+ export declare const SPREAD = 1.35;
59
+ /**
60
+ * The shape of the ribbon down a page, each as a multiplier or a share of the
61
+ * width, so a page can try another without the path's arithmetic changing.
62
+ */
63
+ export interface PathShape {
64
+ /** How far the ribbon swings side to side, over the most its turns allow. */
65
+ swing: number;
66
+ /** How long one S takes, over `PERIOD` screens. */
67
+ period: number;
68
+ /** How far the ribbon's axis sits from the middle, as a share of the width; positive is right. */
69
+ lean: number;
70
+ /** How far from the middle a wave's sweep lands, as a share of the width. */
71
+ sweep: number;
72
+ }
73
+ /**
74
+ * The shape a page draws unless it asks for another: calm, the ribbon
75
+ * swinging six tenths of the most its turns allow over S curves 2.4 screens
76
+ * long, resting a tenth of the width right of the middle. Compared down the
77
+ * whole landing page against wider, quicker, and leaning shapes, this one
78
+ * read as one long current rather than a ribbon zigzagging between cards.
79
+ */
80
+ export declare const SHAPE: PathShape;
50
81
  /**
51
82
  * The ribbon drawn inside its own box, which is what an element without
52
83
  * `travel` shows.
@@ -77,10 +108,28 @@ export declare const boxPath: (variant: StrandsVariant, at: number | undefined,
77
108
  * @param width The viewport's width, in CSS pixels.
78
109
  * @param height The viewport's height, in CSS pixels.
79
110
  * @param page The page's height, in CSS pixels.
111
+ * @param shape The ribbon's shape down the page.
80
112
  */
81
- export declare const pagePath: (stops: readonly PlacedStop[], fill: StrandsVariant, width: number, height: number, page: number) => PathPoint[];
113
+ export declare const pagePath: (stops: readonly PlacedStop[], fill: StrandsVariant, width: number, height: number, page: number, shape?: PathShape) => PathPoint[];
82
114
  /** One coordinate of a uniform Catmull-Rom segment, the same one the shader runs. */
83
115
  export declare const catmull: (p0: number, p1: number, p2: number, p3: number, f: number) => number;
116
+ /**
117
+ * How far along the drawn centerline each point is, in CSS pixels, for the
118
+ * shader to set every frequency by.
119
+ *
120
+ * **Arc length, which only grows.** The shader used the sum of a point's `y`
121
+ * and its `x` in pixels, which grows down a page and across a wave heading
122
+ * right, and runs backward wherever the path heads left faster than it heads
123
+ * down: on a bend's upper arm, and across a wave sweeping left. There the
124
+ * strands' twist and waves ran in reverse, and every strand turned a corner
125
+ * at the same place.
126
+ *
127
+ * Sixteen samples a segment of the same spline the shader draws.
128
+ *
129
+ * @param points The path.
130
+ * @param width The box's width, in CSS pixels, which `x` is a share of.
131
+ */
132
+ export declare const arcLengths: (points: readonly PathPoint[], width: number) => Float32Array;
84
133
  /**
85
134
  * The centerline's `y` at even steps of its parameter, for finding which
86
135
  * stretch is on screen.