THREE.OrbitControls is not a constructor, THREE is not defined

I am developing a Dash app with a ThreeJS component embedded. This is not the final project, but an example. I dont know to fix this, since I have tried a lot of things and ChatGPT, Gemini and Claude dont have an enswer either.

import dash
from dash import html, dcc, Input, Output, clientside_callback
import dash_bootstrap_components as dbc
import json
import numpy as np

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.index_string = '''
<!DOCTYPE html>
<html>
    <head>
        {%metas%}
        <title>{%title%}</title>
        {%favicon%}
        {%css%}
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js"></script>
    </head>
    <body>
        {%app_entry%}
        <footer>
            {%config%}
            {%scripts%}
            {%renderer%}
        </footer>
    </body>
</html>
'''

app.layout = dbc.Container([
    html.H1("Interactive 3D Scatter Plot with Three.js"),
    dbc.Button("Plot 3D Scene", id="plot-button", color="primary", className="mb-3"),
    html.Div(id="threejs-container", style={"width": "100%", "height": "600px"}),
    html.Div(id="error-log", style={"color": "red"}),
    dcc.Store(id="point-data"),
    html.Div(id="scene_initiation"),
])

@app.callback(
    Output("point-data", "data"),
    Input("plot-button", "n_clicks")
)
def generate_points(n_clicks):
    if n_clicks is None:
        return dash.no_update
    
    num_points = 30000
    points = np.random.randn(num_points, 3) * 50
    return json.dumps(points.tolist())

app.clientside_callback(
    """
    function(pointData) {
        const container = document.getElementById('threejs-container');
        const errorLog = document.getElementById('error-log');

        let scene, camera, renderer, pointCloud, controls;

        function initScene() {
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
            renderer = new THREE.WebGLRenderer();

            renderer.setSize(container.clientWidth, container.clientHeight);
            container.innerHTML = '';
            container.appendChild(renderer.domElement);

            camera.position.z = 100;

            // Add OrbitControls
            controls = new OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;
            controls.dampingFactor = 0.25;
            controls.screenSpacePanning = false;
            controls.maxDistance = 500;
            controls.minDistance = 10;

            animate();
        }

        function plotPoints(points) {
            const geometry = new THREE.BufferGeometry();
            const vertices = new Float32Array(points.flat());
            geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));

            const material = new THREE.PointsMaterial({color: 0x00ff00, size: 0.5});
            pointCloud = new THREE.Points(geometry, material);
            scene.add(pointCloud);
        }

        function animate() {
            requestAnimationFrame(animate);
            if (renderer && scene && camera) {
                controls.update(); // Update controls in the animation loop
                renderer.render(scene, camera);
            }
        }

        function onWindowResize() {
            camera.aspect = container.clientWidth / container.clientHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(container.clientWidth, container.clientHeight);
        }

        initScene();

        if (pointData) {
            const points = JSON.parse(pointData);
            plotPoints(points);
        }

        window.addEventListener('resize', onWindowResize, false);

        return '';
    }
    """,
    Output("threejs-container", "children"),
    Input("point-data", "data"),
    Input("scene_initiation", "children")
)

if __name__ == '__main__':
    app.run_server(debug=True)

When I dont use OrbitControls and use

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>

instead of

<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js"></script>

It works, but the 3d plot is not interactive. What could i do?