Example

import { AreaPlot, Chart, LinePlot, Navigation, NavigationStack, Script, ScrollView, Text, useState, VStack } from "scripting"

function Example() {
  // Drive amplitude with state to show that SwiftUI Charts re-samples the fn on every render.
  const [amplitude, setAmplitude] = useState(1)

  return <NavigationStack>
    <ScrollView>
      <VStack
        navigationTitle={"LinePlot & AreaPlot"}
        navigationBarTitleDisplayMode={"inline"}
        spacing={28}
        padding
      >

        <Text font={"caption"} foregroundStyle={"secondaryLabel"}>
          LinePlot / AreaPlot are Chart marks introduced in iOS 18+. You pass a JS function
          (not an array of samples); SwiftUI Charts samples it across the viewport at layout
          time and connects the points into a curve.
          {"\n\n"}
          The fn closure must stay pure (no setState, no external side effects). SwiftUI
          Charts re-samples it hundreds of times per layout pass, and each sample is one
          JSCore call — keep the body cheap.
        </Text>

        {/* 1. Single variable y = fn(x) */}
        <VStack alignment={"leading"} spacing={8}>
          <Text font={"headline"}>1. y = sin(x) — single variable</Text>
          <Text font={"caption"} foregroundStyle={"secondaryLabel"}>
            domain [0, 4π]. Toggle the link below to change the amplitude and watch the
            whole curve re-render (every render re-samples the function).
          </Text>
          <Chart frame={{ height: 220 }}>
            <LinePlot
              x={"X"}
              y={"Y"}
              domain={[0, Math.PI * 4]}
              fn={(x) => amplitude * Math.sin(x)}
              foregroundStyle={"blue"}
            />
          </Chart>
          <Text font={"caption2"}>amplitude: {amplitude.toFixed(2)}</Text>
          {/* Minimal amplitude toggle */}
          <Text
            foregroundStyle={"link"}
            onTapGesture={() => setAmplitude(amplitude === 1 ? 1.6 : 1)}
          >
            tap to toggle amplitude (1 ↔ 1.6)
          </Text>
        </VStack>

        {/* 2. Parametric curve (x, y) = fn(t) */}
        <VStack alignment={"leading"} spacing={8}>
          <Text font={"headline"}>2. Parametric — circle (cos t, sin t)</Text>
          <Text font={"caption"} foregroundStyle={"secondaryLabel"}>
            t ∈ [0, 2π]. fn returns a {`{ x, y }`} tuple, which the bridge converts to
            SwiftUI's (x: Double, y: Double) tuple. Traces a full circle.
          </Text>
          <Chart
            frame={{ height: 240, width: 240 }}
            chartXScale={{ from: -1.2, to: 1.2 }}
            chartYScale={{ from: -1.2, to: 1.2 }}
          >
            <LinePlot
              x={"X"}
              y={"Y"}
              t={"t"}
              domain={[0, Math.PI * 2]}
              fn={(t) => ({ x: Math.cos(t), y: Math.sin(t) })}
              foregroundStyle={"orange"}
            />
          </Chart>
        </VStack>

        {/* 3. AreaPlot — vertical band */}
        <VStack alignment={"leading"} spacing={8}>
          <Text font={"headline"}>3. AreaPlot — sin envelope ±0.5</Text>
          <Text font={"caption"} foregroundStyle={"secondaryLabel"}>
            fn returns {`{ yStart, yEnd }`}, and AreaPlot fills the vertical interval from
            yStart to yEnd at every x — typical use cases are confidence bands and envelopes.
          </Text>
          <Chart frame={{ height: 220 }}>
            <AreaPlot
              x={"X"}
              yStart={"lo"}
              yEnd={"hi"}
              domain={[0, Math.PI * 4]}
              fn={(x) => ({
                yStart: Math.sin(x) - 0.5,
                yEnd: Math.sin(x) + 0.5,
              })}
              foregroundStyle={{ color: "green", opacity: 0.35 }}
            />
            <LinePlot
              x={"X"}
              y={"Y"}
              domain={[0, Math.PI * 4]}
              fn={(x) => Math.sin(x)}
              foregroundStyle={"green"}
            />
          </Chart>
        </VStack>

      </VStack>
    </ScrollView>
  </NavigationStack>
}

async function run() {
  await Navigation.present({ element: <Example /> })
  Script.exit()
}

run()